I am combining facet plots of tiles. I want each tile to be square, or at least take the same height and width.
So far I have managed to give equal height to each row of tiles using layout_matrix
. I am stuck when trying to fix an equal width to each column of tiles (across the plots).
Some code based on mtcars
to try and illustrate the layout of my plot (actual data way more complicated):
library("tidyverse")
library("gridExtra")
df0 <- mtcars %>%
group_by(cyl) %>%
count()
df1 <- mtcars %>%
rownames_to_column("car") %>%
mutate(man = gsub("([A-Za-z]+).*", "\\1", car))
g <- list()
for(i in 1:nrow(df0)){
g[[i]] <- ggplot(data = df1 %>% filter(cyl == df0$cyl[i]),
mapping = aes(x = "", y = car, fill = qsec)) +
geom_tile() +
facet_grid( man ~ ., scales = "free_y", space = "free") +
labs(x = "", y = "") +
guides(fill = FALSE) +
theme(strip.text.y = element_text(angle=0)) +
coord_fixed()
}
m0 <- cbind(c(rep(1, df0$n[1]), rep(NA, max(df0$n) - df0$n[1])),
c(rep(2, df0$n[2]), rep(NA, max(df0$n) - df0$n[2])),
c(rep(3, df0$n[3]), rep(NA, max(df0$n) - df0$n[3])))
grid.arrange(grobs = g, layout_matrix = m0)
Which produces this plot (minus my MS Paint skills):
Presumably the different lengths of the labels in the strip text and y axis lead to the different widths for the plotting area. Not sure how I can avoid this behavior though? I thought I could create on big facet_grid
but I could not get anywhere near the layout of the plot above.
Turns out this is a rather tricky thing to do. Luckily, cowplot::plot_grid
can already do the alignment that results in equal sizes of the columns. I just took that function and removed the fluff, and decoupled the heights from the grid pattern it normally uses. We end up with a little custom function that does the job (all credits to Claus Wilke):
plot_grid_gjabel <- function(plots, heights) {
grobs <- lapply(plots, function(x) {
if (!is.null(x))
cowplot:::ggplot_to_gtable(x)
else NULL
})
num_plots <- length(plots)
num_widths <- unique(lapply(grobs, function(x) {
length(x$widths)
}))
num_widths[num_widths == 0] <- NULL
max_widths <- do.call(grid::unit.pmax,
lapply(grobs, function(x) { x$widths }))
for (i in 1:num_plots) {
grobs[[i]]$widths <- max_widths
}
width <- 1 / num_plots
height <- heights / max(heights)
x <- cumsum(width[rep(1, num_plots)]) - width
p <- cowplot::ggdraw()
for (i in seq_along(plots)) {
p <- p + cowplot::draw_grob(grid::grobTree(grobs[[i]]), x[i], 1 - height[i],
width, height[i])
}
return(p)
}
We can simply call this like so:
plot_grid_gjabel(g, df0$n)
Resulting in: