2011-11-21 22 views
6

Tengo un problema extraño con R que parece que no puedo resolver.Objeto no encontrado error al pasar la fórmula del modelo a otra función

He intentado escribir una función que realiza la validación cruzada K-fold para un modelo elegido por el procedimiento paso a paso en R. (Soy consciente de los problemas con los procedimientos paso a paso, es puramente para fines de comparación) :)

Ahora el problema es que si defino los parámetros de la función (linmod, k, dirección) y ejecuto el contenido de la función, funciona sin problemas. PERO, si lo ejecuto como una función, me sale un error que dice que el objeto datas.train no se puede encontrar.

He intentado recorrer la función con debug() y el objeto existe claramente, pero R dice que no cuando realmente ejecuto la función. Si acabo de encajar un modelo usando lm() funciona bien, entonces creo que es un problema con la función de paso en el ciclo, mientras está dentro de una función. (Intente comentar el comando paso, y establecer las predicciones a los del modelo lineal 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) 

¿Alguna idea?

+2

Parece haber un problema de scoping, donde 'step (lm.1, direction = direction, trace = 0)' no puede encontrar 'datas.train', como ya sabe. No puedo ver la causa del problema yo mismo. Asignar 'datas.train' como una variable global es una solución temporal, pero no particularmente satisfactoria (' datas.train << - datas [-xval.idx [[j]],] '). Quizás esto debería migrarse a StackOverflow? – jthetzel

+0

Específicamente, la llamada a 'add1 (ajuste, alcance $ add, scale = scale, trace = trace, k = k, ...)' en 'step()' arroja el error, donde 'add1()' es ' stats ::: add1.lm'. – jthetzel

+0

@jthetzel, De hecho. Una forma de resolver un problema similar, pero para otra llamada de función dentro de un bucle era asignarlo globalmente. – dcl

Respuesta

10

Cuando creó su fórmula, lm.cars, se le asignó su propio entorno. Este entorno permanece con la fórmula a menos que lo cambie explícitamente. Por lo tanto, cuando extrae la fórmula con la función formula, se incluye el entorno original del modelo.

No sé si estoy usando la terminología correcta aquí, pero creo que es necesario cambiar de forma explícita el entorno de la fórmula dentro de su función:

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

Eso funciona. Tendré que mirar en este entorno cosas. :) Saludos. – dcl

4

Otro problema que puede causar esto es si se pasa character (cadena vector) a lm en lugar de formula. vector s no tiene environment, por lo que cuando lm convierte character en formula, aparentemente tampoco tiene environment en lugar de asignarse automáticamente al entorno local. Si uno utiliza un objeto como ponderaciones que no está en el argumento de datos data.frame, pero está en el argumento de función local, se obtiene un error not found. Este comportamiento no es muy fácil de entender. Es probable que sea un error.

Aquí hay un ejemplo reproducible mínimo. Esta función toma un data.frame, dos nombres de variables y un vector de pesos para usar.

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) 

Y prueba:

> 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 

Es un error muy sutil. Si uno entra en el entorno de funciones con browser, uno puede ver el vector de ponderaciones bien, pero de alguna manera no se encuentra en la llamada lm!

El error se vuelve aún más difícil de depurar si se usó el nombre weights para la variable de ponderaciones. En este caso, puesto que lm no puede encontrar los pesos objeto, el valor predeterminado es la función de weights()base de arrojando así un error aún más extraño:

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

No me preguntes cuántas horas me llevó a resolver esto.

Cuestiones relacionadas