7 min read

Translate from pheatmap to ComplexHeatmap

Introduction

pheatmap is a great R package for making heatmaps, inspiring a lot of other heatmap packages such as ComplexHeatmap. From version 2.5.2 of ComplexHeatmap, I implemented a new ComplexHeatmap::pheatmap() function which actually maps all the parameters in pheatmap::pheatmap() to proper parameters in ComplexHeatmap::Heatmap(), which means, it converts a pheatmap to a complex heatmap. By doing this, the most significant improvement is now you can add multiple pheatmaps and annotations (defined by ComplexHeatmap::rowAnnotation()).

ComplexHeatmap::pheatmap() includes all arguments in pheatmap::pheatmap(), which means, you don’t need to do any adaptation on your pheatmap code, you just rerun your pheatmap code and it will automatically and nicely convert to the complex heatmap.

Some arguments in pheatmap::pheatmap() are disabled and ignored in this translation, listed as follows:

  • kmeans_k
  • filename
  • width
  • height
  • silent

The usage of remaining arguments is exactly the same as in pheatmap::pheatmap().

In pheatmap::pheatmap(), the color argument is specified with a long color vector, e.g. :

pheatmap::pheatmap(mat, 
    color = colorRampPalette(rev(brewer.pal(n = 7, name = "RdYlBu")))(100)
)

You can use the same setting of color in ComplexHeatmap::pheatmap(), but you can also simplify it as:

ComplexHeatmap::pheatmap(mat, 
    color = rev(brewer.pal(n = 7, name = "RdYlBu"))
)

The colors for individual values are automatically interpolated.

Examples

First we load an example dataset which is from the “Examples” section of the documentation of pheatmap::pheatmap() function .

library(ComplexHeatmap)
test = matrix(rnorm(200), 20, 10)
test[1:10, seq(1, 10, 2)] = test[1:10, seq(1, 10, 2)] + 3
test[11:20, seq(2, 10, 2)] = test[11:20, seq(2, 10, 2)] + 2
test[15:20, seq(2, 10, 2)] = test[15:20, seq(2, 10, 2)] + 4
colnames(test) = paste("Test", 1:10, sep = "")
rownames(test) = paste("Gene", 1:20, sep = "")

Calling pheatmap() (which is now actually ComplexHeatmap::pheatmap()) generates a similar heatmap as by pheatmap::pheatmap().

pheatmap(test)  # this is ComplexHeatmap::pheatmap

Everything looks the same except the style of the heatmap legend. There are also some other visual difference which you can find in the “Comparisons” section in this post.

The next one is an example for setting annotations (you should be familiar with how to set these data frames and color list if you are a pheatmap user).

annotation_col = data.frame(
    CellType = factor(rep(c("CT1", "CT2"), 5)), 
    Time = 1:5
)
rownames(annotation_col) = paste("Test", 1:10, sep = "")

annotation_row = data.frame(
    GeneClass = factor(rep(c("Path1", "Path2", "Path3"), c(10, 4, 6)))
)
rownames(annotation_row) = paste("Gene", 1:20, sep = "")

ann_colors = list(
    Time = c("white", "firebrick"),
    CellType = c(CT1 = "#1B9E77", CT2 = "#D95F02"),
    GeneClass = c(Path1 = "#7570B3", Path2 = "#E7298A", Path3 = "#66A61E")
)
pheatmap(test, annotation_col = annotation_col, annotation_row = annotation_row, 
    annotation_colors = ann_colors)

You can split the heatmap by rows or by columns, which is a unique feature of ComplexHeatmap.

pheatmap(test, annotation_col = annotation_col, annotation_row = annotation_row, 
    annotation_colors = ann_colors, 
    row_split = annotation_row$GeneClass,
    column_split = annotation_col$CellType)

ComplexHeatmap::pheatmap() returns a Heatmap object, so it can be added with other heatmaps and annotations. Or in other words, you can add multiple pheatmaps and annotations. Cool!

