The heatmap_layout()
/ggheatmap()
function provides a powerful way to create
customizable heatmaps in R using ggplot2. This vignette will guide you through
its usage.
The data input can be a numeric or character vector, a data frame, and any other data which can be converted into a matrix.
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:
.xpanel
and .ypanel
: the column and row panel
.x
and .y
: the x
and y
coordinates
.row_names
and .column_names
: A factor of the row and column names of
the original matrix (only applicable when names exist).
.row_index
and .column_index
: the row and column index of the original
matrix.
value
: the actual matrix value.
You can treat the heatmap_layout()
object as a normal ggplot2 object with a
default mapping
and data
. You can add ggplot2
elements as usual.
By default, a heatmap layer is added. If you set filling = FALSE
, a blank
heatmap will be drawn, allowing for customized filling geoms. For larger
matrices, you may want to use a raster layer for faster rendering. In such
cases, packages like ggrastr or
ggfx are helpful.
Additionally, ggfx
offers many image filters that can be applied to ggplot2 layers.
ggheatmap(small_mat, filling = FALSE) +
ggrastr::rasterise(geom_tile(aes(fill = value)), dev = "ragg")
Indeed, we have add ggrastr::rasterise()
method for both ggheatmap()
and
ggstack()
objects, you can directly call ggrastr::rasterise()
which will
rasterise all plots in the layout.
A heatmap typically has two observation axes, you can reorder the observations,
or provide additional information for them. An annotation is a stack_layout()
object internally, capable of holding multiple plots, and can be placed at the
top
, left
, bottom
, or right
.
Note that ggalign
use the concept of "number of observations"
in the
vctrs package or NROW()
function. When aligning the observations, you must ensure the number of
observations is equal. For column annotations, the layout data will be
transposed before use. This is necessary because column annotations use heatmap
columns as observations, but we need rows.
By default, heatmap_layout()
/ggheatmap()
does not initialize an active
context, so all additions are placed within the heatmap body. we 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 three
panels. It doesn’t matter if this is added to the top or bottom since it won’t
add a plot area:
We can add any align_*()
function to the annotation. For more details on
align_*()
functions, refer to vignette("layout-customize")
and
vignette("layout-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:
hmanno("r")
change the active context to the right of the heatmap.align_dendro(k = 3L)
adds a dendrogram to this right-side annotation
context and sets itself as the active plot in the annotation stack.geom_point(aes(color = factor(branch)))
is then added to this active plot
within the annotation stack, here, it means the align_dendro()
plot.You can specify the relative sizes of the heatmap body using the .width
and
.height
arguments in the ggheatmap() function. The dot prefix helps avoid
conflicts with arguments from geom_tile()
.
Alternatively, the hmanno()
function allows you to control the heatmap body
sizes when position
is NULL
.
The hmanno()
function allows you to control the total annotation stack size
when position
is a string
. The size
argument controls the relative width
(for left and right annotations) or height (for top and bottom annotations) of
the whole annotation stack.
We can use unit()
to define the size.
All align_*()
functions that add plots in the annotation stack have a size
argument to control the relative width (for left and right annotations) or
height (for top and bottom annotations) of the single plot in the annotation
stack.
By default, ggheatmap()
will collect all guide legends on the side from which
they originate.
heatmap_with_four_side_guides <- ggheatmap(small_mat) +
scale_fill_viridis_c(name = "I'm from heatmap body") +
theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
hmanno("t") +
align_dendro(aes(color = branch), k = 3L) +
scale_color_brewer(
name = "I'm from top annotation", palette = "Dark2",
guide = guide_legend(position = "right")
) +
hmanno("l") +
align_dendro(aes(color = branch), k = 3L) +
scale_color_brewer(
name = "I'm from left annotation", palette = "Dark2",
guide = guide_legend(position = "top")
) +
hmanno("b") +
align_dendro(aes(color = branch), k = 3L) +
scale_color_brewer(
name = "I'm from bottom annotation", palette = "Dark2",
guide = guide_legend(position = "left")
) +
hmanno("r") +
align_dendro(aes(color = branch), k = 3L) +
scale_color_brewer(
name = "I'm from right annotation", palette = "Dark2",
guide = guide_legend(position = "bottom")
) &
theme(plot.margin = margin())
heatmap_with_four_side_guides
This package provides the guides
and free_guides
arguments for precise control
over guide legends.
The guides
argument controls which positions of guide legends should be
collected in the layout. They will be collected to their original side.
For a heatmap layout, it has five plots (heatmap body, four sides of the nested
heatmap annotation stack). When position is NULL
, hmanno()
set the guides
for the heatmap layout.
heatmap_with_four_side_guides +
# the heatmap layout only collect guide legends in the top and bottom
hmanno(guides = "tb")
In this way, the guide legends in the left and right position won’t be collected.
In the nested layouts, the top-level layout won’t collect guide legends from plots within the nested layout unless the nested layout collects them first. Guide legends in the heatmap layout will only be collected from the annotation stack if the stack layout collects its own guide legends first.
When position
is a string, hmanno()
set the guides
for the heatmap
annotation stack. By default, it’ll inherit from the parent layout (heatmap
layout)’s guides
argument.
heatmap_with_four_side_guides +
# the left annotation won't collecte the guide legends
hmanno("l", guides = NULL)
Note: Guide legends in the heatmap layout will only be collected from the annotation stack if the stack layout collects its own guide legends first.
In this example, the guide legends from the left annotation won’t be collected by the heatmap layout since the left annotation doesn’t collect the guide legends.
The free_guides
argument allows you to override the guides
argument for a
single plot.
When position
is NULL
, hmanno()
set the free_guides
for the heatmap
body.
heatmap_with_four_side_guides +
# the heatmap layout only collect guide legends in the top and bottom
hmanno(guides = "tb") +
# we override the layout `guides` argument and collect the right guide
# legend for the heatmap body
hmanno(free_guides = "r")
In this example, the heatmap body right side legend won’t be collected by
default in the layout. We use free_guides
to override the guides
argument
for the heatmap body plot.
In the heamtap layout, each annotation stack is a nested stack plot, we can also
use free_guides
to override the guides
argument in the heatmap layout for
the annotation stack in hmanno()
.
heatmap_with_four_side_guides +
# the heatmap layout only collect guide legends in the top and bottom
hmanno(guides = "tb") +
# we collect the right guide legend for the right annotation in the heatmap layout
hmanno("t", free_guides = "r") +
# we collect the left guide legend for the bottom annotation in the heatmap layout
hmanno("b", free_guides = "l")
All align_*()
functions which add plot include a free_guides
argument to
override the guides
setting for the plot in the annotation stack layout.
ggheatmap(small_mat) +
scale_fill_viridis_c() +
theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
hmanno("t") +
# we won't collect any guide legends from the dendrogram plot
align_dendro(aes(color = branch), k = 3L, free_guides = NULL) +
align_dendro(aes(color = branch), k = 3L, free_guides = NULL) &
scale_color_brewer(
palette = "Dark2",
guide = guide_legend(position = "bottom")
)
By default, ggheatmap()
will 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", unit(20, "mm")) +
align_dendro()
Both free_spaces
and free_labs
operate on individual plots, while layout
values set global parameters for the entire layout. This means that align_*
functions will inherit these values, but you can override them within the
align_*
functions.
If position
is NULL
: These arguments control the behavior of the heatmap
body and set the global values for the heatmap layout. Setting to waiver()
inherits from the parent layout.
If position
is a string: These arguments set the global values for the
annotation stack layout. If set to waiver()
, it inherits from specific
heatmap layout axes:
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", unit(20, "mm")) +
align_dendro()
In the Figure 1 we can also use the free_spaces
to
make a compact heatmap. Here, we remove the right border spaces of the top
annotation stack, and the left border spaces of the bottom annotation stack.
heatmap_with_four_side_guides +
hmanno(guides = "tb") + # we set the `guides` argument here
hmanno("t", free_spaces = "r") + # we remove the right border spaces
hmanno("b", free_spaces = "l") # we remove the left border spaces
align_*()
functions include the free_spaces
argument, which, by default,
inherits from the annotation stack. This allows you to control the alignment
for each plot individually.
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 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")
The same with free_spaces
argument, you can control the free_labs
argument
for the individual plot in align_*()
function.
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.4 ggplot2_3.5.1
#>
#> loaded via a namespace (and not attached):
#> [1] sass_0.4.9 utf8_1.2.4 generics_0.1.3 digest_0.6.36
#> [5] magrittr_2.0.3 evaluate_0.24.0 grid_4.4.0 RColorBrewer_1.1-3
#> [9] bookdown_0.39 iterators_1.0.14 fastmap_1.2.0 foreach_1.5.2
#> [13] jsonlite_1.8.8 ggrastr_1.0.2 seriation_1.5.6 fansi_1.0.6
#> [17] viridisLite_0.4.2 scales_1.3.0 codetools_0.2-20 textshaping_0.4.0
#> [21] jquerylib_0.1.4 cli_3.6.3 registry_0.5-1 rlang_1.1.4
#> [25] munsell_0.5.1 withr_3.0.0 cachem_1.1.0 yaml_2.3.8
#> [29] ggbeeswarm_0.7.2 tools_4.4.0 dplyr_1.1.4 colorspace_2.1-0
#> [33] vctrs_0.6.5 TSP_1.2-4 ca_0.71.1 R6_2.5.1
#> [37] lifecycle_1.0.4 vipor_0.4.7 ragg_1.3.2 pkgconfig_2.0.3
#> [41] beeswarm_0.4.0 pillar_1.9.0 bslib_0.7.0 gtable_0.3.5
#> [45] data.table_1.15.4 glue_1.7.0 systemfonts_1.1.0 xfun_0.45
#> [49] tibble_3.2.1 tidyselect_1.2.1 highr_0.11 knitr_1.47
#> [53] farver_2.1.2 htmltools_0.5.8.1 rmarkdown_2.27 labeling_0.4.3
#> [57] compiler_4.4.0