【问题标题】:Error in eval(expr, envir, enclos) : could not find function - Nested Functions & Environmentseval 中的错误(expr、envir、enclos):找不到函数 - 嵌套函数和环境
【发布时间】:2023-08-18 09:51:01
【问题描述】:

下面显示的 R 代码是重现我无法理解的错误的最小工作示例。运行脚本应该会产生错误,Error in eval(expr, envir, enclos) : could not find function "fitModel"。在environments 上阅读了一两件事后,我想我明白为什么会在这种情况下发生这种情况,在“obscureFunction”的执行环境中没有定义“fitModel”。我通过对“myFormula”进行以下更改来解决此问题: myFormula <- "y ~ eval(fitModel(x, a), envir = environment(fitModel))"

我不明白当在“obscureFunction”的调用环境中找不到函数时,如何在“fitModel”的环境中评估“fitModel”,换句话说我不明白为什么这段代码改变作品。我也不明白为什么如果“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()

【问题讨论】:

  • @BrodieG 确实如此。评论已编辑。

标签: r


【解决方案1】:

嗯,这里有两个问题。第一种是使用字符串作为公式。最好用

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() 作者决定评估其参数的方式所独有的。

【讨论】:

  • 请注意,这适用于后跟 as.formula 的字符公式,因为第二步附加了环境 (+1)
  • 好点@BrodieG。 (但我仍然认为最好跳过这一步,首先制定一个正确的公式)。
  • 谢谢@MrFlick,您的解决方案也适用于我。我也没有想过创建 R 函数的修改版本,所以现在我开始尝试对 nlsLM 进行一些其他修改,我一直想尝试。