【问题标题】:Need help using grid.arrange to arrange two time series plots需要帮助使用 grid.arrange 来安排两个时间序列图
【发布时间】:2019-11-09 06:25:39
【问题描述】:

我在使用 gridextra 库时遇到了麻烦,特别是 grid.arrange 功能将时间序列图堆叠在彼此之上。我想比较 1992-2016 年的军事和 1992-2016 年的网络攻击......但是根据我的数据,军事攻击数据在 2010 年停止,网络攻击直到 2000 年才开始。我想将这两个图叠加在彼此之间不仅可以显示数据上的这种差距,还可以显示正在发生的不同趋势。

使用我在下面提供的代码,是否有人对如何正确使用 grid.arrange 将这两个图排列在彼此之上有任何提示? ...或者也许是做同样事情的不同方式?

    # Aggregated Cyber Attacks
    plot1 <- plot(allmerged$yearinitiated, allmerged$cyberattacks, 
    col="black", 
    xlab = "Year", 
    ylab = "# of Cyber Attacks",
    main = "Cyber Attacks over Time",
    type = "l")
    # Aggregated MID Attacks 
    plot2 <- plot(allmerged$yearinitiated, allmerged$midaction, 
    col="black", 
    xlab = "Year", 
    ylab = "# of MIDs",
    main = "MIDs Attacks over Time",
    type = "l")

下面是我的代码的示例。如您所见,我的“y”会有所不同,但对于这两个图,它们的“x”都应该是 1992-2016。

   yearinitiated      midaction      cyberattacks
   1995                  81              NA
   1996                  75              NA
   1997                  81              NA
   1998                  264             NA
   1999                  363             NA
   2000                  98              1
   2001                  105             7    
   2002                  83              NA
   2003                  79              3
   2004                  52              2
   2005                  50              4
   2006                  35              8
   2007                  26              18
   2008                  39              27
   2009                  31              28
   2010                  73              15
   2011                  NA              27

【问题讨论】:

  • 请在您的代码中放置一个可重现的小示例,该示例可以生成一些基础数据。这样我们就可以加载它并进行实验。缺乏这一点可能导致您的问题被否决(不是我),尽管我认为这有点苛刻,因为您是新人。
  • 嗨@MichaelTuchman,谢谢。我添加了一个可重现的示例。我是 R 的新手(并且每天都在努力变得更好),所以我希望我的问题不会令人恼火......我真的已经自己努力解决了几天,并且对 gridextra 库没有运气(我的教授推荐的东西)。

标签: r plot time-series


【解决方案1】:

数据

首先,请阅读how to make reproducible exampledput(your_data) 是向所有试图帮助您的人提供数据的最佳方式。

dat <- read.table(
  text = "   yearinitiated      midaction      cyberattacks
   1995                  81              NA
   1996                  75              NA
   1997                  81              NA
   1998                  264             NA
   1999                  363             NA
   2000                  98              1
   2001                  105             7    
   2002                  83              NA
   2003                  79              3
   2004                  52              2
   2005                  50              4
   2006                  35              8
   2007                  26              18
   2008                  39              27
   2009                  31              28
   2010                  73              15
   2011                  NA              27",
  stringsAsFactors = F,
  header = T
)

为什么grid.arrange() 不起作用?

如果您参考帮助页面,您可以看到gridExtra::grid.arrange() 函数旨在:

设置一个 gtable 布局以在一个页面上放置多个 grobs

其中,grob 代表图形对象。该功能适用​​于以下情况非常重要:

...grobs、gtables、ggplot 或格子对象...

这就是为什么当您使用base::plot() 绘制数据时使用gridExtra::grid.arrange() 不是最好的主意。检查您的 plot1plot2 变量的类:

class(plot1)
#"NULL"
class(plot2)
#"NULL"

上面的输出告诉您,代码中的plot() 调用返回NULL,而您在图形设备上看到的图只是base::plot()副作用。函数不返回图形对象,您可以在代码中进一步使用。您可以阅读更多关于副作用不纯函数here

为什么你不需要grid.arrange()

您不需要它,因为您可以使用其他工具来实现您的目的。

使用base::plot() 绘图

如果你阅读了base::par()函数的帮助页面,你会发现par()mfrow, mfcol参数的描述:

形式为 c(nr, nc) 的向量。随后的图形将在设备上按列 (mfcol) 或行 (mfrow) 分别绘制在 nr-by-nc 数组中。

这意味着如果你想在 MIDs 图上方绘制网络攻击图,你必须在以这种方式绘制之前调用par()

par(
  mfrow = c(2, 1),
  bty      = 'n',       # suppress the box around the plot
  col      = '#000F55', # set color of the plot
  col.axis = 'grey25',  # make axes grey,
  col.lab  = 'grey25',  # make labels grey
  col.main = 'grey25',  # make main text grey
  family   = 'mono',    # set font family
  mar      = rep(2, 4), # set margins
  tcl      = -0.25,     # set ticks length
  xaxs     = 'r',       # apply axis style
  yaxs     = 'r'        # same as above
  )

设置 x 限制:

XLIM <- range(dat$yearinitiated, na.rm = T)

之后你可以这样调用你的地块:

# Cyber attacks
plot(x    = dat$yearinitiated, 
     y    = dat$cyberattacks,
     xlim = XLIM,
     xlab = "Year", 
     ylab = "# of Cyber Attacks",
     main = "Cyber Attacks over Time",
     type = "l"
)

# MID attacks
plot(x    = dat$yearinitiated, 
     y    = dat$midaction,
     xlim = XLIM,
     xlab = "Year", 
     ylab = "# of MIDs",
     main = "MIDs Attacks over Time",
     type = "l"
     )

# dev.off()

这会给你以下情节:

