Search code examples
rggplot2grob

ggplot2 facet_wrap strip.position different for last column


I would like to have strip.position = "right" for the last column in my facet_wrap. I attempted to implement this SO answer, but did not have any luck.

Data

dta <- structure(list(month = structure(c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 
2L, 3L, 3L, 3L, 3L, 4L, 4L, 4L, 4L, 5L, 5L, 5L, 5L, 6L, 6L, 6L, 
6L, 7L, 7L, 7L, 7L, 8L, 8L, 8L, 8L, 9L, 9L, 9L, 9L), .Label = c("Oct", 
"Nov", "Wave 2", "Jan", "Dec", "Wave 3", "Feb", "Mar", "Wave 4"
), class = "factor"), level = structure(c(1L, 2L, 3L, 4L, 1L, 
2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 
2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L), .Label = c("Student", 
"Student + Staff", "Both", "Either"), class = "factor"), N = c(464L, 
457L, 410L, 511L, 462L, 457L, 410L, 509L, 926L, 914L, 820L, 1020L, 
544L, 533L, 491L, 586L, 459L, 455L, 410L, 504L, 1003L, 988L, 
901L, 1090L, 474L, 463L, 422L, 515L, 430L, 416L, 380L, 466L, 
904L, 879L, 802L, 981L), label = c("464\n(29%)", "457\n(29%)", 
"410\n(26%)", "511\n(32%)", "462\n(29%)", "457\n(29%)", "410\n(26%)", 
"509\n(32%)", "926\n(58%)", "914\n(57%)", "820\n(51%)", "1020\n(64%)", 
"544\n(34%)", "533\n(33%)", "491\n(31%)", "586\n(37%)", "459\n(29%)", 
"455\n(28%)", "410\n(26%)", "504\n(31%)", "1003\n(63%)", "988\n(62%)", 
"901\n(56%)", "1090\n(68%)", "474\n(30%)", "463\n(29%)", "422\n(26%)", 
"515\n(32%)", "430\n(27%)", "416\n(26%)", "380\n(24%)", "466\n(29%)", 
"904\n(56%)", "879\n(55%)", "802\n(50%)", "981\n(61%)")), row.names = c(NA, 
-36L), class = c("tbl_df", "tbl", "data.frame"))

Desired Output enter image description here

Attempt

library(ggplot2)

p1 <- ggplot(dta, aes(x = level, y = N)) + 
  geom_col(aes(fill = level)) + 
  geom_text(aes(label = label), vjust = -0.1) +
  theme(axis.text = element_blank(),
        axis.ticks = element_blank(),
        axis.title = element_blank(),
        legend.position = "left") + 
  scale_y_continuous(expand = expansion(mult = c(0, .4))) + 
  labs(fill = "") + 
  facet_wrap(~ month,
             ncol = 3)

p2 <- ggplot(dta, aes(x = level, y = N)) + 
  geom_col(aes(fill = level)) + 
  geom_text(aes(label = label), vjust = -0.1) +
  theme(axis.text = element_blank(),
        axis.ticks = element_blank(),
        axis.title = element_blank(),
        legend.position = "left") + 
  scale_y_continuous(expand = expansion(mult = c(0, .4))) + 
  labs(fill = "") + 
  facet_wrap(~ month,
             ncol = 3,
             strip.position = "right")

left  <- ggplotGrob(p1)
right <- ggplotGrob(p2)

panels_left  <- panel_cols(left)$l
panels_right <- panel_cols(right)$l

gt <- cbind(left[1:20, 1:14],
            right[, 14:18])

grid::grid.newpage(); grid::grid.draw(gt)

enter image description here

Question

  1. Unless there is a different method out there for accomplishing this, how do I combine the first two columns (3x2) grid from p1 with the last column (3x1) of p2 so they align?
  2. How can I resize the elements from p1 and p2 so that I can adjust their sizes as needed?

Solution

  • Instead of fiddling around with the gtable one option would be to create the first two columns and the third columns as separate plots by filtering the data. One drawback is that you have to fiddle around with plot widths so that the bars have the same width. I used a ratio of 2.5:1 which at least in my eye works fine.

    library(ggplot2)
    library(patchwork)
    
    limits <- c(0, max(dta$N))
    
    p1 <- ggplot(subset(dta, !grepl("^Wave", month)), aes(x = level, y = N)) +
      geom_col(aes(fill = level)) +
      geom_text(aes(label = label), vjust = -0.1) +
      scale_y_continuous(expand = expansion(mult = c(0, .4)), limits = limits) +
      labs(fill = NULL) +
      facet_wrap(~month, ncol = 2) +
      theme(
        axis.text = element_blank(),
        axis.ticks = element_blank(),
        axis.title = element_blank(),
        legend.position = "left",
        plot.margin = margin(5.5, r =  5.5 / 4, 5.5, 5.5)
      )
    
    p2 <- ggplot(subset(dta, grepl("^Wave", month)), aes(x = level, y = N)) +
      geom_col(aes(fill = level)) +
      geom_text(aes(label = label), vjust = -0.1) +
      scale_y_continuous(expand = expansion(mult = c(0, .4)), limits = limits) +
      labs(fill = NULL) +
      facet_wrap(~month, ncol = 1, strip.position = "right") +
      theme(
        axis.text = element_blank(),
        axis.ticks = element_blank(),
        axis.title = element_blank(),
        legend.position = "none",
        plot.margin = margin(5.5, 5.5, 5.5, l = 5.5 / 4)
      )
    
    gridExtra::grid.arrange(p1, p2, nrow = 1, widths = c(2.5, 1))