9

I am trying to write a simple iterative reweighted least squares algorithm in R. I want to pass a function as argument for the calculation of the weights, but unfortunately R complains that the function cannot be found. Any ideas what I am doing wrong? Thanks in advance!

Here is my code:

irls <- function(imodel, wfunc, tol) {

    repeat {
        b0 <- imodel$coef
        imodel <- lm(formula(imodel), weights=wfunc(imodel), data=imodel$model)
        b1 <- imodel$coef
        if(abs((b1-b0)/b0)<=tol) break
    }

    imodel
}

and a silly example to demonstrate the problem

x <- 1:100
y <- x + rnorm(100)
mlm <- lm(y~x-1)
irls(mlm, function(x){rep(1,length(x$fit))},0.001) # error: wfunc not found
nograpes
  • 18,623
  • 1
  • 44
  • 67
linuxfever
  • 3,763
  • 2
  • 19
  • 43
  • 1
    Weird. It seems the problem is in `lm`. When it tries to find the function in the following line: `mf <- eval(mf, parent.frame())` – nograpes Aug 22 '13 at 15:02
  • It may help: http://stackoverflow.com/questions/7027288/error-could-not-find-function-in-r – Fernando Aug 22 '13 at 15:03
  • 1
    I think you're better off defining your function first. `wfunc<-function(x){rep(1,length(x$fit))}` followed with `irls(mlm,wfunc,0.001)` – Carl Witthoft Aug 22 '13 at 15:06
  • I'm not sure what's up with `lm` , but I got your code running by first doing the `wfunc` definition in my other comment, then adding a line inside `irls` that puts the weights inside the model. `imodel$model$weights <- wfunc(imodel)` and then it worked. – Carl Witthoft Aug 22 '13 at 15:16
  • See also http://developer.r-project.org/nonstandard-eval.pdf – hadley Aug 22 '13 at 22:13

3 Answers3

8

The issue comes up with how lm looks for the data. If you change the function to this it seems to work

irls <- function(imodel, wfunc, tol) {

    repeat {
        b0 <- imodel$coef
        dat <- imodel$model
        dat$wts <- wfunc(imodel)
        imodel <- lm(formula(imodel), weights=wts, data=dat)
        b1 <- imodel$coef
        if(abs((b1-b0)/b0)<=tol) break
    }

    imodel
}
Dason
  • 60,663
  • 9
  • 131
  • 148
5

The formula contains the environment of the initial lm call (.GlobalEnv, in this case), in which wfunc was not available. As a workaround, you can replace it with the current environment.

irls <- function(imodel, wfunc, tol) {
  f <- formula(imodel)
  environment(f) <- environment()
  repeat {
    b0 <- imodel$coef
    imodel <- lm(f, weights=wfunc(imodel), data=imodel$model)
    b1 <- imodel$coef
    if(abs((b1-b0)/b0)<=tol) break
  }
  imodel
}
irls(mlm, function(x){rep(1,length(x$fit))},0.001)
Vincent Zoonekynd
  • 31,893
  • 5
  • 69
  • 78
-1

This issue arises because model.frame.default is called inside lm, which evaluates everything in the environment of the formula:

model.frame.default
#function (formula, data = NULL, subset = NULL, na.action = na.fail, 
#    drop.unused.levels = FALSE, xlev = NULL, ...) 
#{
#...
#    env <- environment(formula)
#...
#    extras <- eval(extras, data, env)  <-- this is where you run into a problem
#...

So like the others have suggested, evaluate the function outside of lm.

eddi
  • 49,088
  • 6
  • 104
  • 155