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 standard
ggplot2 object with default mapping and data. This means you can add
ggplot2 layers or elements just like in any ggplot object.
By default, ggheatmap()
adds a heatmap layer. If the
matrix has more than 20,000 cells (nrow * ncol > 20000
),
it uses geom_raster()
for performance efficiency; for
smaller matrices, geom_tile()
is used. You can explicitly
choose the layer by providing a single string ("raster"
or
"tile"
) in the filling
argument.
If you set filling = NULL
, a blank heatmap will be
drawn, allowing for customized filling geoms. In this way, you must set
fill
mapping manually.
ggheatmap(small_mat, filling = NULL) +
geom_tile(aes(fill = value), color = "black", width = 0.9, height = 0.9)
You can use external packages like ggrastr or ggfx to rasterize the heatmap body. 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")
You can rasterize all plots in the layout directly with
ggrastr::rasterise()
. This method works for both
ggheatmap()
and ggstack()
objects.
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)))
#> → heatmap built with `geom_tile()`
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.
ggheatmap(small_mat, height = 2) +
scale_fill_viridis_c() +
hmanno("t") +
align_dendro()
#> → heatmap built with `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.
ggheatmap(small_mat) +
scale_fill_viridis_c() +
hmanno("t", size = 1) +
align_dendro()
#> → heatmap built with `geom_tile()`
You can also specify an exact size using unit()
:
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.
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")
#> → heatmap built with `geom_tile()`
The plot_action()
function defines the behavior of plots
within a layout. It can be used in the action
argument of
layout functions like hmanno()
or
stack_active()
to set global actions for all plots in the
layout. Additionally, plot_action()
can be applied directly
to specific plots through the action
argument in the
align_*()
functions, or it can be added directly to a
plot.
By default, ggheatmap()
will collect all guide legends
on the side from which they originate.
heatmap_collect_all_guides <- ggheatmap(small_mat, width = 2, height = 2L) +
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", direction = "vertical")
) +
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", direction = "vertical")
) &
theme(plot.margin = margin())
heatmap_collect_all_guides
#> → heatmap built with `geom_tile()`
Use the guides
argument control which side of guide
legends should be gathered. In the following example, we’ll collect the
guide legends only on the top
and bottom
sides:
heatmap_collect_all_guides +
# we only collect guides in the top and bottom side
hmanno(action = plot_action(guides = "tb"))
#> → heatmap built with `geom_tile()`
You can also apply the plot_action()
function directly
to specific plots:
heatmap_collect_all_guides +
# we only collect guides in the top and bottom side
hmanno(action = plot_action(guides = "tb")) +
# we ensure the heatmap body is the active context
hmanno() +
# for the heatmap body, we collect guide in the right side
plot_action(guides = "r")
#> → heatmap built with `geom_tile()`
As previously mentioned, the annotation is represented as a stack_layout() object. The guide legends within the annotation stack are first collected by the stack_layout() itself and then passed to the overall heatmap layout for further integration. By default, it inherits the guides arguments from the heatmap layout. See following example:
heatmap_collect_all_guides +
# we only collect guides in the top and bottom side in the heatmap layout
hmanno(action = plot_action(guides = "tb")) +
# we ensure the active context is in the bottom annotation
# By default, it inherits "guides" argument from the heamtap layout, which
# means it'll collect "guides" in the top and bottom side
hmanno("b") +
# for the dendrogram in the bottom annotation, we collect guide in the left side
plot_action(guides = "l")
#> → heatmap built with `geom_tile()`
Here, the guide legend is collected by the bottom annotation but will
not be collected by the heatmap layout since the heatmap layout only
gathers guides from the top
and bottom
. In
this way, the guide legends of the annotation stack will be put around
the annotation stack layout.
To override this guide collection behavior for the heatmap
annotation, you can use the free_guides
argument of the
hmanno()
function. This differs from the
guides
argument in plot_action()
, which
controls the behavior for the plots in the layout. The
free_guides
argument specifies which guide legends from the
annotation stack layout should be collected by the heatmap layout.
heatmap_collect_all_guides +
# we only collect guides in the top and bottom side in the heatmap layout
hmanno(action = plot_action(guides = "tb")) +
# we also collect guides in the left side for the bottom annotation stack
# layout in the heatmap layout
hmanno("b", free_guides = "l") +
# for the dendrogram in the bottom annotation, we collect guide in the left side
plot_action(guides = "l")
#> → heatmap built with `geom_tile()`
Note: The heatmap layout will only collect guide legends from the annotation stack if the stack layout collects its own guides first.
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()
#> → heatmap built with `geom_tile()`
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"), action = plot_action(free_spaces = "l")) +
align_dendro() +
scale_y_continuous(
expand = expansion(),
labels = ~ paste("very long labels", .x)
) +
hmanno("l", unit(20, "mm")) +
align_dendro()
#> → heatmap built with `geom_tile()`
One useful way to utilize free_spaces
is to position the
guide legends next to the annotations. (Note the guide legend from the
bottom annotation):
heatmap_collect_all_guides +
# we only collect guides in the top and bottom side
hmanno(action = plot_action(guides = "tb")) +
# 1. in the bottom annotation stack layout, we collect the legends in the
# left side
# 2. we remove the spaces of the left border in the annotation
hmanno("b", action = plot_action(guides = "l", free_spaces = "l"))
#> → heatmap built with `geom_tile()`
In the ggheatmap()
layout, the behavior of
free_spaces
and free_labs
arguments is
distinct from other arguments in plot_action()
:
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:
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")
#> → heatmap built with `geom_tile()`
To align axis titles, you can set free_labs = NULL
.
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.
sessionInfo()
#> R version 4.4.1 (2024-06-14)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 24.04.1 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.26.so; LAPACK version 3.12.0
#>
#> locale:
#> [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
#> [3] LC_TIME=en_US.UTF-8 LC_COLLATE=C
#> [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
#> [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
#> [9] LC_ADDRESS=C LC_TELEPHONE=C
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
#>
#> time zone: Etc/UTC
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] bookdown_0.41 ggalign_0.0.4.9000 ggplot2_3.5.1 rmarkdown_2.28
#>
#> loaded via a namespace (and not attached):
#> [1] gtable_0.3.5 jsonlite_1.8.9 compiler_4.4.1 highr_0.11
#> [5] crayon_1.5.3 ggbeeswarm_0.7.2 jquerylib_0.1.4 textshaping_0.4.0
#> [9] systemfonts_1.1.0 scales_1.3.0 yaml_2.3.10 fastmap_1.2.0
#> [13] R6_2.5.1 labeling_0.4.3 knitr_1.48 tibble_3.2.1
#> [17] maketools_1.3.1 munsell_0.5.1 bslib_0.8.0 pillar_1.9.0
#> [21] RColorBrewer_1.1-3 rlang_1.1.4 utf8_1.2.4 cachem_1.1.0
#> [25] xfun_0.48 sass_0.4.9 sys_3.4.3 viridisLite_0.4.2
#> [29] cli_3.6.3 withr_3.0.1 magrittr_2.0.3 digest_0.6.37
#> [33] grid_4.4.1 beeswarm_0.4.0 lifecycle_1.0.4 vipor_0.4.7
#> [37] ggrastr_1.0.2 vctrs_0.6.5 evaluate_1.0.1 glue_1.8.0
#> [41] farver_2.1.2 ragg_1.3.3 buildtools_1.0.0 fansi_1.0.6
#> [45] colorspace_2.1-1 tools_4.4.1 pkgconfig_2.0.3 htmltools_0.5.8.1