如何解决R有时无法评估从字符串解析的表达式
我有一个庞大的数据框,需要创建“滞后”变量并将其与以前的时间点进行比较。由于此过程需要可变,因此我选择编写自己的函数来创建这些滞后变量(此处未包括)。
当我使用GLM时,我想使用stepAIC函数,在开始编写“ lag01 + lag02 ...”的十分之一之前,我想创建另一个函数(modelfiller),该函数根据我的参数创建这些字符串,然后我使用string2lang
使其成为表达式。
这在大多数情况下都是有效的,但是有一个问题我无法理解。
正如您在reprex中看到的那样,当我仅使用y~x+lag01+lag02
时可以创建模型。如果我在位置1和3处使用modelfiller("y",2,"x","lag")
,它也可以工作。但是,当我将modelfiller("y","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",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,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","lag"))
#y~x+lag01+lag02
)),GLM_AIC = purrr::map(
data,~ MASS::stepAIC(glm(data = .x,# Location 2 - NOT Working
str2lang(modelfiller("y","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","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
一种解决方法是manually construct the expression that contains the data and then evaluate it:
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
另一种替代方法是将purrr
调用替换为for
循环,这有助于维护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,direction="both")
这里的优点是不需要在调用内部存储整个数据帧。缺点是您实际上无法访问purrr
简化代码的操作。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。