7

I would like to able to call lm within a function and specify the weights variable as an argument passed to the outside function that is then passed to lm. Below is a reproducible example where the call works if it is made to lm outside of a function, but produces the error message Error in eval(expr, envir, enclos) : object 'weightvar' not found when called from within a wrapper function.

olswrapper <- function(form, weightvar, df){
  ols <- lm(formula(form), weights = weightvar, data = df)
  }

df <- mtcars

ols <- lm(mpg ~ cyl + qsec,  weights = gear, data = df)
summary(ols)

ols2 <- olswrapper(mpg ~ cyl + qsec,  weightvar = gear, df = df)
#Produces error: "Error in eval(expr, envir, enclos) : object 'weightvar' not found"
Michael
  • 13,244
  • 23
  • 67
  • 115
  • What do you get when you type `gear`? Nothing, it's not defined globally, and your `olswrapper` doesn't know to look for it inside `df`. One way to make it work is passing `weightvar = "gear"` as a character, then in your `lm` call make `weights = df[weightvar]`. – Gregor Thomas Dec 03 '14 at 00:10
  • possible duplicate of [R : Pass argument to glm inside an R function](http://stackoverflow.com/questions/10858318/r-pass-argument-to-glm-inside-an-r-function) – user20650 Dec 03 '14 at 00:26

3 Answers3

3

Building on the comments, gear isn't defined globally. It works inside the stand-alone lm call as you specify the data you are using, so lm knows to take gear from df.

Howver, gear itself doesn't exist outside that stand-alone lm function. This is shown by the output of gear

> gear
Error: object 'gear' not found

You can pass the gear into the function using df$gear

weightvar <- df$gear
ols <- olswrapper(mpg ~ cyl + qsec, weightvar , df = df)
tospig
  • 7,762
  • 14
  • 40
  • 79
  • Why does this behave differently than passing `df$gear` directly to the wrapper (which produces the same error)? – Michael Dec 03 '14 at 00:27
2

I know I'm late on this, but I believe the previous explanation is incomplete. Declaring weightvar <- df$gear and then passing it in to the function only works because you use weightvar as the name for your weight argument. This is just using weightvar as a global variable. That's why df$gear doesn't work directly. It also doesn't work if you use any name except weightvar.

The reason why it doesn't work is that lm looks for data in two places: the dataframe argument (if specified), and the environment of your formula. In this case, your formula's environment is R_GlobalEnv. (You can test this by running print(str(form)) from inside olswrapper). Thus, lm will only look in the global environment and in df, not the function environment.

edit: In the lm documentation the description of the data argument says: "an optional data frame, list or environment (or object coercible by as.data.frame to a data frame) containing the variables in the model. If not found in data, the variables are taken from environment(formula), typically the environment from which lm is called."

A quick workaround is to say environment(form) <- environment() to change your formula's environment. This won't cause any problems because the data in the formula is in the data frame you specify.

Gabe
  • 1,722
  • 1
  • 11
  • 7
2

eval(substitute(...)) inside a body of a function allows us to employ non-standard evaluation

df <- mtcars
olswrapper <- function(form, weightvar, df)
  eval(substitute(ols <- lm(formula(form), weights = weightvar, data = df)))
  summary(ols)

olswrapper(mpg ~ cyl + qsec,  weightvar = gear, df = df)

More here: http://adv-r.had.co.nz/Computing-on-the-language.html

Styx
  • 9,863
  • 8
  • 43
  • 53
Dominik Lenda
  • 41
  • 1
  • 8