Chapter 13 Other Tricks

13.1 Set the same cell size for different heatmaps with different dimensions

Assume you have a list of heatmaps/oncoPrints that you want to save as different e.g. png or pdf files, one thing you might want to do is to make the size of each grid/cell in the heatmap identical across heatmaps, thus, you need to calculate the size of png/pdf file according to the number of rows or columns in the heatmap. In the heatmap generated by ComplexHeatmap, all the heatmap components have absolute size and only the size of the heatmap body (or the size of the cells) is changable (or in other words, if you change the size of the final graphic device, e.g. by draging the graphics window if you plot in, only the size of the heatmap body is adjusted), which means, the size of the whole plot is linearly related to the number of rows or columns in the heatmap. This implies we can actually fit a linear model y = a*x + b where e.g. y is the height of the whole plot and x is the number of rows.

In following example, we simply demonstrate how to establish the relation between the plot height and the number of rows in the heatmap. We first define a function which generates a 10-column matrix with specific number of rows. Note the values in the matrix is of no importance in this demonstration.

random_mat = function(nr) {
    m = matrix(rnorm(10*nr), nc = 10)
    colnames(m) = letters[1:10]

Since the relation is absolutely linear, we only need to test two heatmaps with different number of rows where the height of a single row is unit(5, "mm"). In the heatmap, there are also column title, column dendrogram, column annotation and the column names.

There are several things that needs to be noted in following code:

  1. The heatmap object should be returned by draw() because the layout of the heatmap is calculated only after the execution of draw().
  2. component_height() returns a vector of units which correspond to the height of all heatmap components from top to bottom in the heatmap. (component_width() returns the width of heatmap components).
  3. When calculating ht_height, we add unit(4, "mm") because on top and bottom of the final plot, there are 2mm white borders.
  4. ht_height needs to be converted to a simple unit in cm or inch.

In following, y contains values which are measured in inch unit.

y = NULL
for(nr in c(10, 20)) {
    ht = draw(Heatmap(random_mat(nr), height = unit(5, "mm")*nr, 
        column_title = "foo", # one line text
        top_annotation = HeatmapAnnotation(bar = 1:10)))
    ht_height = sum(component_height(ht)) + unit(4, "mm")
    ht_height = convertHeight(ht_height, "inch", valueOnly = TRUE)
    y = c(y, ht_height)

Then we can fit a linear relation between y and the number of rows:

x = c(10, 20)
lm(y ~ x)
## Call:
## lm(formula = y ~ x)
## Coefficients:
## (Intercept)            x  
##      1.2222       0.1969

This means the relation between the number of rows x and the height of the plot y is: y = 0.1969*x + 1.3150.

You can test whether the height of single rows are the same for heatmaps with different rows by following code. Note all the heatmap configuations should be the same as the ones you prepare y.

for(nr in c(10, 20)) {
    png(paste0("test_heatmap_nr_", nr, ".png"), width = 5, height = 0.1969*nr + 1.3150, 
        units = "in", res = 100)
    draw(Heatmap(random_mat(nr), height = unit(5, "mm")*nr, 
        column_title = "foo", # column title can be any one-line string
        top_annotation = HeatmapAnnotation(bar = 1:10)))