首页 文章

将模型公式传递给另一个函数时,找不到对象错误

提问于
浏览
10

我有一个奇怪的问题,我似乎无法解决 .

我已经尝试编写一个函数,对R中逐步过程选择的模型执行K折交叉验证 . (我知道逐步过程的问题,它纯粹用于比较目的):)

现在的问题是,如果我定义函数参数(linmod,k,direction)并运行函数的内容,它可以完美地工作 . 但是,如果我将它作为一个函数运行,我会收到一条错误,指出无法找到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 回答

  • 7

    创建公式时, lm.cars ,in已分配了自己的环境 . 除非您明确更改,否则此环境将保留公式 . 因此,当您使用 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
    
  • 13

    另一个可能导致此问题的问题是,如果将 character (字符串 vector )传递给 lm 而不是 formula . vector 没有 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 找不到权重对象,因此默认为 base 中的函数 weights() ,因此引发了一个更奇怪的错误:

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

    不要问我花了多少小时来弄明白这一点 .

相关问题