Search code examples
rggplot2data-visualizationgridextracowplot

ggplot: align multiple faceted plots - facets all different sizes


I am trying to align multiple plots with facets. My problem is somewhat minor but irratating: I can make a plot so that the plot areas are aligned and the facets themselves are aligned, but the facet strips are not all the same width. If the labels of the facets are different lengths, then the facet strips are sized so that the text can fit within the facets. I am unable so far to find a way to make all facet strips the same width when aligning multiple plots.

Here is an example of the type of plots I want to align and my efforts to align them:

library(data.table)
library(ggplot2)
library(foreach)
library(stringr)
library(cowplot)

# example data to show how aligning faceted plots is not quite right
plotvars = c(paste0("plot1_var", 1:7), paste0("plot2_var",1:5), paste0("plot3_var",1:10))
data = 
  foreach(p=plotvars,.combine = "rbind") %do% {
    d = data.table(plot = rep(str_extract(p,pattern = "plot[[:digit:]]"),2),
               plot_variables = rep(p,2),
               fill_categories = c("fill1","fill2"),
               number = sample(1:1000, size = 2))
    d[, facet_variables := ifelse(plot=="plot1", 
                                  rep(sample(paste0("facet",1:3),size=1),2),
                                  ifelse(plot=="plot2",
                                         rep(sample(paste0("facet_title",1:3),size=1),2),
                                         ifelse(plot=="plot3",
                                                rep(sample(paste0("facet_title_longer",1:3),size=1),2),
                                                NA)))]
    d
  }

# function to make stacked barplots with facets + coord_flip
make_plot = function(data, plot_var) {
  ggplot(data[plot==plot_var],
         aes(x=plot_variables,
             y=number,
             fill=fill_categories))+
    geom_bar(stat="identity")+
    coord_flip()+
    facet_grid(facet_variables ~ ., 
               space="free",
               scales="free")+
    theme(strip.text.y = element_text(angle=0),
          legend.position = "none")
}
p1 = make_plot(data=data,plot_var="plot1")
p1

p1

p2 = make_plot(data=data,plot_var="plot2")
p2

enter image description here

p3 = make_plot(data=data,plot_var = "plot3")
p3

enter image description here

# using 'cowplot::plot_grid' gives strange re-sizing of individual bars
cowplot::plot_grid(p1,p2,p3, ncol=1,nrow=3,align = "hv")

enter image description here

# try gtable_rbind version
g1=ggplotGrob(p1)
g2=ggplotGrob(p2)
g3=ggplotGrob(p3)

# this plot keeps the bar widths the correct size, but the facets are still incorrectly different widths.
ggdraw(gridExtra::gtable_rbind(g1,g2,g3))

enter image description here

How can I make the facet strips the same width across plots?


Solution

  • The facet strips are wrapped inside another table, and you need to adjust the widths there. The following seems to work.

    g1 <- ggplotGrob(p1)
    g2 <- ggplotGrob(p2)
    g3 <- ggplotGrob(p3)
    
    # g3 has the widest strips, so get the width from there and copy over
    # to the other plots
    stripwidth <- g3$grobs[[13]]$widths 
    g1$grobs[[13]]$widths <- stripwidth
    g1$grobs[[14]]$widths <- stripwidth
    g1$grobs[[15]]$widths <- stripwidth
    
    g2$grobs[[13]]$widths <- stripwidth
    g2$grobs[[14]]$widths <- stripwidth
    g2$grobs[[15]]$widths <- stripwidth
    
    ggdraw(gridExtra::gtable_rbind(g1,g2,g3))
    

    enter image description here