skip to Main Content

I would like to create a 3×4 grid of ggplots. To avoid repeating axis titles, I am only labeling the axes of some plots (see highlights in the first figure). However, my choice to label only some axes is causing the plots to be (1) unequally spaced and (2) improperly tagged.

(fit2.yield1+fit2.yield2+fit2.yield3+
 fit2.FallWeed1+fit2.FallWeed2+plot_spacer()+
 fit2.WheatDens1+plot_spacer()+plot_spacer()+
 fit2.SumWeed1+plot_spacer()+plot_spacer())+
  plot_annotation(tag_levels = 'a')+
  plot_layout(guides = "collect",ncol = 3) &
  theme(legend.position = 'right',plot.tag.position =  "topleft")

Image of my flawed output

Image of my flawed output

I would like to ignore the axes titles when combining plots to reduce the white area between plots and properly tag the figure. Here is an example of the desired output that I made with Photoshop.

Image of my preferred output

Image of my preferred output

I have tried to combine my plots using ggarrange() and patchwork(), and both have similar issues. I’ve also modified the plot.margin() of each component with no luck.

2

Answers


  1. This is tricky, especially without a reproducible example. I suspect the only way to do this in patchwork is to turn clipping off and draw the axis titles with custom annotations.

    Let’s make a similar plot to yours with the built in iris dataset:

    library(ggplot2)
    library(patchwork)
    
    p1 <- ggplot(iris, aes(Sepal.Width, Petal.Length, col = Species)) + 
      geom_point() +
      theme(axis.title.y = element_text(angle = 0))
    
    p2 <- p1 + theme(axis.title.x = element_blank())
    p3 <- p1 + theme(axis.title.y = element_blank())
    p4 <- p3 + theme(axis.title.x = element_blank())
    
    p2 + p4 + p3 + 
      p2 + p3 + plot_spacer() +
      p2 + plot_spacer() + plot_spacer() +
      p1 + plot_spacer() + plot_spacer() +
      plot_annotation(tag_levels = 'a') +
      plot_layout(guides = "collect", ncol = 3) &
      theme(legend.position = 'right', plot.tag.position =  "topleft")
    

    enter image description here

    You can see that we have the same issue with the tag positions and row spacing caused by the variable presence of the axis titles.

    The solution is to firstly remove the axis titles from all your plots:

    p1 <- p1 + theme(axis.title.y = element_blank(), axis.title.x = element_blank())
    p2 <- p2 + theme(axis.title.y = element_blank())
    p3 <- p3 + theme(axis.title.x = element_blank())
    

    Now we add custom annotations for the x and y axis, ensuring that we switch off clipping in each panel:

    p1 <- p1 + coord_cartesian(clip = 'off') +
      annotation_custom(grid::textGrob('Sepal.Width', 0.5, -0.3)) +
      annotation_custom(grid::textGrob('Petal.Length', -0.5, 0.5)) 
    p2 <- p2 + coord_cartesian(clip = 'off') +
      annotation_custom(grid::textGrob('Petal.Length', -0.5, 0.5))
    p3 <- p3 + coord_cartesian(clip = 'off') +
      annotation_custom(grid::textGrob('Sepal.Width', 0.5, -0.3))
    

    Finally, we make our patchwork, with an added margin to make room for the new text labels:

    p2 + p4 + p3 + 
      p2 + p3 + plot_spacer() +
      p2 + plot_spacer() + plot_spacer() +
      p1 + plot_spacer() + plot_spacer() +
      plot_annotation(tag_levels = 'a', 
                      theme = theme(plot.margin = margin(20, 10, 50, 80))) +
      plot_layout(guides = "collect", ncol = 3) &
      theme(legend.position = 'right',
            plot.tag.position =  'topleft')
    

    enter image description here

    Note that the tag positions are all consistent in their relationship to the panels, the spacing between all the rows is constant, and the x axis labels reach down below the level of the top of the panel in the next row, as desired.

    Login or Signup to reply.
  2. Here is as possible working approach using a minimal reproducible example based on mtcars with only 3 panels. Basic idea is to get rid of the x axis titles in columns > 1 and instead to add them manually as separate "plots" aka textGrobs. Doing so will allows to remove the "vertical" gaps added by patchwork when aligning your charts. To fix your issue with the tags and y axis title you could add them manually using annotation_custom:

    library(ggplot2)
    library(patchwork)
    
    p1 <- p2 <- p3 <-
      ggplot(mtcars, aes(hp, mpg, color = factor(cyl))) +
      geom_point() +
      labs(y = "Longer Titlenwith mutlt line") +
      labs(x = "Longer Titlenwith mutlt line") +
      theme(
        axis.title.y.left = element_text(angle = 0, vjust = .5)
      )
    
    p1 <- p1 + labs(x = NULL)
    p2 <- p2 + labs(y = NULL, x = NULL)
    
    
    x_axis_title_grob <- grid::textGrob(
      label = "Longer Titlenwith mutlt line",
      vjust = 1, y = unit(1, "npc"), x = .4,
      gp = grid::gpar(
        col = "grey30",
        fontsize = 11,
        lineheight = .9
      )
    )
    
    p_list <- list(p1, p2, p3) |>
      setNames(letters[1:3]) |>
      purrr::imap((x, y) {
        x +
          annotation_custom(grid::textGrob(
            label = y,
            x = unit(0, "npc") - unit(15, "pt"),
            y = unit(1, "npc") + unit(10, "pt"),
            vjust = 0, hjust = 1,
            gp = grid::gpar(fontface = "bold")
          )) +
          coord_cartesian(clip = "off") +
          theme(plot.margin = margin(5.5 + 15, 5.5, 5.5, 5.5, "pt"))
      })
    
    names(p_list) <- c("p1", "p2", "p3")
    
    list(
      p_list$p1, p_list$p2,
      p_list$p3, x_axis_title_grob
    ) |>
      wrap_plots(
        guides = "collect",
        ncol = 2
      ) &
      theme(
        legend.position = "right",
        plot.tag.position = "topleft"
      )
    
    ggsave("patch.png", width = 20, height = 15, units = "cm")
    

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search