Heatmap Layout

layout_heatmap() utilizes the grammar of graphics to construct the heatmap and heatmap annotations. You can also use the alias ggheatmap().

library(ggalign)

Input data

The data input can be a numeric or character vector, a data frame, and any other data which can be converted into a matrix.

set.seed(123)
small_mat <- matrix(rnorm(81), nrow = 9)
rownames(small_mat) <- paste0("row", seq_len(nrow(small_mat)))
colnames(small_mat) <- paste0("column", seq_len(ncol(small_mat)))
ggheatmap(small_mat)

Heatmap body

For ggplot2 usage, the matrix input will be converted into a long formated data frame when drawing. The default mapping will use aes(.data$.x, .data$.y), but can be controlled using mapping argument. The data in the underlying ggplot object contains following columns:

You can treat the layout_heatmap() object as a normal ggplot2 object with a default mapping and data. You can add ggplot2 elements as usual.

ggheatmap(small_mat) + geom_point() + scale_fill_viridis_c()

By default, we’ll add the heatmap layer. If you set filling = FALSE, a blank heamtap will be drawn.

ggheatmap(small_mat, filling = FALSE)

Heatmap annotations

Heatmap annotations provide additional information for the rows or columns of the heatmap and can be placed at the top, left, bottom, or right. An annotation is a layout_stack() object internally, capable of holding multiple plots.

By default, ggheatmap() does not initialize an active context, so all additions are placed within the heatmap body. You can use hmanno() to set the active context, directing all subsequent additions to this position. The active context allows for custom layout adjustments and the addition of various plot types.

In the following example, align_kmeans() is used to group the columns into 3 panels. It doesn’t matter if this is added to the top or bottom since it won’t add a plot area:

ggheatmap(small_mat) +
  hmanno("t") +
  align_kmeans(3L)

We can add any align_*() function to the annotation. For more details on align_*() functions, refer to vignette("align-layout") and vignette("align-plot").

ggheatmap(small_mat) +
  theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
  hmanno("r") +
  align_dendro(k = 3L) +
  geom_point(aes(color = factor(branch)))

In this example:

Control size

We can use hmanno() to control the relative sizes between heatmap body and the whole annotation stack.

ggheatmap(small_mat) +
  scale_fill_viridis_c() +
  hmanno(height = 1) +
  hmanno("t", size = 2) +
  align_dendro() +
  ggalign(data = rowSums) +
  geom_bar(aes(y = value, fill = .x), stat = "identity")

ggheatmap(small_mat) +
  scale_fill_viridis_c() +
  hmanno("t", size = 1) +
  align_dendro() +
  ggalign(data = rowSums) +
  geom_bar(aes(y = value, fill = .x), stat = "identity")

We can use unit() to define the size.

ggheatmap(small_mat) +
  scale_fill_viridis_c() +
  hmanno("t", size = unit(30, "mm")) +
  align_dendro() +
  ggalign(data = rowSums) +
  geom_bar(aes(y = value, fill = .x), stat = "identity")

All align_*() functions has a size argument to control the relative width (left and right annotation) or height (top and bottom annotation) of the single plot in the whole annotation stack.

ggheatmap(small_mat) +
  scale_fill_viridis_c() +
  hmanno("l", size = 0.2) +
  ggalign(data = rowSums, aes(x = value), size = unit(10, "mm")) +
  geom_bar(
    aes(y = .y, fill = factor(.y)),
    stat = "identity",
    orientation = "y"
  ) +
  scale_fill_brewer(palette = "Set1", guide = "none") +
  scale_x_reverse()

Alignment of axis titles

By default, ggheatmap() won’t align the axis titles.

ggheatmap(small_mat) +
  scale_fill_viridis_c() +
  ylab("Heatmap title") +
  hmanno("t", size = unit(30, "mm")) +
  align_dendro() +
  ylab("Annotation title")

To align all axis titles, you can set free_labs = NULL in the hmanno() function. Alternatively, A boolean value or a single string containing one or more of axis positions (“t”, “l”, “b”, “r”) to indicate which axis titles should be free from alignment.

