Нужна помощь в использовании grid.arrange для организации двух временных рядов - PullRequest
0 голосов
/ 09 ноября 2019

У меня проблемы с использованием библиотеки 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")

Ниже приведен пример того, как выглядит мой код. Как вы увидите, мое «у» будет отличаться, но для обоих графиков у них обоих должен быть «х» 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

Ответы [ 2 ]

1 голос
/ 09 ноября 2019

Для построения обоих данных друг над другом я бы рекомендовал использовать аргумент facet.grid, равный ggplot.

По сути, с вашим кодом это может выглядеть примерно так:

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

и получите следующий график: enter image description here

Альтернативный участок

Однако, как предложил @utubun, я не думаю, что вам действительно нужен grid.arrange для построения обоих данных. Я скорее предложу вывести данные на один и тот же график и скорректировать их для разных шкал, используя трюк, разработанный в этих статьях: две оси Y с разными шкалами для двух наборов данных в ggplot2 [duplicate] и Постройте две гистограммы из двух наборов данных .csv для сравнения данных в 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"])
              )

И график должен выглядеть так: enter image description here

Надеюсь, это поможет вам.

1 голос
/ 09 ноября 2019

Данные

Прежде всего, пожалуйста, прочитайте , как сделать воспроизводимый пример : dput(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 для размещения нескольких страниц на странице

Где grob обозначает графический объект . Очень важно, чтобы эта функция работала с:

... объектами grobs, gtables, ggplot или trellis ...

И именно поэтому, когда вы выводите свои данныеиспользование base::plot() с использованием gridExtra::grid.arrange() не лучшая идея. Проверьте класс ваших plot1 и plot2 переменных:

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

Приведенный выше вывод говорит вам, что plot() вызовы из вашего кода возвращают NULL, а график, который вы видите на своем графическом устройствеесть только побочный эффект из base::plot(). Функция не возвращает графический объект , который вы можете в дальнейшем использовать в своем коде. Вы можете узнать больше о побочных эффектах и нечистых функциях здесь .

Почему вам не нужны grid.arrange()?

Вам это не нужно, потому что есть другие инструменты, которые вы можете использовать для своих целей.

Печать с base::plot()

Если вы прочитали страницу справки для функции base::par(), вынайдет описание mfrow, mfcol , параметры par():

Вектор вида c (nr, nc). Последующие рисунки будут отображаться в массиве nr-by-nc на устройстве столбцами (mfcol) или строками (mfrow) соответственно.

Это означает, что если вы хотите построить график Cyber ​​Attacksвыше графика MID, вы должны позвонить 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()

Что дает вам следующий график:

base and par

Чтобы сбросить настройки par, позвоните dev.off().

Построение с помощью ggplot2

Вы можете использовать facet_wrap()/facet_grid(), как это предлагается @ dc37.

Зачем вам два участка?

ЧестноЯ думаю, что нет. Гораздо проще сравнивать две тенденции на одном графике, чем пытаться сравнивать два набора данных, представленных отдельными графиками.

Использование функциональности base:

Используя функции base::plot(), base::lines() и base::legend(), вы можете легко наносить одновременно атаки MID и Cyber ​​на одном графике:

# 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

Или, в качестве альтернативы функциональности base, вы можете просто использовать ggplot2 и несколько функций из пакетов tidyverse:

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

ggplot2

...