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 discrete or continuous variables, there are two types of stack layouts:
stack_discrete()
: Align discrete variable along the stack.stack_continuous()
: Align continuous variable along the stack.stack_layout()
integrates the functionalities of stack_discrete()
and stack_continuous()
into a single interface. The first argument for these three functions is direction
which should be a single string indicating the direction of the stack layout, either "h"
(horizontal
) or "v"
(vertical
).
Several aliases are available for convenience:
stack_discretev
: A special case of stack_discrete
that sets direction = "v"
.stack_discreteh
: A special case of stack_discrete
that sets direction = "h"
.stack_continuousv()
: A special case of stack_continuous
that sets direction = "v"
.stack_continuoush()
: A special case of stack_continuous
that sets direction = "h"
.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)))
As discussed in Section 1.3, when aligning discrete variables, we typically use a matrix. For continuous axes, we can still use the long-formatted data frame, which is the same as in ggplot2.
stack_continuous()
, a data frame is required, and the input will be automatically converted using fortify_data_frame()
if needed.stack_discrete()
, 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_discrete()
/stack_continuous()
will set up the layout, but no plot will be drawn until you add a plot element:
stack_discretev(small_mat) +
layout_annotation(
theme = theme(plot.background = element_rect(color = "red"))
)# the same for `stack_continuous()`
In this example, we use layout_annotation()
to insert a plot background in the entire layout, it can also be used to control the theme of title, subtitle, caption (layout_title()
), guides, margins, panel.border, panel.spacing.
When aligning discrete variables, the package offers a suite of align_*
functions designed to give you precise control over the ordering and groups. Instead of detailing each align_*
function individually, we will focus on the general usage and how to combine them with stack_discrete()
.
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_discreteh()
, the observations are aligned along the y-axis
:
stack_discreteh(small_mat) +
align_dendro()
When used for stack_discretev()
, the observations are aligned along the x-axis
:
stack_discretev(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.4.
stack_discreteh(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_discreteh(small_mat) +
align_dendro(active = active(use = FALSE)) +
geom_point()
#> Error in `chain_layout_add()`:
#> ! Cannot add `geom_point()` to the horizontal `stack_discrete()`
#> ℹ 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()
#> <Class: AlignDendro AlignHclust Align AlignProto>
#> 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)
#> <Class: AlignDendro AlignHclust Align AlignProto>
#> 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_discreteh(small_mat) +
align_dendro(data = matrix(rnorm(56), nrow = 8)) +
theme(axis.text.y = element_text())
#> Error in interact_layout(..., self = self): object 'layout_name' not found
set.seed(123)
stack_discreteh(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_discreteh(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:
ggalign()
: Initialize a ggplot object and align the axes.ggfree()
: Initialize a ggplot object without aligning the axes.Both functions initialize a ggplot
object and, by default, set the active
plot when added to the layout.
stack_discreteh(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_discreteh(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_discreteh(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_discretev()
:
stack_discretev(small_mat) +
align_dendro() +
ggalign(data = rowSums) +
geom_bar(aes(.discrete_x, value), stat = "identity") +
theme(axis.text.y = element_text())
ggfree()
focuses on layout integration without enforcing strict axis alignment.
stack_discretev() +
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_continuous()
, only free plots (ggfree()
) can be added. This layout arranges plots in one row or column without enforcing axis alignment:
stack_continuousv(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_continuousv(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_continuousv(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 to the StackLayout
. We’ll introduce the nested layout of StackLayout
in Chapter 8.
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.