Adapt ggsave for patchwork with fixed dimensions
Dear Thomas,
I really like the plot_layout() function you came up with!
I often write something like this:
p1 + p2 + p3 + plot_layout(widths = unit(50, 'mm'), heights = unit(50, 'mm'))
Now, my question is, how to efficiently save this as a pdf with the correct dimensions.
Is there a way to extract the width and height of the patchwork to feed it into ggsave for export? This would be really great!
Thanks a lot and keep up the great work! Broder
I found something that works. Might not be pretty, though.
library(patchwork)
library(ggplot2)
p1 <- ggplot(mtcars) +
geom_point(aes(mpg, disp)) +
ggtitle('Plot 1')
p2 <- ggplot(mtcars) +
geom_boxplot(aes(gear, disp, group = gear)) +
ggtitle('Plot 2')
p3 <- ggplot(mtcars) +
geom_point(aes(hp, wt, colour = mpg)) +
ggtitle('Plot 3')
p4 <- ggplot(mtcars) +
geom_bar(aes(gear)) +
ggtitle('Plot 4')
patchwork <-
p1 + p2 + p3 + p4 +
plot_layout(
ncol = 4,
widths = unit(c(50, 25, 50, 25), "mm"),
heights = unit(50, "mm"),
guides = "collect"
)
gtab <- patchwork:::plot_table(patchwork, 'auto')
overall_width <- grid::convertWidth(sum(gtab$widths) + unit(1, "mm"), unitTo = "mm", valueOnly = TRUE)
overall_height <- grid::convertHeight(sum(gtab$heights) + unit(1, "mm"), unitTo = "mm", valueOnly = TRUE)
ggsave("test_patchwork.pdf", width = overall_width, height = overall_height, unit = "mm", useDingbats = FALSE)
Here, the solution from above wrapped in a function called bro_ggsave().
Ideas to solve this more elegantly are welcome.
devtools::install_github("thomasp85/patchwork")
library(patchwork)
library(tidyverse)
p1 <- ggplot(mtcars) +
geom_point(aes(mpg, disp)) +
ggtitle('Plot 1')
p2 <- ggplot(mtcars) +
geom_boxplot(aes(gear, disp, group = gear)) +
ggtitle('Plot 2')
p3 <- ggplot(mtcars) +
geom_point(aes(hp, wt, colour = mpg)) +
ggtitle('Plot 3')
p4 <- ggplot(mtcars) +
geom_bar(aes(gear)) +
ggtitle('Plot 4')
patchwork <-
p1 + p2 + p3 + p4 +
plot_layout(
ncol = 4,
widths = unit(c(50, 25, 50, 25), "mm"),
heights = unit(50, "mm"),
guides = "collect"
)
bro_get_ggsize <- function(plot) {
gtab <- patchwork:::plot_table(plot, 'auto')
has_fixed_dimensions <-
!gtab$widths %>% map(~is.null(attr(.x, "unit"))) %>% unlist() %>% any() |
!gtab$heights %>% map(~is.null(attr(.x, "unit"))) %>% unlist() %>% any()
if (has_fixed_dimensions) {
width <- grid::convertWidth(sum(gtab$widths) + unit(1, "mm"), unitTo = "mm", valueOnly = TRUE)
height <- grid::convertHeight(sum(gtab$heights) + unit(1, "mm"), unitTo = "mm", valueOnly = TRUE)
c(width = width, height = height)
} else {
c(width = NA, height = NA)
}
}
bro_get_ggsize(p1)
bro_get_ggsize(patchwork)
bro_ggsave <- function(plot = last_plot(), filename, width = NA, height = NA, units = c("in", "cm", "mm"), ...) {
width <- bro_get_ggsize(plot)[["width"]]
height <- bro_get_ggsize(plot)[["height"]]
if(!is.na(width)) message("Saving plot with fixed dimensions: ", round(width, 1), " x ", round(height, 1), " mm")
ggsave(filename = filename, plot = plot, width = width, height = height, units = "mm", ...)
}
bro_ggsave(p1, "bro_test_p1.pdf")
ggsave(plot = p1, filename = "test_p1.pdf")
bro_ggsave(patchwork, "bro_test_patchwork.pdf")
ggsave(plot = patchwork, filename = "test_patchwork.pdf")
It would be great to have an inbuilt function in the patchwork to calculate the resulting image size for these scenarios. I run in the same problem when I was trying to position correctly in an RMd a couple of “patchworked” plots where the heights was set to an equal amount.
Unfortunately the solution kindly provided by the @jbengler is not working for me. Do you have any idea how to solve it?
library(tidyverse)
library(patchwork)
p <- mtcars %>%
ggplot(aes(mpg, hp)) +
geom_smooth()
mtcars_10 <- p + p + p + p + p + p + p + p + p + p +
plot_layout(widths = unit(3, "in"), heights = unit(2, "in"))
bro_get_ggsize <- function(plot) {
gtab <- patchwork:::plot_table(plot, 'auto')
has_fixed_dimensions <-
!gtab$widths %>% map(~is.null(attr(.x, "unit"))) %>% unlist() %>% any() |
!gtab$heights %>% map(~is.null(attr(.x, "unit"))) %>% unlist() %>% any()
if (has_fixed_dimensions) {
width <- grid::convertWidth(sum(gtab$widths) + unit(1, "mm"), unitTo = "mm", valueOnly = TRUE)
height <- grid::convertHeight(sum(gtab$heights) + unit(1, "mm"), unitTo = "mm", valueOnly = TRUE)
c(width = width, height = height)
} else {
c(width = NA, height = NA)
}
}
bro_get_ggsize(mtcars_10)
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
#> width height
#> NA NA
gtab <- patchwork:::plot_table(mtcars_10, 'auto')
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
gtab$widths
#> [1] 1.93302891933029mm 0mm 0mm 0mm
#> [5] 4.93526445966514mm 7.29597602739726mm 0mm 3inches
#> [9] 0mm 0mm 0mm 0mm
#> [13] 0mm 0mm 1.93302891933029mm 1.93302891933029mm
#> [17] 0mm 0mm 0mm 4.93526445966514mm
#> [21] 7.29597602739726mm 0mm 3inches 0mm
#> [25] 0mm 0mm 0mm 0mm
#> [29] 0mm 1.93302891933029mm 1.93302891933029mm 0mm
#> [33] 0mm 0mm 4.93526445966514mm 7.29597602739726mm
#> [37] 0mm 3inches 0mm 0mm
#> [41] 0mm 0mm 0mm 0mm
#> [45] 1.93302891933029mm 1.93302891933029mm 0mm 0mm
#> [49] 0mm 4.93526445966514mm 7.29597602739726mm 0mm
#> [53] 3inches 0mm 0mm 0mm
#> [57] 0mm 0mm 0mm 1.93302891933029mm
gtab$heights
#> [1] 1.93302891933029mm 0mm 0mm 0mm
#> [5] 0mm 0mm 0mm 0mm
#> [9] 0mm 2inches 0mm 4.91472602739726mm
#> [13] 4.93526445966514mm 0mm 0mm 0mm
#> [17] 0mm 1.93302891933029mm 1.93302891933029mm 0mm
#> [21] 0mm 0mm 0mm 0mm
#> [25] 0mm 0mm 0mm 2inches
#> [29] 0mm 4.91472602739726mm 4.93526445966514mm 0mm
#> [33] 0mm 0mm 0mm 1.93302891933029mm
#> [37] 1.93302891933029mm 0mm 0mm 0mm
#> [41] 0mm 0mm 0mm 0mm
#> [45] 0mm 2inches 0mm 4.91472602739726mm
#> [49] 4.93526445966514mm 0mm 0mm 0mm
#> [53] 0mm
It's working when removing the check for the has_fixed_dimensions, just I have to add a bit more extra space to the width (10 mm instead 1 mm, but this can be case by case).
For anyone roaming here in the future, can replace gtab <- patchwork:::plot_table(plot, 'auto') with gtab <- patchwork::patchworkGrob(plot) which also seems to account for overhangs related to plot tags and so it's not necessary to add an extra mm of padding around the patchwork.