下面我创建了两个单独的图,一个用于 1982-1999 年,一个用于 1999-2016 年,然后使用 gridExtra 包中的 grid.arrange 对它们进行布局。水平轴在两个图中的比例相同。
我还使用loess 函数在ggplot 之外生成回归线,以便可以使用geom_line 添加它(您当然可以在这里使用任何回归函数,例如lm、gam、样条线、等等)。使用这种方法,可以在整个时间序列上运行回归,确保两个面板上的回归线的连续性,即使我们将时间序列分成两半进行绘图。
library(dplyr) # For the chaining (%>%) operator
library(purrr) # For the map function
library(gridExtra) # For the grid.arrange function
从 ggplot 中提取图例的功能。我们将使用它在两个单独的图中获得一个图例。
# http://stackoverflow.com/questions/12539348/ggplot-separate-legend-and-plot
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]]
legend
}
# Fake data
set.seed(255)
dat = data.frame(time=rep(seq(1982,2016,length.out=500),2),
value= c(arima.sim(list(ar=c(0.4, 0.05, 0.5)), n=500),
arima.sim(list(ar=c(0.3, -0.3, 0.6)), n=500)),
group=rep(c("A","B"), each=500))
使用loess 生成更平滑的线:我们希望group 的每个级别都有一个单独的回归线,因此我们使用group_by 和来自dplyr 的链接运算符:
dat = dat %>% group_by(group) %>%
mutate(smooth = predict(loess(value ~ time, span=0.1)))
创建一个包含两个绘图的列表,一个用于每个时间段:我们使用map为每个时间段创建单独的绘图,并返回一个包含两个绘图对象作为元素的列表(您也可以使用 base lapply for这个而不是map):
pl = map(list(c(1982,1999), c(1999,2016)),
~ ggplot(dat %>% filter(time >= .x[1], time <= .x[2]),
aes(colour=group)) +
geom_line(aes(time, value), alpha=0.5) +
geom_line(aes(time, smooth), size=1) +
scale_x_continuous(breaks=1982:2016, expand=c(0.01,0)) +
scale_y_continuous(limits=range(dat$value)) +
theme_bw() +
labs(x="", y="", colour="") +
theme(strip.background=element_blank(),
strip.text=element_blank(),
axis.title=element_blank()))
# Extract legend as a separate graphics object
leg = g_legend(pl[[1]])
最后,我们布置了两个图(去除图例后)加上提取的图例:
grid.arrange(arrangeGrob(grobs=map(pl, function(p) p + guides(colour=FALSE)), ncol=1),
leg, ncol=2, widths=c(10,1), left="Value", bottom="Year")