使用拼凑来注释在不同位置具有标签的图

using patchwork to annotate plots that have labels in different positions

我使用拼凑 assemble 用 ggplot2 绘制的图。我还使用 patchwork 的 plot_annotation() 命令自动注释 assembled 图——例如,在每个图的右上角自动放置“A”、“B”等。在大多数情况下,这很容易做到。但是当绘图在不同位置有轴标签时,很难在正确的位置获得注释。这是问题的最小说明:

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))

该代码生成一个双面板图形。第一个面板的标签“A”位于正确的位置。但是第二个面板的标签“B”不是:

有多种方法可以解决此问题。例如,我可以为每个面板单独指定 plot.tag.position

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")

或者我可以完全跳过 plot_annotation(),只对每个面板使用 annotation_custom()。但这些都是糟糕的解决方案。问题是他们需要为每个面板分别指定标签坐标。有时我会在 assembled 图中有很多面板——比如,在两列图中有 N × 2 个面板。在这种情况下,我只想指定两次坐标:一次用于左侧列,一次用于右侧列。这可能吗?

我查看了 patchwork 网站和 Stack Overflow 上的相关帖子,但我没有找到任何解决方案。

解决方法是创建 2 个主题并将左侧主题添加到左侧的绘图,将右侧主题添加到右侧的绘图。

library(ggplot2)
library(patchwork)

theme_left <- function(...) {
  theme_gray(...) %+replace% 
    theme(
      plot.tag.position = c(.935, .96)
    )
}

theme_right <- function(...) {
  theme_gray(...) %+replace% 
    theme(
      plot.tag.position = c(.785, .96)
    )
}

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


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

pLeft + pRight + 
  plot_layout(nrow = 1) & 
  plot_annotation(tag_levels = "A") 

至少有四种基于拼凑的方法来解决这个问题:三种方法为每一列只指定一次标签坐标,即使图中有大量的面板。 (Thomas Lin Pedersen 的出色 patchwork vignettes 帮助我找到了这些解决方案。)下面,我将说明这四种方法。他们都创造了这个数字:

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")

方法 1

使用 patchwork 时,x / y 表示“将 ggplot 对象 x 堆叠在 ggplot 对象 y 之上。”

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

方法 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") 

方法 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

方法 4

此方法依赖于 Paul Murrell 相对较新的 ggrid 包。相对于其他方法,它的优势在于它允许您仅指定一对标记坐标,无论您的图中有多少列或行。

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