p1 = pheatmap(test, name = "mat1")
p2 = rowAnnotation(foo = anno_barplot(1:nrow(test)))
p3 = pheatmap(test, name = "mat2", 
    col = colorRampPalette(c("navy", "white", "firebrick3"))(50))
# or you can simply specify as
# p3 = pheatmap(test, name = "mat2", col = c("navy", "white", "firebrick3"))
p1 + p2 + p3

Nevertheless, if you really want to add multiple pheatmaps, I still suggest you to directly use the Heatmap() function. You can find how to migrate from pheatmap::pheatmap() to ComplexHeatmap::Heatmap() in the next section.

One last thing is since ComplexHeatmap::pheatmap() returns a Heatmap object, if pheatmap() is not called in an interactive environment, e.g. in an R script, inside a function or in a for loop, you need to explicitly use draw() function:

for(...) {
    p = pheatmap(...)
    draw(p)
}

Translation

Following table lists how to map parameters in pheatmap::pheatmap() to ComplexHeatmap::Heatmap().

Arguments in pheatmap::pheatmap() Identical settings/arguments in ComplexHeatmap::Heatmap()
mat matrix
color Users can specify a color mapping function by circlize::colorRamp2(), or provide a vector of colors on which colors for individual values are linearly interpolated.
kmeans_k No corresponding parameter because it changes the matrix for heatmap.
breaks It should be specified in the color mapping function.
border_color rect_gp = gpar(col = border_color). In the annotations, it is HeatmapAnnotation(..., gp = gpar(col = border_color)).
cellwidth width = ncol(mat)*unit(cellwidth, "pt")
cellheight height = nrow(mat)*unit(cellheight, "pt")
scale Users should simply apply scale() on the matrix before sending to Heatmap().
cluster_rows cluster_rows
cluster_cols cluster_columns
clustering_distance_rows clustering_distance_rows. The value correlation should be changed to pearson.
clustering_distance_cols clustering_distance_columns, The value correlation should be changed to pearson.
clustering_method clustering_method_rows/clustering_method_columns
clustering_callback The processing on the dendrogram should be applied before sending to Heatmap().
cutree_rows row_split and row clustering should be applied.
cutree_cols column_split and column clustering should be applied.
treeheight_row row_dend_width = unit(treeheight_row, "pt")
treeheight_col column_dend_height = unit(treeheight_col, "pt")
legend show_heatmap_legend
legend_breaks heatmap_legend_param = list(at = legend_breaks)
legend_labels heatmap_legend_param = list(labels = legend_labels)
annotation_row left_annotatioin = rowAnnotation(df = annotation_row)
annotation_col top_annotation = HeatmapAnnotation(df = annotation_col)
annotation Not supported.
annotation_colors col argument in HeatmapAnnotation()/rowAnnotation().
annotation_legend show_legend argument in HeatmapAnnotation()/rowAnnotation().
annotation_names_row show_annotation_name in rowAnnotation().
annotation_names_col show_annotation_name in HeatmaoAnnotation().
drop_levels Unused levels are all dropped.
show_rownames show_row_names
show_colnames show_column_names
main column_title
fontsize gpar(fontsize = fontsize) in corresponding heatmap components.
fontsize_row row_names_gp = gpar(fontsize = fontsize_row)
fontsize_col column_names_gp = gpar(fontsize = fontsize_col)
angle_col column_names_rot. The rotation of row annotation names are not supported.
display_numbers Users should set a proper cell_fun or layer_fun (vectorized and faster version of cell_fun). E.g. if display_numbers is TRUE, layer_fun can be set as function(j, i, x, y, w, h, fill) { grid.text(sprintf(number_format, pindex(mat, i, j)), x = x, y = y, gp = gpar(col = number_color, fontsize = fontsize_number)) }. If display_numbers is a matrix, replace mat to display_numbers in the layer_fun.
number_format See above.
number_color See above.
fontsize_number See above.
gaps_row Users should construct a “splitting variable” and send to row_split. E.g. slices = diff(c(0, gaps_row, nrow(mat))); rep(seq_along(slices), times = slices).
gaps_col Users should construct a “splitting variable” and send to column_split.
labels_row row_labels
labels_col column_labels
filename No corresponding setting in Heatmap(). Users need to explicitly use e.g. pdf().
width No corresponding setting in Heatmap().
height No corresponding setting in Heatmap().
silent No corresponding setting in Heatmap().
na_col na_col

