rggplot2grobpatchwork

Control padding of grobs added to patchwork


This is a follow up problem to this question. The OP asked for a way to arrange parts of a plot in specific distances. I think teunbrand gave a very good answer.

My own suggestion (extract the legend with cowplot, and stitch them to a plot in desired proportions) is not fully satisfactory, because it worked only "by chance" in the given example - the legend labels were long enough to center the legend grob into the viewport for the third plot.

Having shorter labels reveals the problem - when adding a grob, patchwork centres this grob, basically padding equally to all sides.

My question is, do you know of a way to control this padding behaviour?

Cowplot (or any other ggplot combining package for that sake) also very welcome.

library(tidyverse)
library(patchwork)
data <- midwest %>% 
  head(5) %>% 
  select(2,23:25) %>%
  pivot_longer(cols=2:4,names_to="Variable", values_to="Percent") %>% 
  mutate(Variable=factor(Variable, 
                         levels=c("percbelowpoverty","percchildbelowpovert","percadultpoverty"),
                         labels = paste0("perc", 1:3)))

p1 <- 
  ggplot(data=data, mapping=aes(x=county, y=Percent, fill=Variable)) +
  geom_col() + 
  scale_fill_manual(values = c("#CF232B","#942192","#000000")) +
  theme(legend.background = element_rect(fill = "grey50"))

p_legend <- cowplot::get_legend(p1)

p_main <- p1 <- 
  ggplot(data=data, mapping=aes(x=county, y=Percent, fill=Variable)) +
  geom_col(show.legend = FALSE) + 
  scale_fill_manual(values = c("#CF232B","#942192","#000000"))

p_main + plot_spacer() + p_legend + 
  plot_layout(widths = c(12.5, 1.5, 4)) &
  theme(plot.margin = margin(),
        plot.background = element_rect(colour = "black"))

Not so desired result - the legend grob (with grey background) should be aligned to the left plot border (black line)

Created on 2021-04-09 by the reprex package (v1.0.0)


Solution

  • Update

    As of ggplot2 >= 3.5.0 the desired result can be achieved more easily by setting the legend justification to "left". Also note that due to the change in ggplot2s guide system in version >= 3.5.0 I switched to cowplot::get_plot_component to extract the legend:

    library(patchwork)
    library(ggplot2)
    
    packageVersion("ggplot2")
    #> [1] '3.5.1'
    
    p1 <-
      ggplot(data = data, mapping = aes(x = county, y = Percent, fill = Variable)) +
      geom_col() +
      scale_fill_manual(values = c("#CF232B", "#942192", "#000000")) +
      theme(
        legend.background = element_rect(fill = "grey50"),
        legend.justification.right = "left"
      )
    
    p_legend <- cowplot::get_plot_component(p1, "guide-box-right")
    
    p_main <- p1 <-
      ggplot(data = data, mapping = aes(x = county, y = Percent, fill = Variable)) +
      geom_col(show.legend = FALSE) +
      scale_fill_manual(values = c("#CF232B", "#942192", "#000000"))
    
    p_main + plot_spacer() + p_legend +
      plot_layout(widths = c(12.5, 1.5, 4)) &
      theme(
        plot.margin = margin(),
        plot.background = element_rect(colour = "black")
      )
    

    Created on 2025-03-16 with reprex v2.1.1

    Original Answer

    As far as I get it the issue is not on patchworks side. Having a look at the layout of the legend's gtable we see that it is made up of 5 rows and 5 columns and that the legend is to be placed in the cell in the center:

    p_legend <- cowplot::get_legend(p1)
    p_legend
    #> TableGrob (5 x 5) "guide-box": 2 grobs
    #>                                     z     cells                  name
    #> 99_a788e923bf245af3853cee162f5f8bc9 1 (3-3,3-3)                guides
    #>                                     0 (2-4,2-4) legend.box.background
    #>                                               grob
    #> 99_a788e923bf245af3853cee162f5f8bc9 gtable[layout]
    #>                                     zeroGrob[NULL]
    gtable::gtable_show_layout(p_legend)
    

    Hence, when adding the legend patchwork centers is as demanded by the gtable layout.

    One option to control the positioning or the padding of the legend would be to squash the first column via cowplot::gtable_squash_cols and if desired add some padding by adding a new column with the desired amount of padding via gtable::gtable_add_cols:

    # Squash first column
    p_legend <- cowplot::gtable_squash_cols(p_legend, 1)
    # Add some padding by adding a new col
    p_legend <- gtable::gtable_add_cols(p_legend, unit(.1, "cm"), pos = 1)
    
    p_main <- p1 <- 
      ggplot(data=data, mapping=aes(x=county, y=Percent, fill=Variable)) +
      geom_col(show.legend = FALSE) + 
      scale_fill_manual(values = c("#CF232B","#942192","#000000"))
    
    p_main + plot_spacer() + p_legend + 
      plot_layout(widths = c(12.5, 1.5, 4)) &
      theme(plot.margin = margin(),
            plot.background = element_rect(colour = "black"))