R:将 geom_text() 放置在相对于绘图边界而不是绘图上的固定位置

R: place geom_text() relative to plot borders rather than fixed position on the plot

我正在使用 ggplot2 在 R 中创建许多图,并想要一种方法来标准化截止线的实现。我有大约 10 年时间段内四个城市的许多不同措施的数据。我将它们绘制为折线图,每个城市在给定图表中的颜色不同。我将为每个不同的度量(大约 20 个)创建一个图。

在每个图表上,我需要放置两条截止线(旁边有一个词)代表某些政策的实施,以便阅读图表的人可以轻松识别实施前后的性能差异。以下是我目前正在使用的大致代码。

gg_plot1<- ggplot(data=ggdata, aes(x=Year, y=measure1, group=Area, color=Area)) +
  geom_vline(xintercept=2011, color="#EE0000") +
  geom_text(aes(x=2011, label="City1\n", y=0.855), color="#EE0000", angle=90, hjust=0, family="serif") +
  geom_vline(xintercept=2007, color="#000099") +
  geom_text(aes(x=2007, label="City2", y=0.855), color="#000099", angle=0, hjust=1, family="serif") +
  geom_line(size=.75) +
  geom_point(size=1.5) +
  scale_y_continuous(breaks=round(seq(min(ggdata$measure1, na.rm=T), max(ggdata$measure1, na.rm=T), by=0.01), 2)) +
  scale_x_continuous(breaks=min(ggdata$Year):max(ggdata$Year)) +
  scale_color_manual(values=c("#EE0000", "#00DDFF", "#009900", "#000099")) +
  theme(axis.text.x = element_text(angle=90, vjust=1),
        panel.background = element_rect(fill="white", color="white"),
        panel.grid.major = element_line(color="grey95"),
        text = element_text(size=11, family="serif"))

此实现的问题在于它依赖于将两个 geom_text() 放在特定图形的特定位置。这些不同的措施都有不同的范围,所以为了做到这一点,我需要逐个绘图并找到放置它们的位置。我更愿意做的是将每个绘图的范围强制降低 X%,并将 geom_text() 对齐到范围的底部。线条不需要调整(每个图中的同一年),只需调整文本的位置。我在这里发现了一些类似的问题,但 none 与将某些东西放在不同范围的不同图形上的相同位置的特定问题有关。

有什么方法可以满足我的要求吗?如果我不得不猜测,它就像使用相对定位而不是绝对定位,但我一直无法在 ggplot 中找到这样做的方法。作为记录,我知道两个 geom_text() 的方向不同。我这样做是为了比较我们更喜欢哪个,但留给大家了。我们最终将使用文本旋转 90 度的那个。此外,其中一些将被拼接在一起,因此可能会增加一层难度。还没到那个地步。

边栏:另一种可视化方式是在截止年份将线从实线更改为虚线。这可能吗?我不确定客户是否想要那样,但如果有人能指出我在哪里可以了解如何做到这一点,我很乐意将其作为一种选择提出。

编辑添加:

显示 运行 具有不同 y 范围

时发生的情况的示例数据
ggdata <- data.frame(Area=rep(c("City1", "City2", "City3", "City4"), times=7),
                     Year=c(rep(2006,4), rep(2007,4), rep(2008,4), rep(2009,4), rep(2010,4), rep(2011,4), rep(2012,4)),
                     measure1=rnorm(28,10,2),
                     measure2=rnorm(28,50,10))

示例图,其中 geom_text()s 位于正确的位置,但这是使用上面的代码在图中的固定位置完成的。当我使用具有不同 y 范围的不同度量复制代码时,它最终会拉伸绘图 window。

您可以使用数据的 y 范围定位到文本标签。我已经在下面的示例中明确设置了 y 限制,但这并不是绝对必要的,除非您想更改它们的默认值。您还可以使用数据的 x 范围调整文本标签的 x 位置。无论数据的 y 范围如何,下面的代码都会将标签放置在图的底部。

我也从 geom_text 切换到 annotategeom_text 多次叠加文本标签,数据中的每一行一次。 annotate 绘制标签一次。

ypos = min(ggdata$measure1) + 0.005*diff(range(ggdata$measure1))
xv = 0.02
xh = 0.01
xadj = diff(range(ggdata$Year))

