Make grouped facets the same size in ggplot, accounting for number of facets per group

Viewed 51

I'm working on a problem where I want to create many plots based on some grouping vars. Some of these groups have many values I want to facet, while others only have a few. I want to display all of the facets to be the same size.
In this reprex, the 8 cyl group only has 2 facets while the others have 3. I want the 2 facets in the 8 cyl group to be the same size as facets in the other group, and the space for the 3rd missing facet to just be blank.

library(tidyverse)

for(i in unique(mtcars$cyl)){
  sub_data <- filter(mtcars, cyl == i)
  
  p <- ggplot(sub_data, aes(wt, drat)) +
    geom_point() +
    facet_wrap(~gear) +
    labs(title = paste(i, "cyl"))
  print(p)
}
2 Answers

One option would be ggh4x::facet_manual which allows to add blank panels to a plot. For each of your plots you could specify a design which could be filled with blank panels so that each plot has the same number of panels.

library(ggplot2)
library(dplyr)
library(ggh4x)

# Maximum number of panels
nmax <- max(tapply(mtcars$gear, mtcars$cyl, n_distinct))

for(i in unique(mtcars$cyl)){
  sub_data <- filter(mtcars, cyl == i)
  # Number of panels for plot i
  ngear <- n_distinct(sub_data$gear)
  # Create design
  design = paste(c(LETTERS[seq(ngear)], rep("#", nmax - ngear)), collapse = "")
  print(design)
  p <- ggplot(sub_data, aes(wt, drat)) +
    geom_point() +
    facet_manual(~gear, design = design, trim_blank = FALSE) +
    labs(title = paste(i, "cyl"))
  print(p)
}
#> [1] "ABC"

#> [1] "ABC"

#> [1] "AB#"

I used @stefan 's advice to look into ggh4x, but ended up ditched design and just used facet_wrap2() and specified rows and columns. The data I am using this on was going to have up to 20 facets so using facet_wrap2 made more sense and avoided the need of building a function to robustly declare the design.

Without the loop it looks like this.

library(tidyverse)
library(ggh4x)

p <- ggplot(mpg, aes(displ, hwy, colour = as.factor(cyl))) + geom_point() +
  labs(x = "Engine displacement", y = "Highway miles per gallon") +
  guides(colour = "none")

p + facet_wrap2(vars(class), nrow = 3, ncol = 4, trim_blank = FALSE)

I was confused for a bit but then embraced the trim blank argument.

You can check out the final product for my project here if you want.

Related