Search code examples
rggplot2patchwork

R ggplot2 patchwork common axis labels


Based on the code and data below, is it possible to have common legend labels without having to remove xlab and ylab from the ggplot codes using patchwork?

The reason why I ask this is because I have lots of ggplots and so I don't find it ideal to remove xlab and ylab from each of the ggplots and then use the method in the code. I know I can use ggarrange but ggpubr is much slower than patchwork.

Sample data and code:

library(tidyverse)
library(patchwork)
library(gridextra)

gg1 = ggplot(mtcars) +
  aes(x = cyl, y = disp) +
  geom_point() +
  xlab("Disp") +
  ylab("Hp // Cyl") +
  theme(axis.title = element_blank())

gg2 = gg1 %+% aes(x = hp) +
  xlab("Disp") +
  ylab("Hp // Cyl")

# This method still keeps the individual axis labels.
p = gg1 + gg2
gt = patchwork::patchworkGrob(p)
gridExtra::grid.arrange(gt, left = "Disp", bottom = "Hp // Cyl")

Solution

  • Update

    With patchwork >= 1.2.0 it is now possible to collect the axis titles using the new axis_title= parameter of plot_layout(), i.e. similar to guides="collect" one can now set axis_title="collect" to add a shared axis titles.

    library(ggplot2)
    library(patchwork)
    
    packageVersion("patchwork")
    #> [1] '1.2.0'
    
    gg1 <- ggplot(mtcars) +
      aes(x = cyl, y = disp) +
      geom_point() +
      xlab("Disp") +
      ylab("Hp // Cyl")
    
    gg2 <- gg1 %+% aes(x = hp) +
      xlab("Disp") +
      ylab("Hp // Cyl")
    
    gg1 + gg2 +
      plot_layout(axis_titles = "collect")
    

    Original answer

    One possible option to have a common axis title without having to remove xlab and ylab from the ggplot code would be to remove the axis labels via & labs(...) when creating the patch and adding a common axis title as a separate plot where I made use of cowplot::get_plot_component to create the axis title plot:

    library(ggplot2)
    library(patchwork)
    library(cowplot)
    
    
    gg1 <- ggplot(mtcars) +
      aes(x = cyl, y = disp) +
      geom_point() +
      xlab("Disp") +
      ylab("Hp // Cyl") +
      theme(axis.title = element_blank())
    
    gg2 <- gg1 %+% aes(x = hp) +
      xlab("Disp") +
      ylab("Hp // Cyl")
    
    gg_axis <- cowplot::get_plot_component(ggplot() +
      labs(x = "Hp // Cyl"), "xlab-b")
    
    (gg1 + gg2 & labs(x = NULL, y = NULL)) / gg_axis + plot_layout(heights = c(40, 1))
    

    UPDATE To add a y-axis it's basically the same. To get the left y axis title we have to use ylab-l. Additionally, we have to add a spacer to the patch. IMHO the best approach in this case would be to put all components in a list and use the design argument of plot_layout to place them in the patch.

    p <- ggplot() + labs(x = "Hp // Cyl", y = "Disp")
    x_axis <- cowplot::get_plot_component(p, "xlab-b")
    y_axis <- cowplot::get_plot_component(p, "ylab-l")
    
    design = "
    DAB
    #CC
    "
    
    list(
      gg1 + labs(x = NULL, y = NULL), # A
      gg2 + labs(x = NULL, y = NULL), # B
      x_axis,# C
      y_axis # D
    ) |> 
      wrap_plots() + 
      plot_layout(heights = c(40, 1), widths = c(1, 50, 50), design = design)