R 上带有 ggplot2 的样本大小的辅助 x 轴标签

Secondary x-axis labels for sample size with ggplot2 on R

我正在尝试使用 R 中的 ggplot2 复制我在 Excel 中制作的下图:

我能够使用 geom_line() 成功创建线条,使用 geom_text() / 一些手动调整和 y 轴创建标签。在下面找到我的代码:

library(readxl)
library(ggplot2)

somedata <- read_excel("somedata.xlsx")
somedata[,c(3:6)] <- somedata[,c(3:6)] * 100
somedata[,6] <- somedata[,6] + 25 

somegraph <- ggplot(data = somedata, aes(x = date))
somegraph + 
  geom_point(aes(y = eligible_main), shape = 15, size = 4) + 
  geom_line(aes(y = eligible_main)) + 
  geom_line(aes(y = eligible_center), size = 2) + 
  geom_line(aes(y = eligible_upper), linetype = 2) + 
  geom_line(aes(y = eligible_lower), linetype = 2) + 
  scale_y_continuous(limits = c(0, 100), breaks = seq(0, 100, 10), labels = paste0(seq(0, 100, 10), "%")) + 
  labs(title = "Title", x = "Time", y = "Percentage") + theme_classic() + 
  theme(plot.title = element_text(hjust = 0.5), axis.text.x = element_text(angle = 90, hjust = 1))

作为参考,这是我创建的一些假数据,其格式与我正在绘制的数据类似。您可以将其粘贴到 R 和 运行 我在控制台上面的代码中:http://s000.tinyupload.com/?file_id=43876540434394267818

但我发现在 x 轴上以相同方向创建样本大小的二级标签非常困难。在 ggplot2 或其他包中是否有一个简单的解决方案?另外,我可以在我的图表上添加注释线来指出完成后的事情吗?

非常感谢!

我有一个可能有用的解决方案。我无法获取您共享的数据,我创建了自己的虚拟数据集,如下所示:

set.seed(12345)
library(lubridate)

df <- data.frame(
  dates=as.Date('2020-03-01')+days(0:9),
  y_vals=rnorm(10, 50,7),
  n=100
)

一、基本剧情:

library(scales)
library(ggrepel)    
p.basic <- ggplot(df, aes(dates, y_vals)) +
        geom_line() +
        geom_point(size=2.5, shape=15) +
        geom_text_repel(
            aes(label=paste0(round(y_vals, 1), '%')),
            size=3, direction='y', force=7) +
        ylim(0,100) +
        scale_x_date(breaks=date_breaks('day'), labels=date_format('%b %d')) +
        theme_bw()

请注意,我的代码与您的略有不同。文本标签通过 ggrepel 包被推开。我还使用了 scales 中的一些函数来修复和设置日期轴的格式(另请注意 lubridate 是用于在上面的虚拟数据集中创建日期的包)。否则,相当标准的 ggplot 东西在那里。

对于轴外的文本,最好的方法是通过自定义注释,您必须在其中设置 grob。这里的做法如下:

  • 移动轴 "down" 以便为额外文本留出空间。我们通过在轴标题顶部设置边距来做到这一点。

  • 通过 coord_cartesian(clip='off') 关闭剪辑。这是需要的,以便通过允许在绘图区域外绘制内容来查看绘图外的注释。

  • 遍历 df$n 的值,创建一个单独的 annotation_custom object 通过 for 循环添加到图中。

代码如下:

p <- p.basic +
    theme(axis.title.x = element_text(margin=margin(50,0,0,0))) +
    coord_cartesian(clip='off')

for (i in 1:length(df$n)) {
  p <- p + annotation_custom(
    textGrob(
      label=paste0('n=',df$n[i]), rot=90, gp=gpar(fontsize=9)),
      xmin=df$dates[i], xmax=df$dates[i], ymin=-25, ymax=-15
    )
}
p

高级选项带来更多乐趣

还有两件事要添加:注释(例如特定点的标注 + 文本),以及轴标签内容之间图下方的线条。

对于轴下方的线: 您可以通过 scale_...breaks= 参数很容易地将 breaks= 添加到其他轴;然而,对于日期轴,它是……复杂的。这就是为什么我们将使用与上面相同的方法为轴下方的文本添加行。这里的想法是在下面的代码中将轴分成 sub.div 段,这取决于你的 x 轴上有多少个离散值。我可以这样做 in-line 几次...但是首先创建变量很有趣:

sub.div <- 1/length(df$n)

然后,我使用它通过再次使用 for 循环单独注释步骤 sub.div*i 中的线条来创建线条:

for (i in 1:(length(df$n)-1)) {
  p <- p + annotation_custom(
    linesGrob(
      x=unit(c(sub.div*i,sub.div*i), 'npc'),
      y=unit(c(0,-0.2), 'npc')   # length of the line below axis
    )
  )
}

我知道我这里没有两端的线条,但你可能会看到通过修改上面的方法添加它是多么容易。

注释(有箭头,为什么不呢?): 有很多方法可以做注释。 Some are covered here using the annotate() function. As well as here。如果您愿意,可以使用 annotate(),但在本例中,我将使用 geom_label 作为文本标签,使用 geom_curve 来制作一些弯曲的箭头。

您可以通过调用每个注释的两个函数手动传递单个 aes() 值。例如,geom_text(aes(x=as.Date('2020-03-01'), y=55,...,但如果您的数据集中有一些,建议根据数据框本身的信息设置注释。我先在这里做,我们将在其中标记两点:

df$notes <- c('','','','Wow!','','','OMG!!!','','','')

您可以使用 df$notes 的值来指示哪些点被标记,还可以利用同一数据集中 xy 值的映射.

然后你只需要将这两个 geom 添加到你的 plot 中,根据你的喜好进行修改。

p <- p + geom_curve(
    data=df[which(df$notes!=''),],
    mapping=aes(x=dates+0.5, xend=dates, y=y_vals+20, yend=y_vals+2),
    color='red', curvature = 0.5,
    arrow=arrow(length=unit(5,'pt'))
  ) +
  geom_label(
    data=df[which(df$notes!=''),],
    aes(y=y_vals+20, label=notes),
    size=4, color='red', hjust=0
  )

最后一件事:水平线 我之前在你的代码中注意到的最后一件事,但忘了指出,要制作你的水平线,只需使用 geom_hline.这要容易得多。此外,您可以通过两次调用 geom_hline 非常轻松地完成此操作(如果您希望将数据帧传递给函数,甚至只需一次调用即可):

p <- p + geom_hline(yintercept = 50, size=2, color='gray30') +
  geom_hline(yintercept = c(25,75), linetype=2, color='gray30')

请注意,建议在 geom_line 或 [=45] 之前添加这两个 geom_hline 调用 =] 在原来的 p.basic 情节中,所以他们落后于其他一切。