此脚本的目的是复制类似下图的内容: 在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
以上代码创建了一个数据框对象,该对象可用于创建所需的图形。对于每次迭代(在原始图中用不同的线表示),它会创建一个与轴上方的迭代“高度”相对应的向量(该列名称始终为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)
显然那不是我想要的... 为了测试事物,我明确规定了每次迭代。
# 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)
我想将这些函数放入我目前正在编写的程序包中,因此明确规定要添加的内容(就像我在上面所做的那样)是行不通的...
我不确定为什么我以前的代码不能产生相同的结果。我对使用rlang包处理诺言有些陌生,我怀疑我的错误可能在那里...
答案 0 :(得分:3)
对我有用的是将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)))