2011-11-21 10 views
6

Ho un problema strano con R che non riesco a capire.Errore oggetto non trovato quando si passa la formula del modello a un'altra funzione

Ho provato a scrivere una funzione che esegue la convalida incrociata K-fold per un modello scelto dalla procedura stepwise in R. (Sono a conoscenza dei problemi con procedure stepwise, è puramente a scopo di confronto) :)

Ora il problema è che, se definisco i parametri della funzione (linmod, k, direzione) ed eseguo il contenuto della funzione, funziona perfettamente. MA, se lo eseguo come una funzione, ottengo un errore che dice che l'oggetto datas.train non può essere trovato.

Ho provato a passare attraverso la funzione con debug() e l'oggetto esiste chiaramente, ma R dice che non funziona quando eseguo effettivamente la funzione. Se ho appena adattato un modello usando lm() funziona bene, quindi credo che sia un problema con la funzione passo nel ciclo, mentre all'interno di una funzione. (Provate commentando il comando passo, e impostare le previsioni a quelle del modello lineare ordinario.)

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

Qualche idea?

+2

Sembra che ci sia un problema di scoping, dove 'step (lm.1, direzione = direzione, tracce = 0)' 'non riesce a trovare datas.train', come già sapete. Non riesco a vedere la causa del problema da solo. Assegnare 'datas.train' come variabile globale è un work-around, ma non particolarmente soddisfacente (' datas.train << - datas [-xval.idx [[j]],] '). Forse questo dovrebbe essere migrato a StackOverflow? – jthetzel

+0

In particolare, la chiamata a 'ADD1 (in forma, la portata $ aggiungere, scale = scala, tracce = traccia, k = k, ...)' 'in fase()' genera l'errore, dove 'ADD1()' è ' statistiche ::: add1.lm'. – jthetzel

+0

@jthetzel, infatti. In un modo ho risolto un problema simile, ma per un'altra chiamata di funzione all'interno di un ciclo dovevo assegnarlo globalmente. – dcl

risposta

10

Quando è stata creata la formula, lm.cars, in è stato assegnato il proprio ambiente. Questo ambiente rimane con la formula a meno che non lo si modifichi esplicitamente. Pertanto, quando si estrae la formula con la funzione formula, viene incluso l'ambiente originale del modello.

Non so se sto usando la terminologia corretta qui, ma credo che è necessario modificare in modo esplicito l'ambiente per la formula all'interno della vostra funzione:

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

Che funziona. Dovrò dare un'occhiata a questo ambiente. :) Saluti. – dcl

4

Un altro problema che può causare questo è se si passa uno character (stringa vector) a lm anziché formula. vector s non hanno environment, e così quando lm converte il character ad un formula, ha a quanto pare anche non environment invece di essere assegnato automaticamente l'ambiente locale. Se si utilizza quindi un oggetto come pesi che non è nell'argomento dati data.frame, ma si trova nell'argomento della funzione locale, si ottiene un errore . Questo comportamento non è molto facile da capire. Probabilmente è un bug.

Ecco un esempio riproducibile minimo. Questa funzione richiede uno data.frame, due nomi di variabili e un vettore di pesi da utilizzare.

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) 

e test:

> 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 

Si tratta di un bug molto sottile. Se si entra nell'ambiente delle funzioni con browser, si può vedere il vettore dei pesi bene, ma in qualche modo non si trova nella chiamata lm!

Il bug diventa ancora più difficile eseguire il debug se si utilizza il nome weights per la variabile dei pesi. In questo caso, dal momento che lm non riesce a trovare i pesi oggetto, il valore predefinito è la funzione weights() da base di gettando in tal modo un errore ancora più strano:

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

Non chiedetemi quante ore mi ha portato a capisci questo.

Problemi correlati