Comparisons

I ran all the example code in the “Examples” section of the documentation of pheatmap::pheatmap() function . I also implemented a wrapper function ComplexHeatmap::compare_pheatmap() which basically uses the same set of arguments for pheatmap::pheatmap() and ComplexHeatmap::pheatmap() and draws two heatmaps, so that you can directly see the similarity and difference of the two heatmap implementations.

compare_pheatmap(test)

compare_pheatmap(test, scale = "row", clustering_distance_rows = "correlation")

compare_pheatmap(test, 
    color = colorRampPalette(c("navy", "white", "firebrick3"))(50))

compare_pheatmap(test, cluster_row = FALSE)

compare_pheatmap(test, legend = FALSE)

compare_pheatmap(test, display_numbers = TRUE)

compare_pheatmap(test, display_numbers = TRUE, number_format = "%.1e")

compare_pheatmap(test, 
    display_numbers = matrix(ifelse(test > 5, "*", ""), nrow(test)))

compare_pheatmap(test, cluster_row = FALSE, legend_breaks = -1:4, 
    legend_labels = c("0", "1e-4", "1e-3", "1e-2", "1e-1", "1"))

compare_pheatmap(test, cellwidth = 15, cellheight = 12, main = "Example heatmap")

annotation_col = data.frame(
    CellType = factor(rep(c("CT1", "CT2"), 5)), 
    Time = 1:5
)
rownames(annotation_col) = paste("Test", 1:10, sep = "")

annotation_row = data.frame(
    GeneClass = factor(rep(c("Path1", "Path2", "Path3"), c(10, 4, 6)))
)
rownames(annotation_row) = paste("Gene", 1:20, sep = "")

compare_pheatmap(test, annotation_col = annotation_col)

compare_pheatmap(test, annotation_col = annotation_col, annotation_legend = FALSE)

compare_pheatmap(test, annotation_col = annotation_col, 
    annotation_row = annotation_row)

compare_pheatmap(test, annotation_col = annotation_col, 
    annotation_row = annotation_row, angle_col = "45")

compare_pheatmap(test, annotation_col = annotation_col, angle_col = "0")

ann_colors = list(
    Time = c("white", "firebrick"),
    CellType = c(CT1 = "#1B9E77", CT2 = "#D95F02"),
    GeneClass = c(Path1 = "#7570B3", Path2 = "#E7298A", Path3 = "#66A61E")
)

compare_pheatmap(test, annotation_col = annotation_col, 
    annotation_colors = ann_colors, main = "Title")

compare_pheatmap(test, annotation_col = annotation_col, 
    annotation_row = annotation_row, annotation_colors = ann_colors)

compare_pheatmap(test, annotation_col = annotation_col, 
    annotation_colors = ann_colors[2]) 

compare_pheatmap(test, annotation_col = annotation_col, cluster_rows = FALSE, 
    gaps_row = c(10, 14))

compare_pheatmap(test, annotation_col = annotation_col, cluster_rows = FALSE, 
    gaps_row = c(10, 14), cutree_col = 2)

labels_row = c("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 
    "", "", "Il10", "Il15", "Il1b")
compare_pheatmap(test, annotation_col = annotation_col, labels_row = labels_row)

drows = dist(test, method = "minkowski")
dcols = dist(t(test), method = "minkowski")
compare_pheatmap(test, clustering_distance_rows = drows, 
    clustering_distance_cols = dcols)

library(dendsort)
callback = function(hc, ...){dendsort(hc)}
compare_pheatmap(test, clustering_callback = callback)