ggheatmap(small_mat) +
  hmanno(free_labs = NULL) +
  scale_fill_viridis_c() +
  ylab("Heatmap title") +
  hmanno("t", size = unit(30, "mm")) +
  align_dendro() +
  ylab("Annotation title")

align_*() functions include the free_labs argument, which, by default, inherits from the annotatioon stack. This allows you to control the alignment for each plot individually.

ggheatmap(small_mat) +
  hmanno(free_labs = NULL) +
  scale_fill_viridis_c() +
  ylab("Heatmap title") +
  hmanno("t", size = unit(30, "mm"), free_labs = "l") +
  align_dendro() +
  ylab("Annotation title")

Removing spaces when aligning elements

By default, we’ll align all elements of the plot, which can sometimes lead to unwanted spacing. Consider the following example:

ggheatmap(small_mat) +
  scale_fill_viridis_c() +
  hmanno("t", size = unit(30, "mm")) +
  align_dendro() +
  scale_y_continuous(
    expand = expansion(),
    labels = ~ paste("very very long labels", .x)
  ) +
  hmanno("l") +
  align_dendro() +
  scale_x_reverse(expand = expansion())

In this case, the left annotation stack is positioned far from the heatmap body due to the wide axis labels in the top annotation stack. This occurs because the top annotation stack is aligned with the heatmap body. To fix this, you can remove the left borders around the panel of the top annotation stack by setting free_spaces = "l".

ggheatmap(small_mat) +
  scale_fill_viridis_c() +
  hmanno("t", size = unit(30, "mm"), free_spaces = "l") +
  align_dendro() +
  scale_y_continuous(
    expand = expansion(),
    labels = ~ paste("very very long labels", .x)
  ) +
  hmanno("l") +
  align_dendro() +
  scale_x_reverse(expand = expansion())

The same with free_labs argument, you can control the free_spaces argument for the individual plot in align_*() function.

Session information

sessionInfo()
#> R version 4.4.0 (2024-04-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 24.04 LTS
#> 
#> Matrix products: default
#> BLAS/LAPACK: /usr/lib/x86_64-linux-gnu/libmkl_rt.so;  LAPACK version 3.8.0
#> 
#> locale:
#>  [1] LC_CTYPE=C.UTF-8       LC_NUMERIC=C           LC_TIME=C.UTF-8       
#>  [4] LC_COLLATE=C           LC_MONETARY=C.UTF-8    LC_MESSAGES=C.UTF-8   
#>  [7] LC_PAPER=C.UTF-8       LC_NAME=C              LC_ADDRESS=C          
#> [10] LC_TELEPHONE=C         LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C   
#> 
#> time zone: Asia/Shanghai
#> tzcode source: system (glibc)
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] ggalign_0.0.3 ggplot2_3.5.1
#> 
#> loaded via a namespace (and not attached):
#>  [1] gtable_0.3.5       jsonlite_1.8.8     dplyr_1.1.4        compiler_4.4.0    
#>  [5] highr_0.11         tidyselect_1.2.1   tidyr_1.3.1        jquerylib_0.1.4   
#>  [9] scales_1.3.0       yaml_2.3.8         fastmap_1.2.0      ggh4x_0.2.8       
#> [13] R6_2.5.1           labeling_0.4.3     generics_0.1.3     knitr_1.47        
#> [17] tibble_3.2.1       munsell_0.5.1      RColorBrewer_1.1-3 bslib_0.7.0       
#> [21] pillar_1.9.0       rlang_1.1.4        utf8_1.2.4         cachem_1.1.0      
#> [25] xfun_0.45          sass_0.4.9         viridisLite_0.4.2  cli_3.6.3         
#> [29] withr_3.0.0        magrittr_2.0.3     digest_0.6.36      grid_4.4.0        
#> [33] lifecycle_1.0.4    vctrs_0.6.5        evaluate_0.24.0    glue_1.7.0        
#> [37] farver_2.1.2       fansi_1.0.6        colorspace_2.1-0   rmarkdown_2.27    
#> [41] purrr_1.0.2        tools_4.4.0        pkgconfig_2.0.3    htmltools_0.5.8.1