Mangiola et al., (2020). tidyHeatmap: an R package for modular heatmap production based on tidy principles. Journal of Open Source Software, 5(52), 2472, https://doi.org/10.21105/joss.02472
tidyHeatmap is a package that introduces tidy principles
to the creation of information-rich heatmaps. This package uses ComplexHeatmap
as graphical engine. website: stemangiola.github.io/tidyHeatmap
Advantages:
annotation_group,
e.g. heatmap(...) |> annotation_group(...)| Function | Description | 
|---|---|
| heatmap | Plots base heatmap | 
| annotation_group | Adds group annotation strips and grouping to the heatmap (replaces group_by) | 
| annotation_tile | Adds tile annotation to the heatmap | 
| annotation_point | Adds point annotation to the heatmap | 
| annotation_bar | Adds bar annotation to the heatmap | 
| annotation_numeric | Adds bar + number annotation to the heatmap | 
| annotation_line | Adds line annotation to the heatmap | 
| layer_text | Add layer of text on top of the heatmap | 
| layer_point | Adds layer of symbols on top of the heatmap | 
| layer_square | Adds layer of symbols on top of the heatmap | 
| layer_diamond | Adds layer of symbols on top of the heatmap | 
| layer_arrow_up | Adds layer of symbols on top of the heatmap | 
| layer_arrow_down | Add layer of symbols on top of the heatmap | 
| layer_star | Add layer of symbols on top of the heatmap | 
| layer_asterisk | Add layer of symbols on top of the heatmap | 
| split_rows | Splits the rows based on the dendogram | 
| split_columns | Splits the columns based on the dendogram | 
| save_pdf | Saves the PDF of the heatmap | 
| + | Integrate heatmaps side-by-side | 
| as_ComplexHeatmap | Convert the tidyHeatmap output to ComplexHeatmap for non-standard “drawing” | 
| wrap_heatmap | Allows the integration with the patchworkpackage | 
To install the most up-to-date version
To install the most stable version (however please keep in mind that this package is under a maturing lifecycle stage)
If you want to contribute to the software, report issues or problems with the software or seek support please open an issue here
The heatmaps visualise a multi-element, multi-feature dataset, annotated with independent variables. Each observation is a element-feature pair (e.g., person-physical characteristics).
| element | feature | value | independent_variables | 
|---|---|---|---|
| chrorfctr | chrorfctr | numeric | … | 
Let’s transform the mtcars dataset into a tidy “element-feature-independent variables” data frame. Where the independent variables in this case are ‘hp’ and ‘vs’.
mtcars_tidy <- 
    mtcars |> 
    as_tibble(rownames="Car name") |> 
    
    # Scale
    mutate_at(vars(-`Car name`, -hp, -vs), scale) |>
    
    # tidyfy
    pivot_longer(cols = -c(`Car name`, hp, vs), names_to = "Property", values_to = "Value")
mtcars_tidy## # A tibble: 288 × 5
##    `Car name`       hp    vs Property Value[,1]
##    <chr>         <dbl> <dbl> <chr>        <dbl>
##  1 Mazda RX4       110     0 mpg          0.151
##  2 Mazda RX4       110     0 cyl         -0.105
##  3 Mazda RX4       110     0 disp        -0.571
##  4 Mazda RX4       110     0 drat         0.568
##  5 Mazda RX4       110     0 wt          -0.610
##  6 Mazda RX4       110     0 qsec        -0.777
##  7 Mazda RX4       110     0 am           1.19 
##  8 Mazda RX4       110     0 gear         0.424
##  9 Mazda RX4       110     0 carb         0.735
## 10 Mazda RX4 Wag   110     0 mpg          0.151
## # ℹ 278 more rowsFor plotting, you simply pipe the input data frame into heatmap, specifying:
mtcars
mtcars_heatmap <- 
    mtcars_tidy |> 
    heatmap(`Car name`, Property, Value,    scale = "row"   ) |>
    annotation_tile(hp)## tidyHeatmap says: If you use tidyHeatmap for scientific research, please cite: Mangiola, S. and Papenfuss, A.T., 2020. 'tidyHeatmap: an R package for modular heatmap production based on tidy principles.' Journal of Open Source Software. doi:10.21105/joss.02472.
## This message is displayed once per session.Choose alternative clustering distance and methods.
tidyHeatmap::pasilla |>
    
    heatmap(
        .column = sample,
        .row = symbol,
        .value = `count normalised adjusted`,   
        scale = "row",
        
        # Arguments passed to ComplexHeatmap 
        clustering_distance_rows = "manhattan",
        clustering_distance_columns = "manhattan",
        clustering_method_rows = "ward.D",
        clustering_method_columns = "ward.D"
    ) We can easily group the data (one group per dimension maximum, at the moment only the vertical dimension is supported) with dplyr, and the heatmap will be grouped accordingly
