2015-05-07 63 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

相關問題