ggplot(data=ggdata, aes(x=Year, y=measure1, group=Area, color=Area)) +
  geom_vline(xintercept=2011, color="#EE0000") +
  geom_vline(xintercept=2007, color="#000099") +
  geom_line(size=.75) +
  geom_point(size=1.5) +
  annotate(geom="text", x=2011 - xv*xadj, label="City1", y=ypos, color="#EE0000", angle=90, hjust=0, family="serif") +
  annotate(geom="text", x=2007 - xh*xadj, label="City2", y=ypos, color="#000099", angle=0, hjust=1, family="serif") +
  scale_y_continuous(limits=range(ggdata$measure1),
                     breaks=round(seq(min(ggdata$measure1, na.rm=T), max(ggdata$measure1, na.rm=T), by=1), 0)) +
  scale_x_continuous(breaks=min(ggdata$Year):max(ggdata$Year)) +
  scale_color_manual(values=c("#EE0000", "#00DDFF", "#009900", "#000099")) +
  theme(axis.text.x = element_text(angle=90, vjust=1),
        panel.background = element_rect(fill="white", color="white"),
        panel.grid.major = element_line(color="grey95"),
        text = element_text(size=11, family="serif"))

更新:为了回应您的评论,您可以按照以下方法为数据框中的每个 "measure" 列创建单独的图。

首先,我们使用三个度量列创建可重现的数据:

library(ggplot2)
library(gridExtra)
library(scales)

set.seed(4)
ggdata <- data.frame(Year=rep(2006:2012,each=4),
                     Area=rep(paste0("City",1:4), 7),
                     measure1=rnorm(28,10,2),
                     measure2=rnorm(28,50,10),
                     measure3=rnorm(28,-50,5))

现在,我们将上面的代码打包到一个函数中。该函数接受一个名为 measure_var 的参数。这是数据列,以 character_string 的形式提供,它将提供绘图的 y 值。请注意,我们现在在 ggplot 中使用 aes_string 而不是 aes

plot_func = function(measure_var) {

  ypos = min(ggdata[ , measure_var]) + 0.005*diff(range(ggdata[ , measure_var]))
  xv = 0.02
  xh = 0.01
  xadj = diff(range(ggdata$Year))

  ggplot(data=ggdata, aes_string(x="Year", y=measure_var, group="Area", color="Area")) +
    geom_vline(xintercept=2011, color="#EE0000") +
    geom_vline(xintercept=2007, color="#000099") +
    geom_line(size=.75) +
    geom_point(size=1.5) +
    annotate(geom="text", x=2011 - xv*xadj, label="City1", y=ypos, 
             color="#EE0000", angle=90, hjust=0, family="serif") +
    annotate(geom="text", x=2007 - xh*xadj, label="City2", y=ypos, 
             color="#000099", angle=0, hjust=1, family="serif") +
    scale_y_continuous(limits=range(ggdata[ , measure_var]),
                       breaks=pretty_breaks(5)) +
    scale_x_continuous(breaks=min(ggdata$Year):max(ggdata$Year)) +
    scale_color_manual(values=c("#EE0000", "#00DDFF", "#009900", "#000099")) +
    theme(axis.text.x = element_text(angle=90, vjust=1),
          panel.background = element_rect(fill="white", color="white"),
          panel.grid.major = element_line(color="grey95"),
          text = element_text(size=11, family="serif")) +
    ggtitle(paste("Plot of", measure_var))
}

我们现在可以像这样 运行 一次函数:plot_func("measure1")。但是,让我们使用 lapply 在所有度量列上一次性 运行 它。我们给 lapply 一个向量,其中包含度量列的名称 (names(ggdata)[grepl("measure", names(ggdata))]),它依次 运行s plot_func 在每个列上,将结果图存储在列表 plot_list.

plot_list = lapply(names(ggdata)[grepl("measure", names(ggdata))], plot_func)

现在,如果我们愿意,我们可以使用 grid.arrange 将它们全部布置在一起。在这种情况下,我们只需要一个图例,而不是每个图都有一个单独的图例,因此我们将图例提取为一个单独的图形对象,并将其放在三个图旁边。

# Function to get legend from a ggplot as a separate graphical object
# Source: https://github.com/tidyverse/ggplot2/wiki/Share-a-legend-between-two-ggplot2-graphs/047381b48b0f0ef51a174286a595817f01a0dfad
g_legend<-function(a.gplot){
  tmp <- ggplot_gtable(ggplot_build(a.gplot))
  leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
  legend <- tmp$grobs[[leg]]
  return(legend)
}

# Get legend
leg = g_legend(plot_list[[1]])

# Lay out all of the plots together with a single legend
grid.arrange(arrangeGrob(grobs=lapply(plot_list, function(x) x + guides(colour=FALSE))),
             leg,
             ncol=2, widths=c(10,1))