要重置您的par 设置,请致电dev.off()

使用ggplot2 绘图

您可以按照@dc37 的建议使用facet_wrap()/facet_grid()

为什么需要两个地块?

老实说,我认为你没有。在一个图中比较两个趋势要容易得多,而不是尝试比较由单独的图表示的两个数据集。

使用base 功能:

使用base::plot()base::lines()base::legend() 函数,您可以在一个图中轻松绘制一段时间内的 MID 和网络攻击:

# Plot MID attacks
plot(x    = dat$yearinitiated, 
     y    = dat$midaction,
     xlim = XLIM,
     ylim = range(dat[, -1], na.rm = T),
     col  = "skyblue", 
     xlab = "Year", 
     ylab = "Count",
     main = "Cyber Attacks vs Military actions over Time",
     type = "s"
     )

# Add Cyber attacks 
lines(
  x    = dat$yearinitiated, 
  y    = dat$cyberattacks, 
  col  = "red",
  type = 's'
  )

# Add legend
legend(
  x      = max(dat$yearinitiated, na.rm = T) - 5.5,
  y      = max(dat[, -1], na.rm = T),
  legend = c('Cyber Attacks', 'Military actions'),
  fill  = c('red', 'skyblue')
  )

或者,作为base 功能的替代方案,您可以简单地使用ggplot2tidyverse 包中的几个函数:

library(tidyverse)

dat %>%
  gather(key = 'Action', value = 'Count', -yearinitiated) %>%
  rename('Year' = yearinitiated) %>%
  ggplot(aes(x = Year, y = Count, color = Action)) +
  geom_step() +
  ggthemes::theme_few() +
  ggtitle('Military actions vs Cyber attacks')

【讨论】:

  • 嗨,@utubun - 我认为出于演示的目的,我希望将两个图堆叠在一起。但正如你猜到的那样,对于第一个情节,我什至看不到它,因为边距太大。我试过用谷歌搜索如何使用 par 缩放/调整我的情节,但到目前为止似乎没有任何建议有效......?
  • 嗨,@newtoR。您是否正在尝试将您的情节嵌入到 RPres 中?如果没有,您可以直接在 RStudio 的 Plot 窗口中缩放和调整其大小,以便之后保存绘图。
  • @newtoR 请查看更新后的答案,如果它适用于您的情况,请告诉我
  • 嗨,@utuban - 效果很好!我需要再添加一行代码来扩展我的轴以显示 1990 年到 2018 年 - 只是因为我的数据涵盖了那些年,但这似乎起到了作用!关于在 RStudio 绘图窗口中放大/缩小,我看到了如何放大,但它不会让我缩小。我继续将我的情节保存为 pdf,但即便如此,它也占据了整个页面的大小。虽然不是什么大不了的事。再次感谢您!
【解决方案2】:

为了将两个数据相互叠加,我建议使用ggplotfacet.grid 参数。

基本上,使用您的代码,它可能看起来像这样:

# Orignal dataset
year = seq(1995,2011)
midaction = c(81,75,81,264,363,98,105,83,79,52,50,35,26,39,31,73,NA)
cyber = c(NA,NA,NA,NA,NA,1,7,NA,3,2,4,8,18,27,28,15,27)
df = data.frame(cbind(year,midaction,cyber))

# re-arranging dataset for plotting
new_df = data.frame(Year = df$year,Value=df$midaction)
new_df$type = "Midaction"
df_cyber = data.frame(Year = df$year, Value = df$cyber)
df_cyber$type = "Cyber"
new_df = rbind(new_df,df_cyber)

所以,new_df 看起来像这样:

> head(new_df)
  Year Value      type
1 1995    81 Midaction
2 1996    75 Midaction
3 1997    81 Midaction
4 1998   264 Midaction
5 1999   363 Midaction
6 2000    98 Midaction

对于使用facet_grid 进行绘图,您将执行以下操作:

library(ggplot2)
ggplot(new_df, aes(x = Year, y = Value, color = type)) +
  facet_grid(type ~., scales = 'free_y') +
  geom_line() + 
  scale_y_continuous(name = "Number of events")

并得到下图:

另类情节

但是,正如@utubun 所建议的那样,我认为您并不真的需要 grid.arrange 来绘制这两个数据。 我宁愿建议将这两个数据绘制在同一张图表上,并使用这些帖子中开发的技巧针对不同的比例进行校正:two y-axes with different scales for two datasets in ggplot2 [duplicate]Plot two histograms of two .csv data sets to compare the data in R (ggplot)

基本上,从您的 new_df 数据集开始,您的代码可能看起来像这样:

# setting a scale factor to plot both conditions on the same scaled
scale_factor = 13.33

new_df$scaled_value = ifelse(new_df$type == "Cyber",new_df$Value*scale_factor,new_df$Value)

现在,我们正在生成包含 sec.axis 选项的绘图:

# plotting part
library(ggplot2)
mycolors = c("Midaction" = "blue","Cyber" = "red")
ggplot(new_df,aes(x = Year, y = scaled_value, color = type, group = type)) + 
  geom_path() + 
  geom_line() + 
  scale_y_continuous(name = "Military Actions", sec.axis = sec_axis(~./scale_factor, name = "Cyber Attacks")) + 
  scale_color_manual(name = "Type", values = mycolors) + 
  theme(axis.title.y = element_text(color = mycolors["Midaction"]),
              axis.text.y = element_text(color = mycolors["Midaction"]),
              axis.title.y.right = element_text(color = mycolors["Cyber"]),
              axis.text.y.right = element_text(color = mycolors["Cyber"])
              )

情节应该是这样的:

希望对你有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-14
    • 2011-05-10
    • 2012-02-20
    • 1970-01-01
    • 1970-01-01
    • 2016-07-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多