2011-11-21 83 views
6

我有一個奇怪的R問題,我似乎無法解決問題。我試過編寫一個函數,爲R中的逐步過程選擇的模型執行K-fold交叉驗證(我意識到逐步過程的問題,純粹是爲了比較目的):)將模型公式傳遞給另一個函數時找不到對象

現在的問題是,如果我定義函數參數(linmod,k,方向)並運行函數的內容,它的工作原理是完美無瑕的。但是,如果我將它作爲函數運行,則會出現錯誤,指出無法找到datas.train對象。

我試過用debug()函數來逐步完成這個函數,並且該對象清楚地存在,但是R說它並不是當我真正運行該函數時。如果我只適合使用lm()的模型,它可以正常工作,所以我相信這是循環中的step函數的問題,同時也是函數內部的問題。 (嘗試註釋步驟命令,並將預測設置爲來自普通線性模型的預測。)

#CREATE A LINEAR MODEL TO TEST FUNCTION 
lm.cars <- lm(mpg~.,data=mtcars,x=TRUE,y=TRUE) 


#THE FUNCTION 
cv.step <- function(linmod,k=10,direction="both"){ 
    response <- linmod$y 
    dmatrix <- linmod$x 
    n <- length(response) 
    datas <- linmod$model 
    form <- formula(linmod$call) 

    # generate indices for cross validation 
    rar <- n/k 
    xval.idx <- list() 
    s <- sample(1:n, n) # permutation of 1:n 
    for (i in 1:k) { 
    xval.idx[[i]] <- s[(ceiling(rar*(i-1))+1):(ceiling(rar*i))] 
    } 

    #error calculation 
    errors <- R2 <- 0 

    for (j in 1:k){ 
    datas.test <- datas[xval.idx[[j]],] 
     datas.train <- datas[-xval.idx[[j]],] 
     test.idx <- xval.idx[[j]] 

     #THE MODELS+ 
     lm.1 <- lm(form,data= datas.train) 
     lm.step <- step(lm.1,direction=direction,trace=0) 

     step.pred <- predict(lm.step,newdata= datas.test) 
     step.error <- sum((step.pred-response[test.idx])^2) 
     errors[j] <- step.error/length(response[test.idx]) 

     SS.tot <- sum((response[test.idx] - mean(response[test.idx]))^2) 
     R2[j] <- 1 - step.error/SS.tot 
    } 

    CVerror <- sum(errors)/k 
    CV.R2 <- sum(R2)/k 

    res <- list() 
    res$CV.error <- CVerror 
    res$CV.R2 <- CV.R2 

return(res) 
} 


#TESTING OUT THE FUNCTION 
cv.step(lm.cars) 

有什麼想法?

+2

似乎有一個範圍問題,其中'step(lm.1,direction = direction,trace = 0)'找不到'datas.train',就像你已經知道的那樣。我自己看不出問題的原因。將'datas.train'指定爲全局變量是一種解決方法,但不是一個特別令人滿意的方法('datas.train << - datas [-xval.idx [[j]],]')。也許這應該遷移到StackOverflow? – jthetzel

+0

具體來說,在step()中調用'add1(fit,scope $ add,scale = scale,trace = trace,k = k,...)'會拋出錯誤,其中'add1()'爲'統計::: add1.lm'。 – jthetzel

+0

@jthetzel,的確。我解決類似問題的一種方法是在循環內部調用另一個函數,即全局分配它。 – dcl

回答

10

當你創建你的公式,lm.cars,在被分配了自己的環境。除非您明確更改該環境,否則該環境將保留在公式中。因此,當您使用formula函數提取公式時,將包含模型的原始環​​境。

我不知道,如果我在這裏使用的是正確的術語,但我認爲你需要明確地改變環境爲您的函數內部的公式:

cv.step <- function(linmod,k=10,direction="both"){ 
    response <- linmod$y 
    dmatrix <- linmod$x 
    n <- length(response) 
    datas <- linmod$model 
    .env <- environment() ## identify the environment of cv.step 

    ## extract the formula in the environment of cv.step 
    form <- as.formula(linmod$call, env = .env) 

    ## The rest of your function follows 
+0

這有效。我將不得不尋找這個環境的東西。 :)乾杯。 – dcl

4

另一個問題,可能會導致這如果通過character(字符串vector)到lm而不是formulavector s沒有environment,所以當lmcharacter轉換爲formula時,它顯然也沒有environment,而不是自動分配本地環境。如果使用一個對象作爲不在數據參數data.frame中但是在本地函數參數中的權重,則會得到一個not found錯誤。這種行爲不是很容易理解。這可能是一個錯誤。

這是一個最小可重現的例子。此函數採用data.frame,兩個變量名稱和一個要使用的權重向量。

residualizer = function(data, x, y, wtds) { 
    #the formula to use 
    f = "x ~ y" 

    #residualize 
    resid(lm(formula = f, data = data, weights = wtds)) 
} 

residualizer2 = function(data, x, y, wtds) { 
    #the formula to use 
    f = as.formula("x ~ y") 

    #residualize 
    resid(lm(formula = f, data = data, weights = wtds)) 
} 

d_example = data.frame(x = rnorm(10), y = rnorm(10)) 
weightsvar = runif(10) 

和測試:

> residualizer(data = d_example, x = "x", y = "y", wtds = weightsvar) 
Error in eval(expr, envir, enclos) : object 'wtds' not found 

> residualizer2(data = d_example, x = "x", y = "y", wtds = weightsvar) 
     1   2   3   4   5   6   7   8   9   10 
0.8986584 -1.1218003 0.6215950 -0.1106144 0.1042559 0.9997725 -1.1634717 0.4540855 -0.4207622 -0.8774290 

這是一個非常微妙的錯誤。如果有人進入功能環境browser,可以看到權重向量就好了,但不知何故在lm調用中找不到!

如果使用名稱weights作爲權重變量,則該錯誤更難調試。在這種情況下,由於lm找不到對象的權重,則默認爲功能weights()基地從而拋出一個更奇怪的錯誤:

Error in model.frame.default(formula = f, data = data, weights = weights, : 
    invalid type (closure) for variable '(weights)' 

不要問我有多少時間帶我去想出解決辦法。