Обработка обещаний (rlang) с циклами for и ggplot в R - PullRequest
1 голос
/ 17 апреля 2019

Целью этого скрипта было воспроизвести что-то вроде рисунка ниже: Hydman's Rolling Time series cross-validation найдено на: https://robjhyndman.com/hyndsight/tscv/

Проблема, с которой я столкнулся, связана (я думаю) с тем, как R обрабатывает мои обещания в ggplot.

Ниже приведен пример, который воспроизводит мою проблему.

library(tidyverse)
process_starting_row  <- 600
per_validation_period <- 30
number_of_validations <- 5

graphical_data <- data.frame(x= 1:(process_starting_row + 1 + (number_of_validations)*per_validation_period))

for (it in 1:number_of_validations) {

  # For this graph there is always a line and then a colour component explaining each one...
  graphical_data[,paste0("iteration",it,"line")]   <- c(it)

  # First make the whole row grey and then "dolly up" the colours.
  graphical_data[,paste0("iteration",it,"colour")] <- "grey"
  graphical_data[1:(process_starting_row + (it-1)*per_validation_period), paste0("iteration",it,"colour")] <- "blue"
  graphical_data[(process_starting_row + 1 + (it)*per_validation_period), paste0("iteration",it,"colour")] <- "red"

}
#graphical_data

Приведенный выше код создает объект dataframe, который можно использовать для создания нужной фигуры. Для каждой итерации (на исходном рисунке отдельная линия) создается вектор, соответствующий итерации "высота" над осью (имя столбца всегда iteration#line и соответствующий символьный вектор iteration#colour, с цветным кодом для каждая из точек.

Следующий бит - создание базового объекта ggplot.

ggbase <- ggplot(data = graphical_data, aes(x=x)) +
  coord_cartesian(xlim = c(process_starting_row-1*per_validation_period, nrow(graphical_data))) +
  theme_bw()

Именно на этом базовом объекте я хочу повторить.

Я написал функцию, которая будет добавлять каждую итерацию gg_adding(), а затем еще одну ggaddfor(), которая запускает цикл for.

gg_adding <- function(data, iteration_sub, color_sub){
  iteration_promise <- enquo(iteration_sub)
  colour_promise <- enquo(color_sub)
  gg  <- geom_point(data = data, aes(x= x, y= !! iteration_promise, color = !! colour_promise))
  return(gg)
}

ggaddfor <- function(data, gg){
  ggout <- gg
for (it in 1:number_of_validations) {
  #print(it)
  iterationsub <- paste0("iteration",it,"line")
  coloursub <- paste0("iteration",it,"colour")

  ggout <- ggout + gg_adding(data, iterationsub, coloursub)

  }
  return(ggout)
}

Когда я запускаю эту функцию, я получаю следующее:

# Not working
ggaddfor(graphical_data, ggbase)

, который производит вывод, который выглядит следующим образом: ggaddfor-wrong-output

Очевидно, это не то, на что я надеялся ... Для того, чтобы проверить вещи, я оговорил каждую итерацию в явном виде.

    # Working...
ggadd <- ggbase
ggadd <- ggadd + gg_adding(graphical_data, iteration1line, iteration1colour)
ggadd <- ggadd + gg_adding(graphical_data, iteration2line, iteration2colour)
ggadd <- ggadd + gg_adding(graphical_data, iteration3line, iteration3colour)
ggadd <- ggadd + gg_adding(graphical_data, iteration4line, iteration4colour)
ggadd <- ggadd + gg_adding(graphical_data, iteration5line, iteration5colour)

Это дает желаемый результат: ggadd_output

Я хочу поместить эти функции в пакет, который я сейчас пишу, и поэтому явное указание дополнений (как я делаю прямо выше) не сработает ...

Я не уверен, почему мой предыдущий код не дает таких же результатов. Я немного новичок в обработке обещаний с помощью пакета rlang, и я подозреваю, что моя ошибка могла быть там ...

1 Ответ

3 голосов
/ 17 апреля 2019

Что сработало для меня, так это заменить ваши enquo() вызовы в вашей gg_adding() функции на as.symbol(), чтобы новая функция выглядела так:

gg_adding <- function(data, iteration_sub, color_sub){
  iteration_promise <- as.symbol(iteration_sub)
  colour_promise <- as.symbol(color_sub)
  gg  <- geom_point(data = data, aes(x= x, y= !! iteration_promise, color = !! colour_promise))
  return(gg)
}

Однако, чтобы не дублировать ваши данные на каждой итерации, я бы предложил это как ваш geom_point() вызов.

gg  <- geom_point(aes(y= !! iteration_promise, color = !! colour_promise))

Я тангенциально знаком с аккуратной оценкой и цитатой, но не полностью. Я понимаю, что все, что вы вводите в aes(), всегда будет оцениваться в контексте имен столбцов data, сначала в данных слоя, затем в глобальных данных, если пользователь не является явным в своих вызовах (например, aes(fill = "black") или что-то). Поскольку значение для x и data уже указано в вашей конструкции ggbase, оно не требуется в вашем вызове geom_point().

Я знаю, что это, возможно, незапрошенный совет, и я извиняюсь, но ggplot, похоже, предпочитает работать с длинными данными больше, чем с широкими данными. Под «широкими» данными я подразумеваю, что ваши итерации вроде бы cbind(). Поэтому, если вы сначала вычислите каждую итерацию, а затем rbind() их вместе, вы можете немного сократить свой сценарий и вообще обойти (квази) цитату, чтобы получить похожий график:

new_gr_dat <- lapply(seq_len(number_of_validations), function(it){
  df <- data.frame(x= 1:(process_starting_row + 1 + (number_of_validations)*per_validation_period),
                   line = it, # doubles as y-value and iteration tracker
                   colour = "grey")
  df[1:(process_starting_row + (it-1)*per_validation_period), "colour"] <- "blue"
  df[(process_starting_row + 1 + (it)*per_validation_period), "colour"] <- "red"
  return(df)
})
new_gr_dat <- do.call(rbind, new_gr_dat)

ggplot(new_gr_dat, aes(x = x, y = line, colour = colour)) +
  geom_point() +
  coord_cartesian(xlim = c(process_starting_row-1*per_validation_period, max(new_gr_dat$x)))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...