Search code examples
rggplot2patchwork

using patchwork to annotate plots that have labels in different positions


I use patchwork to assemble plots made with ggplot2. I also use patchwork's plot_annotation() command to automatically annotate the assembled plots—for example, to automatically place "A", "B", etc. in the upper right-hand corner of each plot. For the most part, this is easy to do. But when the plots have axis labels in different positions, it is hard to get the annotations in the right positions. Here is a minimal illustration of the problem:

library(ggplot2)
library(patchwork)

pLeft <- ggplot(mtcars) + 
  geom_point(aes(mpg, disp)) + 
  scale_y_continuous("Left-hand panel", position = "left")

pRight <- pLeft + 
  scale_y_continuous("Right-hand panel", position = "right")

pLeft + pRight + 
  plot_layout(nrow = 1) & 
  plot_annotation(tag_levels = "A") & 
  theme(plot.tag.position  = c(.935, .96))

That code produces a two-panel figure. The tag for the first panel, "A", is in the correct position. But the tag for the second panel, "B", is not: Plot with tag in wrong positions

There are several ways to fix the problem. For example, I can specify plot.tag.position separately for each panel:

pLeft + theme(plot.tag.position  = c(.935, .96)) +
  pRight + theme(plot.tag.position  = c(.785, .96)) + 
  plot_layout(nrow = 1) & 
  plot_annotation(tag_levels = "A")

Or I can skip plot_annotation() entirely and just use annotation_custom() for each panel. But these are bad solutions. The problem is that they require specifying the tag coordinates separately for each panel. And I will sometimes have many panels in the assembled figure—say, N × 2 panels in a two-column figure. In this case, I would like to specify the coordinates only twice: once for the left-hand column, and once for the right-hand column. Is this possible?

I've looked to the patchwork website and here on Stack Overflow for relevant posts, but I haven't found any solutions.


Solution

  • There are at least four patchwork-based ways to solve the problem: three ways to specify the tag coordinates only once for each column, even if the figure has a large number of panels. (Thomas Lin Pedersen’s excellent patchwork vignettes helped me to find these solutions.) Below, I illustrate the four approaches. They all create this figure: Four-panel figure with tags in correct positions.

    library(ggplot2)
    library(patchwork)
    
    pLeft <- ggplot(mtcars) + 
      geom_point(aes(mpg, disp)) + 
      scale_y_continuous("Left-hand panel", position = "left")
    
    pRight <- pLeft + 
      scale_y_continuous("Right-hand panel", position = "right")
    

    APPROACH 1

    When using patchwork, x / y means "stack ggplot object x on top of ggplot object y."

    ((pLeft / pLeft) &  
      theme(plot.tag.position  = c(.935, .96))) -
      
    ((pRight / pRight) & 
      theme(plot.tag.position  = c(.785, .96))) +
      
    plot_annotation(tag_levels = "A") 
    

    APPROACH 2:
    leftCol  <- (pLeft / pLeft)   & theme(plot.tag.position  = c(.935, .96))
    rightCol <- (pRight / pRight) & theme(plot.tag.position  = c(.785, .96))
    
    wrap_plots(leftCol, rightCol) &  # or "leftCol - rightCol"   
      plot_annotation(tag_levels = "A") 
    

    APPROACH 3
    myPlot <- pLeft + pRight + pLeft + pRight + 
      plot_layout(nrow = 2) & 
      theme(plot.tag.position  = c(.935, .96)) &
      plot_annotation(tag_levels = "A")
    
    for (i in c(2, 4)) myPlot[[i]] <- myPlot[[i]] + theme(plot.tag.position  = c(.785, .96))
    myPlot
    

    APPROACH 4

    This approach relies on Paul Murrell's relatively new ggrid package. Its advantage, relative to the other approaches, is that it permits you to specify only one pair of tag coordinates, no matter how many columns or rows you have in your figure.

    library(gggrid)  # devtools::install_github("pmur002/gggrid")
    
    myPlot <- pLeft + pRight + pLeft + pRight + 
      plot_layout(nrow = 2) 
    
    for (i in 1:(length(myPlot$patches$plots) + 1)) {
      myTag <- textGrob(LETTERS[i], x = .925, y = .95)
      myPlot[[i]] <- myPlot[[i]] + grid_panel(myTag)
    }
    myPlot