在绘图之外、轴和绘图区域之间绘制一些东西

Draw something outside of the plot, between the axis and plot area

我有一些具有连续 X 轴和分类 Y 轴的通用图,例如:

data(iris)
p1 <-
  ggplot(iris, aes(y=Species, x=Sepal.Length)) +
  geom_violin()

现在我想用一些标签来注释它,这些标签应该紧挨着 Y 轴放置,使用这样一个快速而简单的解决方案:

df.labels <- data.frame(Species=c('setosa','versicolor','virginica'), Labels=c('labA','labB','labC'))

p2 <- p1 + geom_text(data = df.labels, aes(x=4, y=Species, label=Labels))

请注意,我必须明确指出 x=4;对于更一般的情况,我做了这个功能:

add_label <- function(inpPlot, inpData, colLabel){
  plotX1 <- ggplot_build(inpPlot)$layout$panel_params[[1]]$x.range[1]
  plotX2 <- ggplot_build(inpPlot)$layout$panel_params[[1]]$x.range[2]
  plotW <- plotX2 - plotX1
  posX <- plotX1 - 0.05*plotW

  colGrp <- as_label(inpPlot$mapping$y)
    
  inpPlot + 
    coord_cartesian(clip='off') +
    geom_text(data = inpData, aes_string(x='posX', y=colGrp, label=colLabel))  
}

p3 <- add_label(p1, df.labels, 'Labels')

我不喜欢这个解决方案的地方:

  1. 当我调整绘图大小时,计算为“绘图宽度的 5%”的偏移量效果不佳 window(我希望轴和标签之间的绝对距离固定)
  2. 如果我的初始图已应用 coord_cartesian(),则它不起作用,例如:
p1c <- p1 + coord_cartesian(xlim = c(5,NA))
p4 <- add_label(p1c, df.labels, 'Labels')

  1. 标签显示在绘图区域本身,我希望它们在绘图区域之外(但在 Y 轴“内部”,即在轴标签的右侧)。

  2. 我理想中想要的是这样的:

    p5 <- add_label_good(p1c, df.labels, 'Labels')

即它应该在轴和绘图区域之间绘制标签,并且这个 space 应该是固定宽度的。
有什么办法可以做到这一点吗?

您可以考虑 patchwork,有两个对齐的图:

library(patchwork)

plot_labels <-  ggplot(df.labels, aes(x = 0, y = Species, label = Labels)) +
  geom_text(hjust = 0) +
  theme_void() +
  theme(axis.text.y = element_text(),
        axis.title.y = element_text(angle = 90, margin = margin(0,12,0,0, unit = "pt")))

plot_labels +
  (p1 + theme(axis.title.y = element_blank(),
              axis.text.y = element_blank())) +
  plot_layout(widths = c(1,8))

一种选择是使用面条:

p1 + facet_grid(Species~., scales = 'free_y', switch = "y",
                labeller = labeller(Species = function(x) {
                  df.labels$Labels[match(x, df.labels$Species)]})) +
  theme(text = element_text(size = 16),
        strip.text.y.left = element_text(angle = 0, face =  "bold",
                                         margin = margin(10, 10, 10, 10)),
        strip.background = element_blank(),
        panel.spacing = unit(0, "mm"))

另一种选择是使用ggtext::element_markdown()并使用轴文本本身作为标签:

p1 +
  scale_y_discrete(labels = function(x) {
    paste(x, df.labels$Labels[match(x, df.labels$Species)],
          sep = paste0(rep("&nbsp; ", 5), "<b>", collapse = ""))
  }) +
  theme(text = element_text(size = 16),
        axis.text.y = ggtext::element_markdown())

对于绘图最左侧的真正 fixed-width 文本区域,您可以使用 annotation_custom

p1 + 
  annotation_custom(grid::rectGrob(x = unit(10, "mm"), y = 0.5,
                                   width = unit(20, "mm"), height = 1,
                                   gp = grid::gpar(col = NA))) +
  annotation_custom(grid::textGrob(df.labels$Labels,
                    x = unit(10, "mm"),
                    y = c(0.81, 0.5, 0.19)))