1

I'm trying to build a wrapper for lm() with dots ..., with partly success though. It works well just for the first two formula= and data= arguments. But for every argument that follows, it doesn't seem to work and throws an error.

What I'm doing with the result of lm() in the function is not the cause of the issue. It only seems to be related to using dots. So I was able to isolate the problem in this MWE:

lm_fun <- function(...) {
  lm(...)
  # <some other stuff>
}

lm_fun(formula=mpg ~ hp, data=mtcars)
## works fine

mtcars$w <- runif(nrow(mtcars))  ## adding weights
lm_fun(formula=mpg ~ hp, data=mtcars, weights=w)
# Error in eval(substitute(subset), data, env) : 
#   ..3 used in an incorrect context, no ... to look in

lm_fun(formula=mpg ~ hp, data=mtcars, subset=am == 1)
# Error in eval(substitute(subset), data, env) : 
#   ..3 used in an incorrect context, no ... to look in

lm_fun(formula=mpg ~ hp, data=mtcars, subset=am == 1, weights=w)
# Error in eval(extras, data, env) : 
#   ..4 used in an incorrect context, no ... to look in

It seems to be a different issue than that question because I provide the complete set of arguments in the function call and don't add something in the function.

Am I missing something? What's the reason for this behavior, and how can I get this to work properly?

Edit

@user63230 offered a nice workaround below. However, it won't work in an lapply, which would be important for my needs.

lm_fun2 <- function(...) eval(substitute(lm(...)))

fo <- c(mpg ~ hp, mpg ~ am)

lapply(fo, lm_fun, data=mtcars)
# works

lapply(fo, lm_fun2, data=mtcars)
# Error in stats::model.frame(formula = X[[i]], data = mtcars, drop.unused.levels = TRUE) : 
#   object 'X' not found 
jay.sf
  • 60,139
  • 8
  • 53
  • 110

1 Answers1

2

I think the problem is the environment that w etc. is searched for, so if you wrap it in eval(substitute(, the following examples work:

lm_fun <- function(...) {
  result <- eval(substitute(lm(...)))
  result
}
lm_fun(formula = mpg ~ hp, data = mtcars)
# Call:
# lm(formula = mpg ~ hp, data = mtcars)

# Coefficients:
# (Intercept)           hp  
#    30.09886     -0.06823 

lm_fun(formula = mpg ~ hp, data = mtcars, weights = w)
# Call:
# lm(formula = mpg ~ hp, data = mtcars, weights = w)

# Coefficients:
# (Intercept)           hp  
#    31.41313     -0.07391  

lm_fun(formula = mpg ~ hp, data = mtcars, weights = w, na.action = na.omit)
# Call:
# lm(formula = mpg ~ hp, data = mtcars, weights = w, na.action = na.omit)

# Coefficients:
# (Intercept)           hp  
#    31.41313     -0.07391 

lm_fun(formula = mpg ~ hp, data = mtcars, weights = w, na.action = na.omit, 
  singular.ok = F)
# Call:
# lm(formula = mpg ~ hp, data = mtcars, weights = w, na.action = na.omit, 
#     singular.ok = F)

# Coefficients:
# (Intercept)           hp  
#    31.41313     -0.07391 

lm_fun(formula=mpg ~ hp, data=mtcars, subset=am == 1, weights=w)
# Call:
# lm(formula = mpg ~ hp, data = mtcars, subset = am == 1, weights = w)

# Coefficients:
# (Intercept)           hp  
#    32.96650     -0.07072

See here too. The formula is automatically associated with df but weights could be external to df.

user63230
  • 4,095
  • 21
  • 43
  • 1
    The problem might be of a different nature, I remember I also tried `lm_fun(formula=mpg ~ hp, data=mtcars, weights=mtcars$w)`, and the error persists. Regardless +1 your solution seems to work beautifully as a workaround. – jay.sf Jul 01 '21 at 14:51
  • 1
    However this won't work so well with `eval(substitute(`: `fo <- c(mpg ~ hp, mpg ~ am);lapply(fo, lm_fun, data=mtcars)` – jay.sf Jul 01 '21 at 15:02
  • interesting. Although I wasn't able to do it (and ignoring my approach above), i think the solution is to capture `...` in a `list` in the classical `R` [way](https://stackoverflow.com/questions/3057341/how-to-use-rs-ellipsis-feature-when-writing-your-own-function) and then unlist it some way within `lm`. This [question](https://stackoverflow.com/questions/7028385/can-i-remove-an-element-in-dot-dot-dot-and-pass-it-on) follows a similar thought process albeit slight different angle (they're removing an element but you want to unlist the elements). – user63230 Jul 01 '21 at 21:39