12  Nested Layouts

Both stack_layout() and quad_layout() support nesting: you can add a stack_layout() inside a quad_layout(), and vice versa.

Code
library(ggalign)
#> Loading required package: ggplot2
#> 
#> Attaching package: 'ggalign'
#> The following object is masked from 'package:ggplot2':
#> 
#>     element_polygon
set.seed(123)
small_mat <- matrix(rnorm(56), nrow = 7)
rownames(small_mat) <- paste0("row", seq_len(nrow(small_mat)))
colnames(small_mat) <- paste0("column", seq_len(ncol(small_mat)))

note: when stack_layout() contains a nested quad_layout(), it cannot be used within the annotation of another quad_layout().

12.1 Add quad_layout() to stack_layout()

The following table summarizes which quad_layout() types are compatible with each stack_layout() variant:

stack_discreteh() stack_discretev() stack_continuoush() stack_continuousv()
quad_continuous()/ggside()
quad_layout(xlim = ...)
quad_layout(ylim = ...)
quad_discrete()/ggheatmap()

As long as the alignment mode is consistent across both stack_layout() and quad_layout(), you can directly add quad_layout().

stack_discreteh(small_mat) +
    ggheatmap()
#> → heatmap built with `geom_tile()`

When ggheatmap()/quad_layout() is added to a stack_layout(), it will also set the active context to itself, which means subsequent addition will be directed to ggheatmap()/quad_layout(). One exception is the ggheatmap()/quad_layout() itself, which cannot be added to another quad_layout(). In this case, they will be added directly to the stack_layout(), ignoring the active context.

stack_discretev(small_mat) +
    ggheatmap() +
    ggheatmap() +
    scale_fill_viridis_c()
#> → heatmap built with `geom_tile()`
#> → heatmap built with `geom_tile()`

The data of ggheatmap()/quad_layout() can inherit from the parent stack_layout(), but the data format must match. Otherwise, you will need to manually provide the data.

quad_continuousv_plot <- quad_layout(small_mat, xlim = NULL) +
    geom_boxplot(aes(value, .discrete_y, fill = .row_names)) +
    scale_fill_brewer(palette = "Dark2")
# Note: `stack_continuousv()` requires a data frame
# but `quad_layout(xlim = NULL)` requires a matrix
stack_continuousv() +
    quad_continuousv_plot +
    quad_continuousv_plot

When adding ggheatmap()/quad_layout() to a vertical stack_layout(), the inherited matrix is automatically transposed. This ensures the columns of the nested plot align with the observations of the stack.

stack_discretev(small_mat) +
    ggheatmap() +
    ggheatmap()
#> → heatmap built with `geom_tile()`
#> → heatmap built with `geom_tile()`

stack_discrete() ensures that all plots aligned along the stack share the same ordering index or groups.

You can customize the layout either inside stack_discrete() or within nested quad_layout(). Use stack_active() to switch the active context back to the stack_layout() from a nested plot.

stack_discretev(small_mat) +
    ggheatmap() +
    ggheatmap() +
    anno_bottom(size = 0.2) +
    align_dendro(aes(color = branch), k = 3)+
    layout_title("dendrogram in ggheatmap()")
#> → heatmap built with `geom_tile()`
#> → heatmap built with `geom_tile()`

stack_discretev(small_mat) +
    ggheatmap() +
    ggheatmap() +
    stack_active() +
    align_dendro(aes(color = branch), k = 3, size = 0.2) +
    scale_y_reverse() +
    layout_title("dendrogram in stack_layout()")
#> → heatmap built with `geom_tile()`
#> → heatmap built with `geom_tile()`

12.2 Control sizes

A numeric or a unit object of length 3 should be provided in stack_discrete()/stack_continuous() when placing a quad_layout(). For vertical stack_layout(), this means quad_layout() with left or right annotations; for horizontal stack_layout(), this means quad_layout() with top or bottom annotations. The first size controls the relative width/height of the left or top annotation, the second controls the relative width/height of the main plot, and the third controls the relative width/height of the right or bottom annotation.

By default the three rows/columns will have equal sizes.

stack_discretev(small_mat) +
    ggheatmap() +
    theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
    anno_left() +
    align_dendro(aes(color = .panel), k = 3L) +
    anno_right() +
    ggalign(data = rowSums) +
    geom_bar(aes(value, fill = .panel), orientation = "y", stat = "identity") +
    ggheatmap() +
    theme(axis.text.x = element_text(angle = -60, hjust = 0))
#> → heatmap built with `geom_tile()`
#> → heatmap built with `geom_tile()`

heat1 <- ggheatmap(t(small_mat)) +
    theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
    anno_left() +
    align_dendro(aes(color = .panel), k = 3L) +
    anno_right() +
    ggalign(data = rowSums) +
    geom_bar(aes(value, fill = .panel), orientation = "y", stat = "identity")

stack_discretev(small_mat, sizes = c(1, 2, 1)) +
    heat1 +
    ggheatmap() +
    theme(axis.text.x = element_text(angle = -60, hjust = 0))
#> → heatmap built with `geom_tile()`
#> → heatmap built with `geom_tile()`

In these cases, specifying width/height within quad_active(), quad_layout(), or ggheatmap() will not affect the size, as the stack layout size settings take precedence:

stack_discretev(small_mat) +
    ggheatmap(width = unit(2, "null")) + # not work
    theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
    anno_left() +
    align_dendro(aes(color = .panel), k = 3L) +
    anno_right() +
    ggalign(data = rowSums) +
    geom_bar(aes(value, fill = .panel), orientation = "y", stat = "identity") +
    ggheatmap(width = unit(2, "null")) + # not work
    theme(axis.text.x = element_text(angle = -60, hjust = 0))
#> → heatmap built with `geom_tile()`
#> → heatmap built with `geom_tile()`

Now that we have covered all the Layout usage in the package, we will build upon these concepts to explore more advanced strategies for integrating elements across multiple plots and annotations within a layout.