Skip to contents

In this thread, We’ll use ggalign to draw all the heatmap in https://jokergoo.github.io/ComplexHeatmap-reference/book/a-single-heatmap.html

library(ggalign)
#> Loading required package: ggplot2
set.seed(123)
nr1 <- 4
nr2 <- 8
nr3 <- 6
nr <- nr1 + nr2 + nr3
nc1 <- 6
nc2 <- 8
nc3 <- 10
nc <- nc1 + nc2 + nc3
mat <- cbind(
    rbind(
        matrix(rnorm(nr1 * nc1, mean = 1, sd = 0.5), nrow = nr1),
        matrix(rnorm(nr2 * nc1, mean = 0, sd = 0.5), nrow = nr2),
        matrix(rnorm(nr3 * nc1, mean = 0, sd = 0.5), nrow = nr3)
    ),
    rbind(
        matrix(rnorm(nr1 * nc2, mean = 0, sd = 0.5), nrow = nr1),
        matrix(rnorm(nr2 * nc2, mean = 1, sd = 0.5), nrow = nr2),
        matrix(rnorm(nr3 * nc2, mean = 0, sd = 0.5), nrow = nr3)
    ),
    rbind(
        matrix(rnorm(nr1 * nc3, mean = 0.5, sd = 0.5), nrow = nr1),
        matrix(rnorm(nr2 * nc3, mean = 0.5, sd = 0.5), nrow = nr2),
        matrix(rnorm(nr3 * nc3, mean = 1, sd = 0.5), nrow = nr3)
    )
)
mat <- mat[sample(nr, nr), sample(nc, nc)]
rownames(mat) <- paste0("row", seq_len(nr))
colnames(mat) <- paste0("column", seq_len(nc))

Colors

Because the ComplexHeatmap will reorder the dendrogram by default, but align_dendro won’t change the tree layout. In following codes, we hypothesized that the arguments row_dend_reorder and column_dend_reorder of ComplexHeatmap::Heatmap is FALSE.

It is important to note that ggalign considers the left-bottom as the starting point, while ComplexHeatmap considers the left-top as the starting point.

The scale_fill_* function in ggplot2 makes it easy to modify colors.

dim(mat)
#> [1] 18 24
ggheatmap(mat) +
    scale_fill_gradient2(low = "#2600D1FF", high = "#EE3F3FFF") +
    theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
    hmanno("r", size = unit(15, "mm")) +
    align_dendro() +
    hmanno("t", size = unit(15, "mm")) +
    align_dendro() +
    hmanno(NULL) &
    theme(plot.margin = margin())

# ComplexHeatmap::Heatmap(mat,
#   row_dend_reorder = FALSE,
#   column_dend_reorder = FALSE
# )
ggheatmap(mat) +
    scale_fill_gradient2(low = "green", high = "red") +
    theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
    hmanno("r", size = unit(15, "mm")) +
    align_dendro() +
    hmanno("t", size = unit(15, "mm")) +
    align_dendro() +
    hmanno(NULL) &
    theme(plot.margin = margin())

oob argument in the scale_fill_* function can be used to deal with the outliers.

mat2 <- mat
mat2[1, 1] <- 100000
ggheatmap(mat2) +
    scale_fill_gradient2(
        low = "green", high = "red",
        limits = c(-2, 2),
        oob = scales::squish
    ) +
    theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
    hmanno("r", size = unit(15, "mm")) +
    align_dendro() +
    theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
    hmanno("t", size = unit(15, "mm")) +
    align_dendro() +
    hmanno(NULL) &
    theme(plot.margin = margin())

We can use align_plots() to arrange them.

h1 <- ggheatmap(mat) +
    scale_fill_gradient2(name = "mat", low = "green", high = "red") +
    theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
    hmanno("r", size = unit(15, "mm")) +
    align_dendro() +
    theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
    hmanno("t", size = unit(15, "mm")) +
    align_dendro() +
    hmanno(NULL) &
    theme(plot.margin = margin())

