R 有时无法评估从字符串解析的表达式
R sometimes fails to evaluate expressions parsed from strings
我有一个庞大的数据框,我需要在其中创建“滞后”变量并将它们与以前的时间点进行比较。由于这个过程需要可变,我选择编写自己的函数来创建这些滞后变量(此处未包含)。
当我使用 GLM 时,我想使用 stepAIC 函数,在开始编写“lag01 + lag02 ...”的十分之一之前,我想创建另一个函数(modelfiller),它根据我的参数创建这些字符串,然后我用 string2lang
使它们成为表达式。
这大部分都有效,但有一个问题我无法解决。
正如你在 reprex 中看到的那样,当我只使用 y~x+lag01+lag02
时可以创建 full.model。如果我在位置 1 和 3 使用 modelfiller("y", 2, "x", "lag")
它也可以。但是当我将 modelfiller("y", 2, "x", "lag")
放在代码中的位置 2 时(在 stepAIC glm 中),它会创建以下错误消息:
Error: Problem with `mutate()` input `GLM_AIC`.
x object '.x' not found
i Input `GLM_AIC` is `purrr::map(...)`.
i The error occurred in group 1: group = "a".
我也试过 as.formula
有和没有 eval
,但它导致了同样的问题。
group <- c(rep("a", 10), rep("b", 10), rep("c", 10))
order <- c(seq(1:10), seq(1:10), seq(1:10))
x <- c(runif(30))
y <- c(runif(30))
df <- data.frame(group, order, x, y)
df <- df %>%
dplyr::group_by(group) %>%
dplyr::arrange(group, order) %>%
dplyr::mutate(lag01 = dplyr::lag(x, n=1),
lag02 = dplyr::lag(x, n=2)) %>%
tidyr::drop_na()
modelfiller = function(depPar, maxlag, indepPar, str) {
varnames = list()
for (i in seq(1:maxlag)) {
varnames[i] = paste0(str, stringr::str_pad(i, width = 2, pad = "0"))
}
varnames = paste0(varnames, collapse="+")
varnames = paste(indepPar, varnames, sep = "+")
return(paste(depPar, varnames, sep = "~"))
}
full.model <- df %>%
tidyr::nest(- group) %>%
dplyr::mutate(
# Perform GLM calculation on each group and then a step-wise model selection based on AIC
GLM = purrr::map(
data, ~ lm(data = .x,
# Location 1 - Working
str2lang(modelfiller("y", 2, "x", "lag"))
#y~x+lag01+lag02
)),
GLM_AIC = purrr::map(
data, ~ MASS::stepAIC(glm(data = .x,
# Location 2 - NOT Working
str2lang(modelfiller("y", 2, "x", "lag"))
#y~x+lag01+lag02
)
,direction = "both", trace = FALSE, k = 2,
scope = list(
lower = lm(data = .x,
y ~ 1),
upper = glm(data = .x,
# Location 3 - Working
str2lang(modelfiller("y", 2, "x", "lag"))
#y~x+lag01+lag02
)
)))
)
问题是 glm
存储了用于引用数据的变量名称,然后 stepAIC
尝试检索该名称并对其求值以访问数据,但对定义变量的环境。为了演示,我将把你的代码简化为
mdl <- str2lang(modelfiller("y", 2, "x", "lag")) # This is your y~x+lag01+lag02
dfn <- df %>% tidyr::nest( data = c(-group) ) # First step of your %>% chain
glms <- purrr::map( dfn$data, ~glm(data = .x, mdl) ) # Construct the models
# Examine glms to observe that
# Call: glm(formula = mdl, data = .x) <--- glm() remembers that the data is in .x
# but stepAIC is not properly aware of where .x
# is defined and behaves effectively as
MASS::stepAIC( glms[[1]] ) # Error: object '.x' not found
选项 1
一种解决方法是 :
glm2 <- function(.df, ...) {
eval(rlang::expr(glm(!!rlang::enexpr(.df),!!!list(...)))) }
glms2 <- purrr::map( dfn$data, ~glm2(data = .x, mdl) ) # Same as above, but with glm2
MASS::stepAIC( glms2[[1]] ) # Now works
在有问题的地方将 glm
更改为 glm2
也会使您的代码正常工作。缺点是 Call:
然后会记住整个数据框,如果它们非常大,这可能会有问题。
选项 2
另一种方法是用 for
循环替换 purrr
调用,这有助于维护 stepAIC
假定的调用帧,从而将其引导至定义数据的位置
# This fails with Error: object '.x' not found
purrr::map( dfn$data, ~MASS::stepAIC(glm(data=.x, mdl), direction="both") )
# This works
for( mydata in dfn$data )
MASS::stepAIC(glm(data=mydata, mdl), direction="both")
这里的优点是不需要在调用中存储整个数据帧。缺点是您实际上无法访问 purrr
为简化代码所做的工作。
我有一个庞大的数据框,我需要在其中创建“滞后”变量并将它们与以前的时间点进行比较。由于这个过程需要可变,我选择编写自己的函数来创建这些滞后变量(此处未包含)。
当我使用 GLM 时,我想使用 stepAIC 函数,在开始编写“lag01 + lag02 ...”的十分之一之前,我想创建另一个函数(modelfiller),它根据我的参数创建这些字符串,然后我用 string2lang
使它们成为表达式。
这大部分都有效,但有一个问题我无法解决。
正如你在 reprex 中看到的那样,当我只使用 y~x+lag01+lag02
时可以创建 full.model。如果我在位置 1 和 3 使用 modelfiller("y", 2, "x", "lag")
它也可以。但是当我将 modelfiller("y", 2, "x", "lag")
放在代码中的位置 2 时(在 stepAIC glm 中),它会创建以下错误消息:
Error: Problem with `mutate()` input `GLM_AIC`.
x object '.x' not found
i Input `GLM_AIC` is `purrr::map(...)`.
i The error occurred in group 1: group = "a".
我也试过 as.formula
有和没有 eval
,但它导致了同样的问题。
group <- c(rep("a", 10), rep("b", 10), rep("c", 10))
order <- c(seq(1:10), seq(1:10), seq(1:10))
x <- c(runif(30))
y <- c(runif(30))
df <- data.frame(group, order, x, y)
df <- df %>%
dplyr::group_by(group) %>%
dplyr::arrange(group, order) %>%
dplyr::mutate(lag01 = dplyr::lag(x, n=1),
lag02 = dplyr::lag(x, n=2)) %>%
tidyr::drop_na()
modelfiller = function(depPar, maxlag, indepPar, str) {
varnames = list()
for (i in seq(1:maxlag)) {
varnames[i] = paste0(str, stringr::str_pad(i, width = 2, pad = "0"))
}
varnames = paste0(varnames, collapse="+")
varnames = paste(indepPar, varnames, sep = "+")
return(paste(depPar, varnames, sep = "~"))
}
full.model <- df %>%
tidyr::nest(- group) %>%
dplyr::mutate(
# Perform GLM calculation on each group and then a step-wise model selection based on AIC
GLM = purrr::map(
data, ~ lm(data = .x,
# Location 1 - Working
str2lang(modelfiller("y", 2, "x", "lag"))
#y~x+lag01+lag02
)),
GLM_AIC = purrr::map(
data, ~ MASS::stepAIC(glm(data = .x,
# Location 2 - NOT Working
str2lang(modelfiller("y", 2, "x", "lag"))
#y~x+lag01+lag02
)
,direction = "both", trace = FALSE, k = 2,
scope = list(
lower = lm(data = .x,
y ~ 1),
upper = glm(data = .x,
# Location 3 - Working
str2lang(modelfiller("y", 2, "x", "lag"))
#y~x+lag01+lag02
)
)))
)
问题是 glm
存储了用于引用数据的变量名称,然后 stepAIC
尝试检索该名称并对其求值以访问数据,但对定义变量的环境。为了演示,我将把你的代码简化为
mdl <- str2lang(modelfiller("y", 2, "x", "lag")) # This is your y~x+lag01+lag02
dfn <- df %>% tidyr::nest( data = c(-group) ) # First step of your %>% chain
glms <- purrr::map( dfn$data, ~glm(data = .x, mdl) ) # Construct the models
# Examine glms to observe that
# Call: glm(formula = mdl, data = .x) <--- glm() remembers that the data is in .x
# but stepAIC is not properly aware of where .x
# is defined and behaves effectively as
MASS::stepAIC( glms[[1]] ) # Error: object '.x' not found
选项 1
一种解决方法是
glm2 <- function(.df, ...) {
eval(rlang::expr(glm(!!rlang::enexpr(.df),!!!list(...)))) }
glms2 <- purrr::map( dfn$data, ~glm2(data = .x, mdl) ) # Same as above, but with glm2
MASS::stepAIC( glms2[[1]] ) # Now works
在有问题的地方将 glm
更改为 glm2
也会使您的代码正常工作。缺点是 Call:
然后会记住整个数据框,如果它们非常大,这可能会有问题。
选项 2
另一种方法是用 for
循环替换 purrr
调用,这有助于维护 stepAIC
假定的调用帧,从而将其引导至定义数据的位置
# This fails with Error: object '.x' not found
purrr::map( dfn$data, ~MASS::stepAIC(glm(data=.x, mdl), direction="both") )
# This works
for( mydata in dfn$data )
MASS::stepAIC(glm(data=mydata, mdl), direction="both")
这里的优点是不需要在调用中存储整个数据帧。缺点是您实际上无法访问 purrr
为简化代码所做的工作。