Introduction
The compose_*() family assembles multiple
plotit objects into multi-panel layouts. Unlike
split_*() (which splits one dataset across facets),
compose_*() combines independent plots —
each can have different data, geometries, scales, and coordinate
systems.
Three composers are available:
| Function | Layout |
|---|---|
compose_grid() |
Grid of plots (rows × columns) |
compose_inset() |
Floating overlay on a base plot |
compose_marginal() |
Scatter + marginal distributions |
All return a plotit_composite object that pipes
seamlessly into label_*(), style(), and
export().
compose_grid() — Grid Layout
Arrange any number of plots into a grid. By default, plots stack
vertically (ncol = 1).
Basic Grid
p1 <- iris |>
plotit(encode(x = Sepal.Width, y = Sepal.Length, colour = Species)) |>
mark_point(size = 2, alpha = 0.7) |>
scale_color(range = "viridis")
p2 <- iris |>
plotit(encode(x = Species, y = Sepal.Length, fill = Species)) |>
mark_boxplot() |>
scale_fill(range = "brewer")
compose_grid(p1, p2) |>
label_title("Iris Dataset Overview")
Custom Layout
Control columns, rows, and relative sizes with ncol,
nrow, widths, and heights:
p1 <- mtcars |>
plotit(encode(x = wt, y = mpg, colour = factor(cyl))) |>
mark_point(size = 2) |>
scale_color(range = "brewer") |>
label_title("Weight vs MPG")
p2 <- mtcars |>
plotit(encode(x = factor(cyl))) |>
mark_bar() |>
label_title("Cylinder Count")
p3 <- mtcars |>
plotit(encode(x = hp, y = mpg, colour = factor(cyl))) |>
mark_point(size = 2) |>
scale_color(range = "brewer") |>
label_title("Horsepower vs MPG")
p4 <- mtcars |>
plotit(encode(x = factor(gear), y = mpg)) |>
mark_boxplot() |>
label_title("Gears vs MPG")
# 2×2 grid, first column twice as wide
compose_grid(p1, p2, p3, p4, ncol = 2, widths = c(2, 1)) |>
label_title("mtcars Dashboard") |>
label_caption("Source: Motor Trend, 1974")Shared Axes and Guides
p1 <- iris |>
plotit(encode(x = Sepal.Width, y = Sepal.Length, colour = Species)) |>
mark_point(alpha = 0.6) |>
scale_color(range = "viridis")
p2 <- iris |>
plotit(encode(x = Petal.Width, y = Petal.Length, colour = Species)) |>
mark_point(alpha = 0.6) |>
scale_color(range = "viridis")
# Shared legend, shared axes
compose_grid(p1, p2, ncol = 2, guides = "collect", axes = "collect") |>
label_title("Sepal vs Petal Measurements")
Sub-Figure Tags
Use tag_levels to auto-label sub-figures:
compose_grid(p1, p2, ncol = 2, tag_levels = "A") |>
label_title("Comparison of Iris Measurements")
Supported tag schemes: "A" (uppercase), "a"
(lowercase), "1" (numbers), "i" (roman
numerals), or custom character vectors like
c("(a)", "(b)").
compose_inset() — Floating Overlay
Place a smaller plot as an overlay on a base plot. Position is specified in normalised parent coordinates (0–1).
base <- mtcars |>
plotit(encode(x = wt, y = mpg, colour = factor(cyl))) |>
mark_point(size = 2, alpha = 0.7) |>
scale_color(range = "brewer") |>
label_title("Weight vs MPG")
inset <- mtcars |>
plotit(encode(x = factor(cyl))) |>
mark_bar() |>
label_title("Cylinders")
# Place inset in the top-right corner
compose_inset(base, inset,
left = 0.55, bottom = 0.55, right = 0.95, top = 0.95
)
# Larger inset with panel alignment
compose_inset(base, inset,
left = 0.60, bottom = 0.08, right = 0.98, top = 0.45,
align_to = "panel"
)
compose_marginal() — Scatter + Distributions
Create a scatter plot with marginal histogram or density plots on the top (x-axis) and right (y-axis). Axes are shared so bins align perfectly.
main <- iris |>
plotit(encode(x = Sepal.Width, y = Sepal.Length, colour = Species)) |>
mark_point(alpha = 0.6) |>
scale_color(range = "viridis") |>
label_title("")
top <- iris |>
plotit(encode(x = Sepal.Width, fill = Species)) |>
mark_histogram(bins = 20, alpha = 0.4) |>
scale_fill(range = "viridis")
right <- iris |>
plotit(encode(x = Sepal.Length, fill = Species)) |>
mark_histogram(bins = 20, alpha = 0.4) |>
scale_fill(range = "viridis") |>
project_cartesian(flip = TRUE)
compose_marginal(main, top, right) |>
label_title("Iris Sepal Measurements with Marginal Distributions")
Adjusting Proportions
Control the relative size of marginal panels with widths
and heights:
# Larger marginals
compose_marginal(main, top, right, widths = c(3, 1), heights = c(1, 3)) |>
label_title("Iris Sepal Measurements")
Composing Composites
compose_grid() accepts plotit_composite
objects, so you can nest compositions:
# Create a composite of the scatter + marginals
scatter_with_marginals <- compose_marginal(main, top, right)
# Create a standalone boxplot
box <- iris |>
plotit(encode(x = Species, y = Sepal.Length, fill = Species)) |>
mark_boxplot() |>
scale_fill(range = "brewer") |>
label_title("By Species")
# Combine them
compose_grid(scatter_with_marginals, box, widths = c(3, 1)) |>
label_title("Iris Dataset: Two Views") |>
label_caption("Anderson's Iris Data, 1935")
Exporting Composites
Composite objects are exported the same way as single plots:
dashboard <- compose_grid(p1, p2, p3, p4, ncol = 2, tag_levels = "A") |>
label_title("Dashboard") |>
style(ggplot2::theme_minimal(base_size = 12))
export(dashboard, "dashboard.png", width = 12, height = 8, dpi = 300)What’s Not Supported on Composites
Operations that modify individual plots (mark_*(),
scale_*(), project_*(),
split_*(), label_axis(),
label_legend()) are not supported on composite objects.
Build and style each sub-plot before composing.