Skip to contents

Introduction

cHeatmap is a wrapper of the excellent ComplexHeatmap::Heatmap() function with additional functions and more friendly interface for some common tasks in my work, thus called the convenience Heatmap. I highly recommend reading the ComplexHeatmap book for more advanced use.

Here are the features:

  1. Automatic or manual exclusion of outliers in the color mapping so that the color scale of the heatmap and annotations is not dominated by outliers.

  2. The option to set the color-value mappings for the body and annotations of heatmaps.

  3. Automatic coloring of the dendrogram.

  4. Easy highlight or display of the values of certain cells in the body and annotations of the heatmap.

  5. Discrete color-value mapping for integer matrices containing few unique values.

  6. Interface to plot across rows.

  7. Handling of edge cases:

    • Inf and -Inf values in input matrix cause errors in stats::dist() for clustering, they are reset as NA.

    • If missing values are present in the input matrix or row and column annotations, a legend for missing value is added.

    • Plot a heatmap of a data.frame with columns of different data types clustered by one of the columns.

Examples

Exclusion of outliers in the color mapping

Outliers in a numeric matrix dominate the color scale and make the differences among many cells barely visible, such as the heatmap body and ra1 annotation below.

library(cHeatmap)

set.seed(100)
nMat1 <- rnorm(27)
mat1 <- matrix(c(-5,-Inf,nMat1,Inf), nrow = 6)
rownames(mat1)=paste0('r',1:nrow(mat1))
colnames(mat1)=paste0('c',1:ncol(mat1))
raDf=data.frame(ra1=c(20,Inf,1:4),ra2=c(NA,letters[1:5]))
rownames(raDf)=rownames(mat1)
caDf=data.frame(ca1=c(9.8,11.3,6.9,5.4,-80))
rownames(caDf)=colnames(mat1)

cHeatmap(
  mat1,
  NA.color = 'grey50',
  name = "value", # title of the legend for heatmap body
  rmLegendOutliers = F, # keep outliers in the legends
  cluster_rows = F, cluster_columns = F, # no clustering
  cellFun = function(x) { x }, # display all the values in the body of the heatmap
  rowAnnoDf = raDf, # add row annotation
  colmAnnoDf = caDf, # add row annotation
  annoCellFunList = list(ra1=\(x) {1:nrow(raDf)}) # display all values of ra1 annotation
)

Set rmLegendOutliers = T, default for numerical vectors, to remove outliers in the color-value mapping of the legends, so that the difference in the body and annotation can be easily visualized. See Reference Manual for details. Note that the affected labels of the legends are now prefixed with > or <, unless mat1 is a discrete integer vector like ra1, i.e. the number of unique items is less than intAsDiscreteCutoff.

cHeatmap(
  mat1,
  name = "value", 
  rmLegendOutliers = T, # remove outliers in the legends
  cluster_rows = F, cluster_columns = F, 
  cellFun = function(x) { x }, 
  rowAnnoDf = raDf, 
  colmAnnoDf = caDf,
  annoCellFunList = list(ra1=\(x) {1:nrow(raDf)},
                         ca1=\(x) {1:nrow(caDf)}) 
)

Manually set the color-value mapping

Instead of auto-detecting the outliers, use the colMap to set the range. Values outside of the range are treated as outliers.

cHeatmap(
  mat1,
  name = "value", 
  # manually set the color mapping for the heatmap body, here only the numeric 
    # values are set since the default colors are green, white, and red.
    # one can also set only the upper or low bound, e.g. colMap = c(NA, 0, 1)
  colMap = c(-1, 0, 1), 
  rmLegendOutliers = T, # remove outliers in the legends
  cluster_rows = F, cluster_columns = F, 
  colmAnnoDf = caDf,
  # manually set the color mapping for the ra1
  colmAnnoColMap = list(ca1=c('white'=0,'gold'=12))
)

Set multi-color mapping to visualize local clusters or outliers

# create an Inf value
nMat1 <- rnorm(30)
uMat1 <- runif(12, -5, 20)
mat2 <- matrix(c(nMat1, uMat1), nrow = 6)
rownames(mat2)=paste0('r',1:nrow(mat1))
caDf2=data.frame(ca2=c(9.8,11.3,6.9,5.4,-80, 7, 60))
rownames(caDf2)=colnames(mat2)

bCm=c("green4" = -1, "white" = 0, "red" = 1, #cluster 1
             "yellow" = 3, "blue" = 18) #cluster 2
caCm = c('purple'=-80,'white'=0,'yellow4'=12, 'red'=60)

cHeatmap(mat2,
  name = "value",
  rmLegendOutliers = F,
  colMap = bCm,
  column_split = c(rep("nMat1", 5), rep("uMat1", 2)),
  rowAnnoDf = raDf, 
  # manually set the color mapping for the ra1
  rowAnnoColMap = list(ra1=c('white'=1,'gold'=4,'pink4'=20)),
  colmAnnoDf = caDf2,
  colmAnnoColMap = list(ca2=caCm)
  
)

Set distinct color to different numerical ranges by adding a | between the names of two colors, e.g. the bCm and caCm below. And adjust the legend to

  • lengthen the portion for cluster 1
  • shorten the portion between 1 and 3 if majority of cluster 2 are in (3, 18)
  • increase the height of the whole legend to 4cm
