Code
library(ggalign)
set.seed(123)
<- matrix(rnorm(56), nrow = 7)
small_mat rownames(small_mat) <- paste0("row", seq_len(nrow(small_mat)))
colnames(small_mat) <- paste0("column", seq_len(ncol(small_mat)))
stack_layout()
arranges plots either horizontally or vertically. Based on whether we want to align the observations, there are two types of stack layouts:
stack_align()
: align the observations along the stack.stack_free()
: don’t align the observations.Several aliases are available for convenience:
stack_alignv
: Aligns the stack vertically (special case of stack_align()
).stack_alignh
: Aligns the stack horizontally (special case of stack_align()
).stack_freev
: A vertical version of stack_free()
.stack_freeh
: A horizontal version of stack_free()
.library(ggalign)
set.seed(123)
<- matrix(rnorm(56), nrow = 7)
small_mat rownames(small_mat) <- paste0("row", seq_len(nrow(small_mat)))
colnames(small_mat) <- paste0("column", seq_len(ncol(small_mat)))
When aligning observations, we typically use a matrix, as it is easy to melt the matrix into a long-formatted data frame. Additionally, matrices are used to fit the observation concept, as they can be transposed (rows to columns, columns to rows), which is necessary for use in functions like quad_layout()
and ggheatmap()
, where observations may be aligned in both directions simultaneously.
stack_free()
, a data frame is required, and the input will be automatically converted using fortify_data_frame()
if needed.stack_align()
, a matrix is required, and the input will be automatically converted using fortify_matrix()
if needed.By default, fortify_data_frame()
will invoke the ggplot2::fortify()
function for conversion. Note, for matrix, it will be converted to a long-formatted data frame which is different from the ggplot2::fortify()
.
stack_align()
/stack_free()
will set up the layout, but no plot will be drawn until you add a plot element:
stack_alignh(small_mat) +
layout_annotation(
theme = theme(plot.background = element_rect(color = "red"))
)# the same for `stack_free()`
In this example, we use layout_annotation()
to insert a plot background in the entire layout, it can be also used to control the theme of title, subtitle, caption (layout_title()
), guides, margins, panel.border, panel.spacing.
When we use stack_align()
, it aligns the observations across multiple plots along the specified direction:
stack_alignh()
: Alignment occurs along the horizontal direction (y-axis).stack_alignv()
: Alignment occurs along the vertical direction (x-axis).The package offers a suite of align_*
functions designed to give you precise control over the observations. These functions enable you to reorder the observations or partition the observations into multiple groups. Instead of detailing each align_*
function individually, we will focus on the general usage and how to combine them with stack_align()
.
Here, we remain take align_dendro()
as an example, it can reorder the observations, split them into groups, and can add a plot for visualization.
When used for stack_alignh()
, the observations are aligned along the y-axis
:
stack_alignh(small_mat) +
align_dendro()
When used for stack_alignv()
, the observations are aligned along the x-axis
:
stack_alignv(small_mat) +
align_dendro()
When align_dendro()
is added to the layout, it performs following actions:
The active plot refers to the plot that subsequent ggplot2 components will target. In this case, the active plot is the dendrogram, and any new layers added will be applied to it. For instance, we can add additional layers to visualize the dendrogram’s structure or data. The default data underlying the ggplot
object of align_dendro()
consists of the dendrogram node data. It is also possible to use the dendrogram’s edge data for customization, which I will introduce in Section 5.5.
stack_alignh(small_mat) +
align_dendro() +
geom_point()
The active
argument controls whether a plot should be set as the active plot. It accepts an active()
object with the use
argument to specify if the plot should be active when added.
stack_alignh(small_mat) +
align_dendro(active = active(use = FALSE)) +
geom_point()
#> Error in `stack_layout_add()`:
#> ! Cannot add `geom_point()` to `stack_align()`
#> ℹ No active plot component
#> ℹ Did you forget to initialize a <ggplot> object with `ggalign()` or
#> `ggfree()`?
Usually, you don’t need to set this manually, as the active context is automatically applied only for functions that add plot areas. You can inspect whether a align_*
function will add a plot by print it:
align_dendro()
#> `align_dendro()` object:
#> plot: yes
#> reorder: yes
#> split: no
You might find it confusing that we mentioned align_dendro()
will split observations into groups, while the print output shows split = "no"
. This happens because we haven’t specified the k
/h
argument in align_dendro()
.
align_dendro(k = 3L)
#> `align_dendro()` object:
#> plot: yes
#> reorder: yes
#> split: yes
You don’t need to explicitly provide data
to align_dendro()
. By default, it inherits data from the layout. However, you can always provide another data source, but note that this package uses the concept of number of observations
(NROW()
). When aligning the observations, you must ensure the number of observations is consistent across all plots.
set.seed(123)
stack_alignh(small_mat) +
align_dendro(data = matrix(rnorm(56), nrow = 8)) +
theme(axis.text.y = element_text())
#> Error in `layout()`:
#> ! `align_dendro(data = matrix(rnorm(56), nrow = 8))` (nobs: 8) is not
#> compatible with the `stack_align()` (nobs: 7)
set.seed(123)
stack_alignh(small_mat) +
align_dendro(data = matrix(rnorm(70), nrow = 7)) +
theme(axis.text.y = element_text())
Alternatively, you can provide a function (or purrr-lambda) that will be applied to the layout’s matrix. For layouts that align observations, a matrix is always required, so the data input must be in matrix form.
set.seed(123)
stack_alignh(small_mat) +
align_dendro(data = ~ .x[sample(nrow(.x)), ]) +
theme(axis.text.y = element_text())
Without adding another plot, it’s difficult to appreciate the benefits. Let’s now explore how to incorporate a plot.
There are two primary functions for adding plots:
align_gg()
/ggalign()
: Create a ggplot object and align with the layout.free_gg()
/ggfree()
: Create a ggplot object without aligning.Both functions initialize a ggplot
object and, by default, set the active
plot when added to the layout.
For stack_align()
, plots can be added regardless of whether they need to align observations.
stack_alignh(small_mat) +
align_dendro() +
ggalign(data = rowSums) +
geom_bar(aes(value, .discrete_y), stat = "identity") +
theme(axis.text.y = element_text())
You can build the plot separately and then add it to the layout:
<- ggalign(data = rowSums) +
my_bar geom_bar(aes(value, .discrete_y), stat = "identity") +
theme(axis.text.y = element_text())
stack_alignh(small_mat) +
align_dendro() +
my_bar
The active
argument can also control the place of the plot area to be added. It accepts an active()
object with the order
argument to specify the order of the plot area.
stack_alignh(small_mat) +
align_dendro() +
ggalign(data = rowSums, active = active(order = 1)) +
geom_bar(aes(value, .discrete_y), stat = "identity") +
theme(axis.text.y = element_text())
You can also stack plots vertically using stack_alignv()
:
stack_alignv(small_mat) +
align_dendro() +
ggalign(data = rowSums) +
geom_bar(aes(.discrete_x, value), stat = "identity") +
theme(axis.text.y = element_text())
stack_align()
can also add plot without aligning observations. free_gg()
focuses on layout integration without enforcing strict axis alignment. ggfree()
is an alias for free_gg
.
stack_alignv() +
ggfree(mpg, aes(displ, hwy, colour = class)) +
geom_point(size = 2) +
ggfree(mpg, aes(displ, hwy, colour = class)) +
geom_point(size = 2) &
scale_color_brewer(palette = "Dark2") &
theme_bw()
The &
operator applies the added element to all plots in the layout, similar to its functionality in the patchwork
package.
For stack_free()
, only free plots (ggfree()
) can be added. This layout arranges plots in one row or column without enforcing axis alignment:
stack_freev(mpg) +
ggfree(mapping = aes(displ, hwy, colour = class)) +
geom_point(size = 2) +
ggfree(mapping = aes(displ, hwy, colour = class)) +
geom_point(size = 2) &
scale_color_brewer(palette = "Dark2") &
theme_bw()
By default, ggfree()
will also inherit data from the layout and call fortify_data_frame()
to convert the data to a data frame. So, note that if the layout data is a matrix, it will be converted into a long-formatted data frame.
Both ggalign()
and ggfree()
functions have a size
argument to control the relative width
(for horizontal stack layout) or height
(for vertical stack layout) of the plot’s panel area.
stack_freev(mpg) +
ggfree(mapping = aes(displ, hwy, colour = class), size = 2) +
geom_point(size = 2) +
ggfree(mapping = aes(displ, hwy, colour = class), size = 1) +
geom_point(size = 2) &
scale_color_brewer(palette = "Dark2") &
theme_bw()
Alternatively, you can define an absolute size by using a unit()
object:
stack_freev(mpg) +
ggfree(mapping = aes(displ, hwy, colour = class), size = unit(1, "cm")) +
geom_point(size = 2) +
ggfree(mapping = aes(displ, hwy, colour = class)) +
geom_point(size = 2) &
scale_color_brewer(palette = "Dark2") &
theme_bw()
As mentioned earlier, the active plot refers to the plot that subsequent ggplot2 components will target. The package provide two functions to work with active plot for stack_layout
.
stack_switch()
: switch the active contextstack_active()
: An alias for stack_switch()
, which sets what = NULL
The stack_switch()
function accepts the what
argument, which can either be the index of the plot added (based on its adding order) or the plot name specified via the active()
object using the name
argument.
Note that the what
argument must be explicitly named, as it is placed second in the function signature. This is because, in most cases, we don’t need to switch the active plot manually—adjusting the order of plot additions typically suffices.
stack_alignh(small_mat) +
align_dendro() +
ggalign(data = rowSums) +
geom_bar(aes(value, .discrete_y), stat = "identity") +
stack_switch(what = 1) +
geom_point() +
theme(axis.text.y = element_text()) +
layout_title(title = "switch by integer")
stack_alignh(small_mat) +
align_dendro(active = active(name = "tree")) +
ggalign(data = rowSums) +
geom_bar(aes(value, .discrete_y), stat = "identity") +
stack_switch(what = "tree") +
geom_point() +
theme(axis.text.y = element_text()) +
layout_title(title = "switch by string")
In the example, we use layout_title()
to insert a title for the entire layout. Alternatively, you can add a title to a single plot with ggtitle()
.
By setting what = NULL
(or alias stack_active()
), we remove the active plot. This is particularly useful when the active plot is a nested Layout
object, as any additions would otherwise be directed to that nested Layout
. By removing the active plot, you can continue adding components directly to the StackLayout
. We’ll introduce the nested layout of StackLayout
in Chapter 7.
In the next chapter, we will dive into the HeatmapLayout
, which can take the StackLayout
as input. Heatmap layouts offer additional features for aligning observations in both directions. Let’s move ahead and explore how heatmaps can be seamlessly integrated into your layout workflows.