# Make up more groupings
mtcars_tidy_groupings = 
    mtcars_tidy |>
    mutate(property_group = if_else(Property %in% c("cyl", "disp"), "Engine", "Other"))
mtcars_tidy_groupings |> 
    heatmap(`Car name`, Property, Value, scale = "row") |>
    annotation_group(vs, property_group) |>
    annotation_tile(hp)You can provide colour palettes and other aesthetics to groupings
mtcars_tidy_groupings |> 
    heatmap(
        `Car name`, Property, Value ,  
        scale = "row"
    ) |>
    annotation_group(
        vs, property_group,
        palette_grouping = list(
            # For first grouping (vs)
            c("#66C2A5", "#FC8D62"), 
            # For second grouping (property_group)
            c("#b58b4c", "#74a6aa")
        ),
        group_label_fontsize = 14,
        show_group_name = TRUE,
        group_strip_height = grid::unit(20, "pt")
    ) |>
    annotation_tile(hp)We can split based on the cladogram
mtcars_tidy |> 
    heatmap(`Car name`, Property, Value,    scale = "row"   ) |>
    split_rows(2) |>
    split_columns(2)We can split on kmean clustering (using ComplexHeatmap options, it is stochastic)
We can easily use custom palette, using strings, hexadecimal color character vector,
mtcars_tidy |> 
    heatmap(
        `Car name`, 
        Property, 
        Value,  
        scale = "row",
        palette_value = c("red", "white", "blue")
    )A better-looking blue-to-red palette
mtcars_tidy |> 
    heatmap(
        `Car name`, 
        Property, 
        Value,  
        scale = "row",
        palette_value = circlize::colorRamp2(
            seq(-2, 2, length.out = 11), 
            RColorBrewer::brewer.pal(11, "RdBu")
        )
    )Or a grid::colorRamp2 function for higher flexibility
mtcars_tidy |> 
    heatmap(
        `Car name`, 
        Property, 
        Value,  
        scale = "row",
        palette_value = circlize::colorRamp2(c(-2, -1, 0, 1, 2), viridis::magma(5))
    )We can use custom colors for tile annotation
mtcars_tidy |> 
    heatmap(
        `Car name`, 
        Property, 
        Value,  
        scale = "row"
    ) |>
    annotation_tile(
        hp, 
        palette = c("red", "white", "blue")
    )We can use grid::colorRamp2 function for tile annotation for specific color scales
mtcars_tidy |> 
    heatmap(
        `Car name`, 
        Property, 
        Value,  
        scale = "row"
    ) |>
    annotation_tile(
        hp, 
        palette = circlize::colorRamp2(c(0, 100, 200, 300), viridis::magma(4))
    )tidyHeatmap::pasilla |>
    heatmap(
        .column = sample,
        .row = symbol,
        .value = `count normalised adjusted`,  
        scale = "row"
    ) |>
    annotation_group(location, type) |>
    annotation_tile(condition) |>
    annotation_tile(activation)Example with custom aesthetics for annotation_group
tidyHeatmap::pasilla |>
    heatmap(
        .column = sample,
        .row = symbol,
        .value = `count normalised adjusted`,  
        scale = "row"
    ) |>
    annotation_group(
        location, type,
        group_label_fontsize = 12,
        group_strip_height = grid::unit(15, "pt"),
        show_group_name = FALSE
    ) |>
    annotation_tile(condition) |>
    annotation_tile(activation)Remove legends, adding aesthetics to annotations in a modular
fashion, using ComplexHeatmap arguments
tidyHeatmap::pasilla |>
    heatmap(
        .column = sample,
        .row = symbol,
        .value = `count normalised adjusted`,   
        scale = "row",
        show_heatmap_legend = FALSE
    ) |>
    annotation_tile(condition, show_legend = FALSE) |>
    annotation_tile(activation, show_legend = FALSE)“tile”, “point”, “bar”, “line” and “numeric” are available
# Create some more data points
pasilla_plus <- 
    tidyHeatmap::pasilla |>
    dplyr::mutate(activation_2 = activation, activation_3 = activation) |> 
    tidyr::nest(data = -sample) |>
    dplyr::mutate(size = rnorm(n(), 4,0.5)) |>
    dplyr::mutate(age = runif(n(), 50, 200)) |>
    tidyr::unnest(data) 
