ggplot2 - 在图表外注释

87

我想要在图表上将样本量的值与点关联起来。我可以使用 geom_text 将数字定位到点附近,但这样很凌乱。如果将它们沿着图表的外边缘排列会更加清晰。

例如,我有以下数据:

df=data.frame(y=c("cat1","cat2","cat3"),x=c(12,10,14),n=c(5,15,20))

ggplot(df,aes(x=x,y=y,label=n))+geom_point()+geom_text(size=8,hjust=-0.5)

这个产生了这个图形: 此处输入图片描述

我更喜欢这样的画法: 此处输入图片描述

我知道可以创建第二个绘图并使用grid.arrange(就像这篇文章一样),但是确定文本物件与y轴对齐的间距会很麻烦。是否有更简单的方法?谢谢!


1
这可以通过次要轴完成,我认为它正在开发中。但是,如果您想尝试,请访问此链接https://groups.google.com/forum/?fromgroups=#!topic/ggplot2/_3Pm-JEoCqE - Luciano Selzer
哦,有趣...我在想Hadley是否会实现这个。不过,我尝试加载 devtools 时出现了一些奇怪的错误:调用: if (!version_match) { 错误: 参数长度为零 - jslefche
我只能说DevTools对我有用。如果您无法解决问题,可以尝试发布一个问题。 - Luciano Selzer
我通过从CRAN上的.zip安装ggplot2 0.9.2.1来解决了这个问题。现在,@LucianoSelzer链接中提供的代码无法运行(guide_axis有多个参数)。也许今晚太晚了?我会睡一觉,看看明天能否想出解决方法。 - jslefche
请参见https://dev59.com/I-o6XIcBkEYKwwoYNBdQ#17493256。 - baptiste
5个回答

90

自从ggplot2 3.0.0以来,这现在已经变得很简单了,因为现在可以通过在坐标函数中使用clip = 'off'参数来禁用图表中的裁剪,例如coord_cartesian(clip = 'off')coord_fixed(clip = 'off')。以下是一个示例。

    # Generate data
    df <- data.frame(y=c("cat1","cat2","cat3"),
                     x=c(12,10,14),
                     n=c(5,15,20))

    # Create the plot
    ggplot(df,aes(x=x,y=y,label=n)) +
      geom_point()+
      geom_text(x = 14.25, # Set the position of the text to always be at '14.25'
                hjust = 0,
                size = 8) +
      coord_cartesian(xlim = c(10, 14), # This focuses the x-axis on the range of interest
                      clip = 'off') +   # This keeps the labels from disappearing
      theme(plot.margin = unit(c(1,3,1,1), "lines")) # This widens the right margin

插入图片描述


如何在箱线图的x轴上实现这个? - ktyagi
1
这绝对是最简单的选择,谢谢!@ktyagi也许你现在已经解决了这个问题,但你可以使用完全相同的参数,只需指定ylim而不是xlim - EcologyTom
3
为了完整起见,clip = off也可以在使用coord_flip(以及我认为coord_x)时调用。对我来说,添加coord_cartesian(clip='off')并不是一个解决方案,因为我需要使用coord_flip - Leroy Tyrone
1
这是一个很好的观点,谢谢@LeroyTyrone。我刚刚更新了答案以反映这一点。 - bschneidr
1
值得注意的是,这似乎不能与 coord_polar 一起使用。 - MokeEire
1
有人知道如果x轴是日期,代码应该怎么写吗?我尝试使用as.Date("2020-09-01"),但出现了“Error in as.Date() : 'origin' must be supplied”的错误信息。 - José Rojas

67

不必绘制第二个图,您可以使用annotation_custom在绘图区域内外的任何位置放置grobs。grobs的定位是根据数据坐标系来进行的。假设"5"、"10"、"15"与"cat1"、"cat2"、"cat3"对齐,文本grobs的垂直定位已经处理好了-三个文本grobs的y坐标由三个数据点的y坐标给出。默认情况下,ggplot2将grobs剪裁到绘图区域,但可以覆盖该剪裁。需要扩展相关边距以为grobs腾出空间。以下代码(使用ggplot2 0.9.2)生成类似于您的第二幅图的绘图:

library (ggplot2)
library(grid)

df=data.frame(y=c("cat1","cat2","cat3"),x=c(12,10,14),n=c(5,15,20))

p <- ggplot(df, aes(x,y)) + geom_point() +            # Base plot
     theme(plot.margin = unit(c(1,3,1,1), "lines"))   # Make room for the grob

for (i in 1:length(df$n))  {
p <- p + annotation_custom(
      grob = textGrob(label = df$n[i], hjust = 0, gp = gpar(cex = 1.5)),
      ymin = df$y[i],      # Vertical position of the textGrob
      ymax = df$y[i],
      xmin = 14.3,         # Note: The grobs are positioned outside the plot area
      xmax = 14.3)
 }    

# Code to override clipping
gt <- ggplot_gtable(ggplot_build(p))
gt$layout$clip[gt$layout$name == "panel"] <- "off"
grid.draw(gt)

在此输入图像描述


9
把所有的geom_text图层都放在x=Inf,hjust>=1的位置并关闭裁剪,这样做不是更容易吗? - baptiste
16
@jslefche,你需要注意@baptiste提供的解决方案更加简单。p = p + geom_text(aes(label = n, x = Inf, y = y), hjust = -1)。然后关闭剪切。虽然对齐可能会稍有偏差。 - Sandy Muspratt
4
如何关闭削波? - Thomas Browne
1
@ThomasBrowne 要关闭剪辑,请参见上面代码的最后三行。 - Sandy Muspratt
3
不幸的是,您需要定位文本的x位置随着x轴范围的变化而变化。因此,您需要使用相对于轴范围的x值。这是我的解决方案。我使用 xlim.range <- ggplot_build(plot)$panel$ranges[[1]]$x.range 获取ggplot计算的x轴范围,然后将其用作x位置:x = xlim.range[1] - diff(xlim.range)/10,它有效! - Bakaburg
显示剩余3条评论

5
基于 grid 的更简单的解决方案。
require(grid)

df = data.frame(y = c("cat1", "cat2", "cat3"), x = c(12, 10, 14), n = c(5, 15, 20))

p <- ggplot(df, aes(x, y)) + geom_point() + # Base plot
theme(plot.margin = unit(c(1, 3, 1, 1), "lines"))

p

grid.text("20", x = unit(0.91, "npc"), y = unit(0.80, "npc"))
grid.text("15", x = unit(0.91, "npc"), y = unit(0.56, "npc"))
grid.text("5", x = unit(0.91, "npc"), y = unit(0.31, "npc"))

7
乍一看似乎简单,但字体与ggplot2默认设置不匹配,因此您需要调整这些设置,并且由于使用npc单位而更难定位文本。可能最终变得同样复杂。 - Mooks

2
另一种选择可能是使用ggplot2中的annotate,这与使用geom_text几乎相同:
library(ggplot2)
df=data.frame(y=c("cat1","cat2","cat3"),x=c(12,10,14),n=c(5,15,20))
ggplot(df,aes(x=x,y=y)) + 
  geom_point() + 
  annotate("text", x = max(df$x) + 0.5, y = df$y, label = df$n, size = 8) +
  coord_cartesian(xlim = c(min(df$x), max(df$x)), clip = "off") +
  theme(plot.margin = unit(c(1,3,1,1), "lines"))

创建于2022年8月14日,使用reprex包(v2.0.1)。

1
这个特定的例子可能是一个ggh4x::guide_axis_manual的案例。
# remotes::install_github("teunbrand/ggh4x")
library(ggplot2)

df <- data.frame(y=c("cat1","cat2","cat3"), x=c(12,10,14), n=c(5,15,20))

ggplot(df, aes(x=x, y=y)) +
  geom_point() +
  guides(y.sec=ggh4x::guide_axis_manual(title = element_blank(), breaks = df$y, labels = paste0("n=",df$n)))

2023-08-24创建,使用reprex v2.0.2生成


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接