Best way to tell if a formula contains a random effect?

Viewed 56

I have a list of formulas that I would like to fit in a loop using a function. Some of these formulas are random effects models and others are straightforward linear models. I want the function to detect whether the model contains a random effect and if so, use lmer() to fit the model. Otherwise, it should use lm(). Any suggestions on how to check this condition (other than converting the formula to a string and checking for parentheses)? At this stage, they have the same class so I can't just check that. I could also use error handling to catch when lmer() returns an error from a model without a random effect and reroute towards regular lm(), but this also seems unnecessarily messy.

Example below:

fit_models <- function(formula_list) {
  models <- list()
    for(ii in seq_along(formula_list)) {
      if(formula_list[[ii]] is lmer) { # Enter condition here
        print("lmer")
      } else {
        print("lm")
      }
  }
}

f1 <- formula(y ~ x)
f2 <- formula(y ~ 1 + x + (1 + x | z))
formulas <- c(f1, f2)

fit_models(formulas)
3 Answers

I would say

length(lme4::findbars(f))>0

should reliably detect formulas containing a random-effects component (in the lme4 sense).

From the right hand side of a formula for a mixed-effects model, determine the pairs of expressions that are separated by the vertical bar operator.

This is (implicitly) the test that's done in the lme4 code, here ...

The symbols in formulas don't have inherent meanings. A function can reinterpret the symbols to mean whatever they like. So just because there is a "|", that doesn't mean necessarily that that's a formula that has a random effect. That's just how lmer chose to interpret that symbol.

Given that formulas are basically just ordered collections of unevaluated symbols, there's not much more you can do than a basic equality check for a symbol operating on just the formula itself. Rather than a strait up character conversion, you could use all.names. So something like

f2 <- formula(y ~ 1 + x + (1 + x | z))
all.names(f2)
#  [1] "~" "y" "+" "+" "x" "(" "|" "+" "x" "z"
"|" %in% all.names(f2)
# [1] TRUE

This won't be fooled if you have something like formula(`a|b` ~ x) where a|b is a (terrible) column name.

You can just convert the formula to a character and look for the pipe operator |:

f1 <- formula(y ~ x)
f2 <- formula(y ~ 1 + x + (1 + x | z))
formulas <- c(f1, f2)

sapply(formulas, function(x) any(grepl("\\|", as.character(x))))
#> [1] FALSE  TRUE
Related