# Plot
pasilla_plus |>
    heatmap(
        .column = sample,
        .row = symbol,
        .value = `count normalised adjusted`,   
        scale = "row"
    ) |>
    annotation_tile(condition) |>
    annotation_point(activation) |>
    annotation_numeric(activation_3) |>
    annotation_tile(activation_2) |>
    annotation_bar(size) |>
    annotation_line(age)We can customise annotation sizes using the
grid::unit(), and the size of their names using in-built
ComplexHeatmap arguments
pasilla_plus |>
    heatmap(
        .column = sample,
        .row = symbol,
        .value = `count normalised adjusted`,   
        scale = "row"
    ) |>
    annotation_tile(condition, size = unit(0.3, "cm"),  annotation_name_gp= gpar(fontsize = 8)) |>
    annotation_point(activation, size = unit(0.3, "cm"),    annotation_name_gp= gpar(fontsize = 8)) |>
    annotation_tile(activation_2, size = unit(0.3, "cm"),   annotation_name_gp= gpar(fontsize = 8)) |>
    annotation_bar(size, size = unit(0.3, "cm"),    annotation_name_gp= gpar(fontsize = 8)) |>
    annotation_line(age, size = unit(0.3, "cm"),    annotation_name_gp= gpar(fontsize = 8))Add a layer on top of the heatmap
tidyHeatmap::pasilla |>
    
    # filter
    filter(symbol %in% head(unique(tidyHeatmap::pasilla$symbol), n = 10)) |>
    
    # Add dynamic size
    mutate(my_size = runif(n(), 1,5)) |> 
    
    heatmap(
        .column = sample,
        .row = symbol,
        .value = `count normalised adjusted`,   
        scale = "row"
    ) |> 
    layer_point(
        `count normalised adjusted log` > 6 & sample == "untreated3"
    ) |>
    layer_square(
        `count normalised adjusted log` > 6 & sample == "untreated2",
        .size = my_size
    ) |>
    layer_arrow_up(
        `count normalised adjusted log` > 6 & sample == "untreated1",
        .size = 4
    )Add a text layer on top of the heatmap
tidyHeatmap::pasilla |>
    
    # filter
    filter(symbol %in% head(unique(tidyHeatmap::pasilla$symbol), n = 10)) |>
    
    # Add dynamic text
    mutate(my_text = "mt", my_size = 7) |> 
    
    # Plot
    heatmap(
        .column = sample,
        .row = symbol,
        .value = `count normalised adjusted`,   
        scale = "row"
    ) |> 
    layer_text(
        `count normalised adjusted log` > 6 & sample == "untreated3", 
        .value = "a", 
        .size = 15
    ) |> 
    layer_text(
    `count normalised adjusted log` > 6 & sample == "untreated2", 
    .value = my_text,
    .size = my_size
)Add more than two heatmaps
Note:
Due to limitations in R’s method dispatch, adding more than two tidyHeatmap heatmaps using the
+operator (e.g.p_heatmap + p_heatmap + p_heatmap) may result in an error or unexpected behavior.To robustly combine more than two heatmaps, convert each to a ComplexHeatmap object first, this will use the
+functionality ofComplexHeatmap. This ensures that all objects are compatible and can be combined using ComplexHeatmap’s native addition functionality.
## Warning: package 'ggplot2' was built under R version 4.5.1## Warning: package 'patchwork' was built under R version 4.5.1p_heatmap =
    mtcars_tidy |> 
    heatmap(
        `Car name`, Property, Value,    
        scale = "row", 
            show_heatmap_legend = FALSE,
        row_names_gp = gpar(fontsize = 7)
    ) 
p_ggplot = data.frame(value = 1:10) |> ggplot(aes(value)) + geom_density()
wrap_heatmap(p_heatmap) + 
    p_ggplot +
    
    # Add padding for better aesthetics
    wrap_heatmap(
        p_heatmap,
        padding = grid::unit(c(-30, -0, -0, -10), "points" ),
        clip = FALSE
    ) + 
    plot_layout(width = c(1, 0.3, 1))mtcars_tidy |> 
    heatmap(
        `Car name`, Property, Value,    
        scale = "row", 
        rect_gp = grid::gpar(col = "#161616", lwd = 0.5)
    ) ## Warning: package 'forcats' was built under R version 4.5.1mtcars_tidy |> 
    mutate(`Car name` = forcats::fct_reorder(`Car name`, `Car name`, .desc = TRUE)) %>% 
    heatmap(
        `Car name`, Property, Value,    
        scale = "row", 
        cluster_rows = FALSE
    ) mtcars_tidy |> 
    mutate(`Car name` = forcats::fct_reorder(`Car name`, `Car name`, .desc = TRUE)) %>% 
    heatmap(
        `Car name`, Property, Value,    
        scale = "row", 
        column_dend_height = unit(0.2, "cm"), 
        row_dend_width = unit(0.2, "cm")
    ) mtcars_tidy |> 
    mutate(`Car name` = forcats::fct_reorder(`Car name`, `Car name`, .desc = TRUE)) %>% 
    heatmap(
        `Car name`, Property, Value,    
        scale = "row", 
        row_names_gp = gpar(fontsize = 7),
        column_names_gp = gpar(fontsize = 7),
        column_title_gp = gpar(fontsize = 7),
        row_title_gp = gpar(fontsize = 7)
    ) You can completely remove the row and column titles by setting them to empty strings:
