在 R 中使用 for 循环和 ggplot 处理承诺 (rlang)
Handling promises (rlang) with for loops and ggplot in R
此脚本的目的是复制如下图所示的内容:
发现于: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
以上代码创建了一个数据框对象,可用于创建所需的图形。对于每次迭代(在原始图中不同的行),它会创建一个对应于轴上方迭代 "height" 的向量(该列名称始终为 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 包处理承诺有些陌生,我怀疑我的错误可能在那里......
对我有用的是用 as.symbol()
替换 gg_adding()
函数中的 enquo()
调用,这样新函数将如下所示:
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))
我对 tidy 的评估和引用略有了解,但并不完全。我理解的是,无论你在 aes()
中输入什么,都将始终在 data
列名的上下文中进行评估,首先在层的数据中,然后在全局数据中,除非用户在他的电话(例如 aes(fill = "black")
之类的)。因为 x
和 data
的值已经在您的 ggbase
构造中指定,所以我们在您的 geom_point()
调用中不需要它。
我知道这可能是一个未经请求的提示,我深表歉意,但 ggplot 似乎更喜欢处理长数据而不是宽数据。我对 'wide' 数据的意思是你的迭代有点像 cbind()
-ed 在一起。因此,如果您首先计算每个迭代,然后 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)))
此脚本的目的是复制如下图所示的内容:
我遇到的问题与(我认为)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
以上代码创建了一个数据框对象,可用于创建所需的图形。对于每次迭代(在原始图中不同的行),它会创建一个对应于轴上方迭代 "height" 的向量(该列名称始终为 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 包处理承诺有些陌生,我怀疑我的错误可能在那里......
对我有用的是用 as.symbol()
替换 gg_adding()
函数中的 enquo()
调用,这样新函数将如下所示:
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))
我对 tidy 的评估和引用略有了解,但并不完全。我理解的是,无论你在 aes()
中输入什么,都将始终在 data
列名的上下文中进行评估,首先在层的数据中,然后在全局数据中,除非用户在他的电话(例如 aes(fill = "black")
之类的)。因为 x
和 data
的值已经在您的 ggbase
构造中指定,所以我们在您的 geom_point()
调用中不需要它。
我知道这可能是一个未经请求的提示,我深表歉意,但 ggplot 似乎更喜欢处理长数据而不是宽数据。我对 'wide' 数据的意思是你的迭代有点像 cbind()
-ed 在一起。因此,如果您首先计算每个迭代,然后 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)))