下面显示的R代码是一个简单的工作示例,用于重现我无法理解的错误。运行脚本应该产生错误Error in eval(expr, envir, enclos) : could not find function "fitModel"
。在environments上阅读了一两件事后,我想我明白为什么会出现这种情况," fitModel"未在" obscureFunction"的执行环境中定义。我通过对" myFormula"进行以下更改来修复:
myFormula <- "y ~ eval(fitModel(x, a), envir = environment(fitModel))"
我不明白&#34; fitModel&#34;可以在&#34; fitModel&#34;的环境中进行评估。当函数无法在&#34; obscureFunction&#34;的调用环境中找到时,换句话说,我不明白为什么这个代码更改有效。我也不明白为什么如果&#34; topFunction&#34;的主体原始代码工作正常。如果我们定义&#34; fitModel&#34;和&#34; obscureFunction&#34;在R_GlobalEnv
并致电&#34; obscureFunction&#34;从控制台。
## Minimum Working Example to reproduce error
rm(list = ls())
library(minpack.lm)
topFunction <- function(){
fitModel <- function(x, a){
exp(-a * x)
}
## Create a function to use with lapply()
obscureFunction <- function(){
x <- seq(-1, 1, 0.01)
y <- exp(-0.5 * x)
Data <- data.frame(x, y)
init <- c(a = 1)
myFormula <- "y ~ fitModel(x, a)"
myFormula <- as.formula(myFormula)
nlsOutput <- nlsLM(formula = myFormula, start = init, data = Data)
return(nlsOutput)
}
## Function call
obscureFunction()
## Other calculations done with fitModel()
}
topFunction()
答案 0 :(得分:7)
嗯,这里有两个问题。第一个是使用字符串表示公式。最好使用
myFormula <- y ~ fitModel(x, a)
原因是公式捕获他们的环境,而字符串则没有。 (正如@BridieG所指出的,as.formula()
将捕获环境;我在阅读代码时跳过了那一行。我仍然认为最好直接创建公式。)拥有一个参考环境可以更容易地找到函数在公式中使用。因此,如果您使用lm()
而不是nlsLM
,则可以使用这两项更改
# myFormula <- "y ~ fitModel(x, a)" ... becomes
myFormula <- y ~ fitModel(x, 1)
#nlsOutput <- nlsLM(formula = myFormula, start = init, data = Data) ...becomes
nlsOutput <- lm(formula = myFormula, data = Data)
这适用于公式语法(不带引号的var名称)而不是字符串,因为公式可以捕获环境。
至少应该如何运作。包作者可以随意评估公式,nlsLM()
函数的作者决定忽略分配给公式的环境。他们在nlsLM()
FCT <- function(par) {
mf[m] <- par
rhs <- eval(formula[[3L]], envir = mf)
res <- lhs - rhs
res <- .swts * res
res
}
所以这是第二个问题。在这里,他们在mf
对象中强制执行评估,该对象是由数据的协变量和参数估计组成的data.frame。如果它写成
rhs <- eval(formula[[3L]], envir = mf, environment(formula))
它会起作用。这基本上是model.frame()
lm()
中允许此操作的内容。我们可以用制作我们自己的“修正”版本的功能
# tested with minpack.lm_1.1-8
nlsLM2<-nlsLM
body(nlsLM2)[[27]][[3]][[3]][[3]]<-quote(rhs<-eval(formula[[3L]], envir = mf, environment(formula)))
然后进行这些替换
# myFormula <- "y ~ fitModel(x, a)" ... becomes
myFormula <- y ~ fitModel(x, a)
#nlsOutput <- nlsLM(formula = myFormula, start = init, data = Data) ...becomes
nlsOutput <- nlsLM2(formula = myFormula, start = init, data = Data)
工作并返回
Nonlinear regression model
model: y ~ fitModel(x, a)
data: Data
a
0.5
residual sum-of-squares: 0
Number of iterations to convergence: 5
Achieved convergence tolerance: 1.49e-08
因此,关于所有R函数如何处理环境和范围,你所能说的并不多。这种行为对于nlsLM()
作者决定评估其参数的方式是独一无二的。