h2 <- ggheatmap(mat / 4) +
    scale_fill_gradient2(
        name = "mat/4", limits = c(-2, 2L),
        oob = scales::squish,
        low = "green", high = "red"
    ) +
    theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
    hmanno("r", size = unit(15, "mm")) +
    align_dendro() +
    theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
    hmanno("t", size = unit(15, "mm")) +
    align_dendro() +
    hmanno(NULL) &
    theme(plot.margin = margin())

h3 <- ggheatmap(abs(mat)) +
    scale_fill_gradient2(name = "abs(mat)", low = "green", high = "red") +
    theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
    hmanno("r", size = unit(15, "mm")) +
    align_dendro() +
    theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
    hmanno("t", size = unit(15, "mm")) +
    align_dendro() +
    hmanno(NULL) &
    theme(plot.margin = margin())
align_plots(h1, h2, h3, ncol = 2L)

ggheatmap(mat) +
    scale_fill_gradientn(colors = rev(rainbow(10))) +
    theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
    hmanno("r", size = unit(15, "mm")) +
    align_dendro() +
    hmanno("t", size = unit(15, "mm")) +
    align_dendro() +
    hmanno(NULL) &
    theme(plot.margin = margin())

For character matrix, you can use ggplot2 discrete filling scales.

discrete_mat <- matrix(sample(1:4, 100, replace = TRUE), 10, 10)
colors <- structure(1:4, names = c("1", "2", "3", "4")) # black, red, green, blue
ggheatmap(discrete_mat, aes(fill = factor(value))) +
    scale_fill_manual(values = colors) +
    hmanno("r", size = unit(15, "mm")) +
    align_dendro() +
    hmanno("t", size = unit(15, "mm")) +
    align_dendro() +
    hmanno(NULL) &
    theme(plot.margin = margin())

discrete_mat <- matrix(sample(letters[1:4], 100, replace = TRUE), 10, 10)
colors <- structure(1:4, names = letters[1:4])
ggheatmap(discrete_mat) +
    scale_fill_manual(values = colors)

mat_with_na <- mat
na_index <- sample(c(TRUE, FALSE),
    nrow(mat) * ncol(mat),
    replace = TRUE, prob = c(1, 9)
)
mat_with_na[na_index] <- NA
ggheatmap(mat_with_na) +
    scale_fill_gradient2(
        low = "#2600D1FF",
        high = "#EE3F3FFF",
        na.value = "black"
    ) +
    theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
    hmanno("r", size = unit(15, "mm")) +
    align_dendro() +
    hmanno("t", size = unit(15, "mm")) +
    align_dendro() +
    hmanno(NULL) &
    theme(plot.margin = margin())

We won’t compare the LAB and RGB space. If you want to convert color between different color space, try to use farver pacakge.

In ggplot2, you can use panel.border argument in theme() function to control the Heatmap body border.

ggheatmap(mat) +
    scale_fill_gradient2(low = "#2600D1FF", high = "#EE3F3FFF") +
    theme(
        axis.text.x = element_text(angle = -60, hjust = 0),
        panel.border = element_rect(linetype = "dashed", fill = NA)
    ) +
    hmanno("r", size = unit(15, "mm")) +
    align_dendro() +
    hmanno("t", size = unit(15, "mm")) +
    align_dendro() +
    hmanno(NULL) &
    theme(plot.margin = margin())

You can use color aesthetic to specify the cell border color and linewidth aesthetic to specify the border width.

ggheatmap(mat, color = "white") +
    scale_fill_gradient2(low = "#2600D1FF", high = "#EE3F3FFF") +
    theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
    hmanno("r", size = unit(15, "mm")) +
    align_dendro() +
    hmanno("t", size = unit(15, "mm")) +
    align_dendro() +
    hmanno(NULL) &
    theme(plot.margin = margin())

