rggplot2patchwork

merging legends with ggplot and patchwork


With the code

library(ggplot2)
library(patchwork)

dat.a <- data.frame(year=rep(2021:2024,times=2),group=rep(c("A","B"),each=4),val=runif(8,1,10))
dat.b <- data.frame(year=rep(2021:2024,times=1),group=rep(c("A"),each=4),val=runif(4,1,10))

ggp.a <- ggplot(dat.a,aes(x=year,y=val,fill=group))+
            geom_bar(stat="identity") +
            scale_fill_manual(values=c("A"="red","B"="blue"))

            
ggp.b <- ggplot(dat.b,aes(x=year,y=val,fill=group))+
            geom_bar(stat="identity") +
            scale_fill_manual(values=c("A"="red","B"="blue"))

(ggp.a + ggp.b) +
plot_layout(guides="collect") & 
theme(legend.position="bottom")

I have the plot

enter image description here

How can I get legends merged, not placed side by side? I expected to get only "group ■ A ■ B" and not "group ■ A ■ B group ■ A".


Solution

  • patchwork will only merge legends when they are identical. To achieve that in cases where e.g. one plot shows only some of the categories you can or have to set the limits= accordingly. Additionally, in your case we also have to add show.legend=TRUE to the geoms which for ggplot2 >= 3.5.0 is required to display a legend key for missing categories:

    library(ggplot2)
    library(patchwork)
    
    dat.a <- data.frame(year = rep(2021:2024, times = 2), group = rep(c("A", "B"), each = 4), val = runif(8, 1, 10))
    dat.b <- data.frame(year = rep(2021:2024, times = 1), group = rep(c("A"), each = 4), val = runif(4, 1, 10))
    
    ggp.a <- ggplot(dat.a, aes(x = year, y = val, fill = group)) +
      geom_col(show.legend = TRUE) +
      scale_fill_manual(
        values = c("A" = "red", "B" = "blue"),
        limits = c("A", "B")
      )
    
    
    ggp.b <- ggplot(dat.b, aes(x = year, y = val, fill = group)) +
      geom_col(show.legend = TRUE) +
      scale_fill_manual(
        values = c("A" = "red", "B" = "blue"),
        limits = c("A", "B")
      )
    
    (ggp.a + ggp.b) +
      plot_layout(guides = "collect") &
      theme(legend.position = "bottom")