【问题标题】:Dividing long time series in multiple panels with ggplot2用ggplot2在多个面板中划分长时间序列
【发布时间】:2017-08-17 19:28:18
【问题描述】:

我有一个相当长的时间序列,我想在 ggplot 中绘制,但它足够长,即使使用页面的全宽也几乎无法阅读。

我想要做的是将绘图分成 2 个(或更多,在一般情况下)面板,一个在一个之上。

我可以手动完成,但不仅麻烦,而且很难让轴具有相同的比例。理想情况下,我想要这样的东西:

ggplot(data, aes(time, y)) + 
  geom_line() +
  facet_time(time, n = 2)

然后得到这样的东西:

(此图是使用facet_wrap(~(year(as.Date(time)) > 2000), ncol = 1, scales = "free_x") 制作的,它弄乱了x 轴比例,仅适用于2 个面板,不适用于geom_smooth()

此外,理想情况下,它还可以正确处理汇总统计信息。例如,为geom_smooth() 使用正确的数据(因此 facetting 不会这样做,因为在每个 facet 的开头它不会使用前一个 facet 的最后一块中的数据)。

有没有办法做到这一点?

谢谢!

【问题讨论】:

    标签: r ggplot2


    【解决方案1】:

    您可以通过存储绘图对象,然后将其打印两次来做到这一点。每次添加一个选项coord_cartesian

    orig_plot <- ggplot(data, aes(time, y)) + 
      geom_line() 
    
    early <-  orig_plot + coord_cartesian(xlim = c(1982, 2000))
    late  <-  orig_plot + coord_cartesian(xlim = c(2000, 2016))
    

    这样可以确保两个图都使用所有数据。

    要将它们绘制在同一页面上,请使用网格(我是从 ggplot2 书中得到的,它可能在某处以 pdf 格式出现):

    library(grid)
    vp1 <- viewport(width = 1, height = .5, just = c("center", "bottom"))
    vp2 <- viewport(width = 1, height = .5, just = c("center", "top"))
    print(early, vp = vp1)
    print(late, vp = vp2)
    

    【讨论】:

    • 这似乎不适用于 Date 类 x 变量。我得到Error: Invalid input: date_trans works with objects of class Date only
    • 您可以使用 gridExtra 包中的 grid.arrange 来布置图:grid.arrange(early, late, ncol=1)
    • 使用coord_cartesian(xlim = as.Date(c(1982, 2000))) 解决了这个问题,result 是可行的(标签是重复的,所以我需要手动删除它们)。另一个问题是它在 Rmarkdown 文档中不起作用。
    • 在 Rmarkdown 中,问题要简单得多:只打印early,然后打印late
    • 我用这种方法遇到的问题是比例不相等,但我认为通过一些修补我可以让它工作。
    【解决方案2】:

    下面我创建了两个单独的图,一个用于 1982-1999 年,一个用于 1999-2016 年,然后使用 gridExtra 包中的 grid.arrange 对它们进行布局。水平轴在两个图中的比例相同。

    我还使用loess 函数在ggplot 之外生成回归线,以便可以使用geom_line 添加它(您当然可以在这里使用任何回归函数,例如lmgam、样条线、等等)。使用这种方法,可以在整个时间序列上运行回归,确保两个面板上的回归线的连续性,即使我们将时间序列分成两半进行绘图。

    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")
    

    【讨论】:

    • 这就是我所做的,但在缩放和处理 geom_smooth() 之类的东西方面存在一些问题。
    • 太棒了。这非常完美。虽然很遗憾我需要预先计算平滑度,但可以忍受。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-08
    • 2018-07-05
    • 1970-01-01
    • 2016-06-24
    • 2018-09-11
    • 1970-01-01
    • 2017-11-15
    相关资源
    最近更新 更多