You can use filling argument to turn off the heatmap cell filling.

ggheatmap(mat, filling = FALSE) +
    theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
    hmanno("r", size = unit(15, "mm")) +
    align_dendro() +
    hmanno("t", size = unit(15, "mm")) +
    align_dendro() +
    hmanno(NULL) &
    theme(plot.margin = margin())

Titles

We can use patch_titles() to add titles around each border of the plot. You can use theme() to control the text appearance.

ggheatmap(mat) +
    scale_fill_gradient2(low = "#2600D1FF", high = "#EE3F3FFF") +
    theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
    hmanno("r", size = unit(20, "mm")) +
    align_dendro() +
    patch_titles(right = "I am a row title") +
    theme(plot.patch_title.right = element_text(face = "bold", size = 16)) +
    hmanno("t", size = unit(15, "mm")) +
    align_dendro() +
    patch_titles(top = "I am a column title") +
    theme(plot.patch_title.top = element_text(face = "bold", size = 16)) +
    hmanno(NULL) &
    theme(plot.margin = margin())

Clustering

ggheatmap(mat) +
    scale_fill_gradient2(low = "#2600D1FF", high = "#EE3F3FFF") +
    theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
    hmanno("r", size = unit(20, "mm")) +
    align_dendro(distance = "pearson") +
    theme(plot.patch_title.right = element_text(face = "bold", size = 16)) +
    hmanno("t", size = unit(15, "mm")) +
    align_dendro() +
    patch_titles(top = "pre-defined distance method (1 - pearson)") +
    theme(plot.patch_title.top = element_text(face = "bold", size = 16)) +
    hmanno(NULL) &
    theme(plot.margin = margin())

# ComplexHeatmap::Heatmap(mat,
#   name = "mat", clustering_distance_rows = "pearson",
#   column_title = "pre-defined distance method (1 - pearson)",
#   row_dend_reorder = FALSE,
#   column_dend_reorder = FALSE
# )

Session information

sessionInfo()
#> R version 4.4.1 (2024-06-14)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 22.04.5 LTS
#> 
#> Matrix products: default
#> BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3 
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.20.so;  LAPACK version 3.10.0
#> 
#> locale:
#>  [1] LC_CTYPE=C.UTF-8       LC_NUMERIC=C           LC_TIME=C.UTF-8       
#>  [4] LC_COLLATE=C.UTF-8     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: UTC
#> tzcode source: system (glibc)
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] ggalign_0.0.4 ggplot2_3.5.1
#> 
#> loaded via a namespace (and not attached):
#>  [1] gtable_0.3.5      jsonlite_1.8.8    highr_0.11        dplyr_1.1.4      
#>  [5] compiler_4.4.1    tidyselect_1.2.1  tidyr_1.3.1       jquerylib_0.1.4  
#>  [9] systemfonts_1.1.0 scales_1.3.0      textshaping_0.4.0 yaml_2.3.10      
#> [13] fastmap_1.2.0     R6_2.5.1          labeling_0.4.3    generics_0.1.3   
#> [17] knitr_1.48        tibble_3.2.1      desc_1.4.3        munsell_0.5.1    
#> [21] bslib_0.8.0       pillar_1.9.0      rlang_1.1.4       utf8_1.2.4       
#> [25] cachem_1.1.0      xfun_0.47         fs_1.6.4          sass_0.4.9       
#> [29] cli_3.6.3         pkgdown_2.1.1     withr_3.0.1       magrittr_2.0.3   
#> [33] digest_0.6.37     grid_4.4.1        lifecycle_1.0.4   vctrs_0.6.5      
#> [37] evaluate_1.0.0    glue_1.7.0        farver_2.1.2      ragg_1.3.3       
#> [41] fansi_1.0.6       colorspace_2.1-1  rmarkdown_2.28    purrr_1.0.2      
#> [45] tools_4.4.1       pkgconfig_2.0.3   htmltools_0.5.8.1