mtcars_tidy |> 
    mutate(`Car name` = forcats::fct_reorder(`Car name`, `Car name`, .desc = TRUE)) |> 
    heatmap(
        `Car name`, Property, Value,    
        scale = "row", 
        row_title = "",      # Remove row title
        column_title = ""    # Remove column title
    ) This can be done only for annotation_numeric because of
ComplexHeatmap requirements
(?ComplexHeatmap::anno_numeric)
mtcars_tidy |> 
    mutate(`Car name` = forcats::fct_reorder(`Car name`, `Car name`, .desc = TRUE)) %>% 
    heatmap(
        `Car name`, Property, Value,    
        scale = "row"
    ) |> 
  annotation_numeric(hp, align_to="right")ComplexHeatmap functionalitiesComplexHeatmap has some graphical functionalities that are not
included in the standard functional framework. We can use
as_ComplexHeatmap to convert our output before applying
drawing options.
heatmap(mtcars_tidy, `Car name`, Property, Value, scale = "row" ) %>%
    as_ComplexHeatmap() %>%
    ComplexHeatmap::draw(heatmap_legend_side = "left"   )## R version 4.5.0 (2025-04-11)
## Platform: x86_64-apple-darwin20
## Running under: macOS Sonoma 14.6.1
## 
## Matrix products: default
## BLAS:   /Library/Frameworks/R.framework/Versions/4.5-x86_64/Resources/lib/libRblas.0.dylib 
## LAPACK: /Library/Frameworks/R.framework/Versions/4.5-x86_64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.1
## 
## locale:
## [1] C/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## time zone: Australia/Adelaide
## tzcode source: internal
## 
## attached base packages:
## [1] grid      stats     graphics  grDevices utils     datasets  methods  
## [8] base     
## 
## other attached packages:
## [1] forcats_1.0.1      patchwork_1.3.2    ggplot2_4.0.0      tidyHeatmap_1.12.3
## [5] tidyr_1.3.1        dplyr_1.1.4       
## 
## loaded via a namespace (and not attached):
##  [1] viridis_0.6.5         utf8_1.2.6            sass_0.4.10          
##  [4] generics_0.1.4        shape_1.4.6.1         digest_0.6.37        
##  [7] magrittr_2.0.4        evaluate_1.0.5        RColorBrewer_1.1-3   
## [10] iterators_1.0.14      circlize_0.4.16       fastmap_1.2.0        
## [13] foreach_1.5.2         doParallel_1.0.17     jsonlite_2.0.0       
## [16] GlobalOptions_0.1.2   gridExtra_2.3         ComplexHeatmap_2.25.2
## [19] purrr_1.1.0           viridisLite_0.4.2     scales_1.4.0         
## [22] codetools_0.2-20      jquerylib_0.1.4       cli_3.6.5            
## [25] rlang_1.1.6           crayon_1.5.3          withr_3.0.2          
## [28] cachem_1.1.0          yaml_2.3.10           tools_4.5.0          
## [31] parallel_4.5.0        colorspace_2.1-2      GetoptLong_1.0.5     
## [34] BiocGenerics_0.55.4   vctrs_0.6.5           R6_2.6.1             
## [37] png_0.1-8             magick_2.9.0          matrixStats_1.5.0    
## [40] stats4_4.5.0          lifecycle_1.0.4       S4Vectors_0.47.4     
## [43] IRanges_2.43.5        clue_0.3-66           cluster_2.1.8.1      
## [46] dendextend_1.19.1     pkgconfig_2.0.3       gtable_0.3.6         
## [49] pillar_1.11.1         bslib_0.9.0           Rcpp_1.1.0           
## [52] glue_1.8.0            xfun_0.53             tibble_3.3.0         
## [55] tidyselect_1.2.1      rstudioapi_0.17.1     knitr_1.50           
## [58] farver_2.1.2          rjson_0.2.23          htmltools_0.5.8.1    
## [61] labeling_0.4.3        rmarkdown_2.30        Cairo_1.6-5          
## [64] compiler_4.5.0        S7_0.2.0