2015-05-07 34 views
1

下面显示的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))"eval(expr,envir,enclos)中的错误:找不到函数 - 嵌套函数和环境

我不明白在调用环境中找不到函数时,如何在“fitModel”环境中评估“fitModel”的“obscureFunction”,换句话说,我不明白为什么这个代码改变的作品。我也不明白为什么如果在不调用它的情况下运行“topFunction”的主体,原始代码工作正常,即我们在R_GlobalEnv中定义“fitModel”和“obscureFunction”,并从控制台调用“obscureFunction”。

## 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

@BrodieG的确如此。评论编辑。 – nsheff

回答

6

那么,这里有两个问题。第一种是使用一个字符串作为公式。这是更好的使用

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) 

这适用公式语法(不带引号的变种名称),而不是工作的字符串,因为该公式可以捕捉环境。

至少它应该如何工作。软件包作者可自由评估公式的方式,并且nlsLM()函数的作者决定忽略分配给公式的环境。他们在这个函数这么做内nlsLM()

FCT <- function(par) { 
    mf[m] <- par 
    rhs <- eval(formula[[3L]], envir = mf) 
    res <- lhs - rhs 
    res <- .swts * res 
    res 
} 

所以这是第二个问题。在这里,他们在mf对象中执行评估,该对象是由数据和参数估计的协变量组成的数据框。如果它被写为

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()作者如何决定评估其参数是独特的。

+1

请注意,这适用于字符公式后跟'as.formula',因为第二步附加环境(+1) – BrodieG

+0

好点@BrodieG。 (但我仍然认为最好跳过这一步,并首先制定一个合适的公式)。 – MrFlick

+0

谢谢@MrFlick,您的解决方案也适用于我的最终目标。我也没有想到创建R函数的修改版本,所以现在我开始尝试对nlsLM进行一些其他修改,我一直想尝试。 – frank2165

相关问题