bAt=c(-1,0,1,3,6,9,12,15,18)
bCm=c(green = -1,
           'green4|yellow' = 0,
           'yellow4|red'=1,
           'red4|deepskyblue' = 3,
           blue4 = 18)
caCm= c('purple'=-80,'purple2|white'=3,'yellow4|red'=12, 'red2'=60)
caAt=c(-80,3,6,9,12,60)
cHeatmap(mat2,
         name = "value",
         rmLegendOutliers = F,
         #set color mapping with distinct regions
         colMap = bCm, 
         column_split = c(rep("nMat1", 5), rep("uMat1", 2)),
         legendTicks = bAt,
         legendBreakDist = rep.int(1,length(bAt)-1),
         legendHeight = 4,
         rowAnnoDf = raDf,
         rowAnnoColMap = list(ra1=c('white'=1,'gold'=4,'pink4'=20)),
         colmAnnoDf = caDf2,
         colmAnnoColMap = list(ca2=caCm),
         colmAnnoPara = list(annotation_legend_param=list(
           ca2=list(at=caAt,break_dist=rep.int(1,length(caAt)-1))
           ))
)

Display cell values using cellFun and annoCellFun

Show only values in (0.5, 1) in the heatmap body and the 5th value in ra1 and value b in ra2.

cHeatmap(
  mat1,
  name = "value", 
  cellFun = \(x) { if (is.finite(x) && x > 0.5 && x < 1) x  }, 
  rowAnnoDf = raDf, 
  annoCellFunList = list(ra1=\(x) {5},
                         ra2=\(x) {which(x=='b')}) 
)

Display only the outliers in the heatmap body and ra1.

cHeatmap(
  mat1,
  name = "value", 
  cellFun = 'o', 
  rowAnnoDf = raDf, 
  annoCellFunList = list(ra1=\(x) 'o') 
)

Mark outliers as X;

cHeatmap(
  mat1,
  name = "value", 
  cellFun = c("o", "X"), 
  rowAnnoDf = raDf, 
  annoCellFunList = list(ra1=\(x) list('o','X')) 
)

Color outliers by black edge.

cHeatmap(mat1, 
  cellFun = c(
    "o", list("rect", col = "black", lwd = 2)
  )
)

Display H if cell values > 2 and L if < 0.

cHeatmap(mat1,
  cellFun = function(x) {
    if(is.finite(x)){
      if(x > 2) "H" else if(x < 0) "L"
    }
  }
)

Add black edge if cell values > 2 and display L if < 0.

cHeatmap(mat1,
  cellFun = function(x) {
    if(is.finite(x)){
      if(x > 2) list("rect", col = "black", lwd = 2) else if(x < 0) "L"
    }
  }
)

Color dendrograms and split the clusters

nRowCluster and nColmCluster specify the number of colors in the corresponding dendrograms.

cHeatmap(mat1,
  name = "value",
  rmLegendOutliers = F,
  nRowCluster = 2, nColmCluster = 3,
  row_split = 2, column_split = 3
)

Integer matrices

If the number of unique values is greater than intAsDiscreteCutoff whose default value is 6 in cHeatmap parameter settings, the color mapping is continuous; otherwise, the mapping is discrete.

mati <- matrix(sample(1:100, 42, TRUE), nrow = 6)
cHeatmap(mati, name = "value")


mati <- matrix(sample(1:5, 42, TRUE), nrow = 6)
cHeatmap(mati, name = "value")

Set colors manually.

cHeatmap(mati,
  name = "value",
  colMap = c("red" = 1, "pink" = 2, "yellow" = 3, "green" = 4, "green4" = 5)
)

Concatenate two heatmaps

Set drawHeatmap = F to concatenate multiple heatmaps.

nMat1 <- matrix(rnorm(30), nrow = 6)
uMat1 <- matrix(runif(12, -5, 20), nrow = 6)

hm1 <- cHeatmap(nMat1, drawHeatmap = F, name = "nMat1")
hm2 <- cHeatmap(uMat1, drawHeatmap = F, name = "uMat1")
hm1 + hm2

Plot inside each row using rowDraw

Here painScore of patient 2 is plotted across visits. Its values are transformed to the range of (0,1), representing relative values across visits.

mat1 <- matrix(c(nMat1, uMat1), nrow = 6)
rownames(mat1) <- paste("pt", 1:6)
colnames(mat1) <- paste("visit", 1:7)

painScore <- rnorm(7)

cHeatmap(mat1,
  name = "value", cluster_columns = F,
  rowDraw = list(
    list("grid.lines", col = "black", lwd = 2),
    matrix(painScore, nrow = 1), # data to be plotted
    2 # plot at the 2nd row of mat1
  )
)

Plot both lines and points in multiple rows.

# longitudinal scores of four patients
painScore <- matrix(rnorm(28), nrow = 4)
cHeatmap(mat1,
  name = "value", cluster_columns = F,
  rowDraw = list(
    list(
      list("grid.points", size = 0.5, pch = 15, col = "blue"),
      list("grid.lines", col = "black", lwd = 2)
    ),
    painScore, 
    c(2, 3, 4, 5) # row indices of the four patients in mat1
  )
)

Plot a heatmap of each column of a data.frame of different data types

df2=cbind(m1=mat1[,1],raDf)
cHeatmap(
  df2[1],
  name = "m1", 
  rowAnnoDf = df2[2:3]
)