Home > Net >  grid.arrange to display multiple facet_wrap plots - how do I make panel widths consistent?
grid.arrange to display multiple facet_wrap plots - how do I make panel widths consistent?

Time:10-11

I have some data that are classified into 25 categories and 6 parent groups, with an uneven number of categories in each parent group (between 3 and 5). For each parent group, I used facet_wrap to create separate panels based on category, then displayed the parent group plots together using grid.arrange. The issue is that, since the number of categories in each parent group varies, the plot widths are automatically resized. Is there a way to manually set the plot widths or create empty panels so that the widths are consistent?

Simple recreation here:

library(dplyr)
library(gridExtra)

x <- c(1, 2, 3, 4, 1, 2)
y <- c(3, 3, 3, 3, 3, 3)
category <- c("category a", "category b", "category c", "category d", "category e", "category f")
group <- c("group one", "group one", "group one", "group one", "group two", "group two")
df <- data.frame(x, y, category, group)

group_one <- df %>% filter(group=="group one")
g1 <- ggplot(group_one, aes(x=x, y=y))  
  geom_point()  
  facet_wrap(~category, nrow=1)  
  labs(title="Group One")

group_two <- df %>% filter(group=="group two")
g2 <- ggplot(group_two, aes(x=x, y=y))  
  geom_point()  
  facet_wrap(~category, nrow=1)  
  labs(title="Group Two")

grid.arrange(g1, g2)

Here's what the above code produces - note uneven panel widths. How do I make the Group Two panels the same width as the Group One panels, with empty space to the right?

If there's an easier alternative way to create this type of plot arrangement with subheadings according to group, I'd love to hear it! Thanks!

CodePudding user response:

One option would be to make use of the patchwork package. In case of your example code you could do

library(patchwork)

g1 / (g2   plot_spacer()) 

As a more generalizable approach you could

  1. Put your plots in a list
  2. Make a vector containing the number of categories per group
  3. Loop over the plot list, add a spacer to each plot and set the widths of each subplot and the spacer using plot_layout such that the panels align.

Note: To make the example more interesting I added a third group.

EDIT To color the points by group you could make use of a named vector of colors, which assigns colors to groups. This way you assign the colors to the color argument of geom_point. In the code below I use the default ggplot2 colors, but you could set the colors as you like.

library(dplyr)
library(ggplot2)
library(forcats)
library(stringr)

x <- c(1, 2, 3, 4, 1, 2, 1:3)
y <- c(3, 3, 3, 3, 3, 3, 1:3)
category <- c("category a", "category b", "category c", "category d", "category e", "category f", "category d", "category e", "category f")
group <- c("group one", "group one", "group one", "group one", "group two", "group two", "group three", "group three", "group three")
df <- data.frame(x, y, category, group)

# Named vector of colors
colors <- scales::hue_pal()(length(unique(df$group)))
names(colors) <- unique(df$group)

p <- df %>% 
  # Set order of group
  mutate(group = forcats::fct_inorder(group)) %>% 
  split(.$group) %>% 
  purrr::imap(function(.data, .title) {
    ggplot(.data, aes(x=x, y=y))  
      geom_point(color = colors[.title])  
      facet_wrap(~category, nrow=1)  
      labs(title = stringr::str_to_title(.title))  
  })

# Make a vector containing number of categories per group
ncat <- df %>% 
  group_by(group) %>% 
  summarise(n = n_distinct(category)) %>% 
  tibble::deframe()
ncat_max <- max(ncat)

library(patchwork)

p <- purrr::imap(p, function(x, y) {
  ncat <- ncat[[y]]
  n_spacer <- ncat_max - ncat
  # Add spacer to each plot and set widths
  x   plot_spacer()   plot_layout(ncol = 2, widths = c(ncat, n_spacer))
})

wrap_plots(p, nrow = length(p))

  • Related