Arrangement of plots with absolute dimensions using `theme(panel.widths, panel.heights)` in ggplot2 v4.0.0
Since ggplot2 4.0.0 it is possible to generate plots with absolute dimensions using theme(panel.widths, panel.heights)
When feeding those plots into patchwork, one runs into some unexpected results. Here are some examples:
library(ggplot2)
library(patchwork)
p1 <- ggplot(mtcars) + geom_point(aes(mpg, disp)) +
theme(panel.widths = unit(30, "mm"), panel.heights = unit(30, "mm"))
p2 <- ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear)) +
theme(panel.widths = unit(30, "mm"), panel.heights = unit(30, "mm"))
p3 <- ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear)) +
facet_wrap(vars(gear)) +
theme(panel.widths = unit(30, "mm"), panel.heights = unit(30, "mm"))
p1 + p2 # looks good
p1 / p2 # looks good
p1 | p2 # looks good
(p1 | p2) / (p2 + p1) # vertical spacing dynamic instead of fixed
(p1 | p2) / p1 # bottom plot looses its absolute width
p1 + p3 # unexpected
p1 / p3 # looks good
p1 | p3 # unexpected
(p1 | p3) / (p3 + p1) # unexpected
(p1 | p3) / p1 # unexpected
Sorry for bringing up this potentially unpleasant topic 🫣
Best wishes Broder
In the latest version of ggalign in CRAN, I’ve introduced this behavior. I’ll also explore whether I can integrate all of these features into patchwork without causing major disruptions to existing code. However, this integration is challenging since ggalign uses a different strategy for aligning plots, and it can even handle strip placements in different locations (please see https://github.com/thomasp85/patchwork/issues/446) and collect guide legends in the four sides (top, left, bottom, right) or inner position.
Could you please confirm if the following behavior aligns with your expectations?
library(ggalign)
p1 <- ggplot(mtcars) + geom_point(aes(mpg, disp)) +
theme(panel.widths = unit(30, "mm"), panel.heights = unit(30, "mm"))
p2 <- ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear)) +
theme(panel.widths = unit(30, "mm"), panel.heights = unit(30, "mm"))
p3 <- ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear)) +
facet_wrap(vars(gear)) +
theme(panel.widths = unit(30, "mm"), panel.heights = unit(30, "mm"))
ggalign::align_plots(p1, p2)
ggalign::align_plots(p1, p2, ncol = 1)
ggalign::align_plots(
ggalign::align_plots(p1, p2),
ggalign::align_plots(p2, p1),
ncol = 1
)
ggalign::align_plots(
ggalign::align_plots(p1, p2),
p1,
ncol = 1
)
ggalign::align_plots(p1, p3)
ggalign::align_plots(p1, p3, ncol = 1)
ggalign::align_plots(
ggalign::align_plots(p1, p3),
ggalign::align_plots(p3, p1),
ncol = 1
)
ggalign::align_plots(ggalign::align_plots(p1, p3), p1, ncol = 1)
Hey @Yunuuuu, for most of my use cases your solution works perfectly!
First, I was thinking the lower plot in your last example should be left aligned, instead of centered. However, the more I played around with more complex layouts, "centered" is probably the best default.
Maybe something like this? ggalign treats NULL as an empty space, allowing you to add an empty area so that the top and bottom plots align properly.
ggalign::align_plots(
ggalign::align_plots(p1, p3),
ggalign::align_plots(p1, NULL),
ncol = 1
)
Maybe something like this?
ggaligntreatsNULLas an empty space, allowing you to add an empty area so that the top and bottom plots align properly.
Yes, I also came across this really nice solution.
ggalign provides the free_vp function to control the viewport. You can use the following command to left-align the plot. Note that, by default, the viewport width and height will match the underlying gtable's dimensions
ggalign::align_plots(
ggalign::align_plots(p1, p3),
free_vp(p1, x = 0, just = "left"),
ncol = 1
)