ELI5: how does rlang work with lapply()?

I'm struggling to understand how to use lapply() in conjunction with rlang. I know how to do this in base R and with the old group_by_(), but I don't understand rlang well enough to make this work.

All I want to do is iterate a function over a list of variable names. I don't care whether the names are quoted or not. But I want to do it in a tidyverse way.

Here is what I've tried:

library(tidyverse)
library(rlang)
my_fun <- function(data, var) {
  my_var <- enquo(var)
  data %>%
    filter((!!my_var) == 1) %>%
    summarize(N = n(), 
              mean_value = mean(mpg, na.rm = TRUE))
}
my_fun(mtcars, am)

I sort of understand why this doesn't work:

# names(mtcars) is already quoted
lapply(names(mtcars), my_fun, data = mtcars)

But how do I lapply() over a list of variable names?

UPDATE: A colleague made this work with the following modifications:

library(tidyverse)
library(rlang)

my_fun <- function(data, var) {
  my_var <- var
  data %>%
    filter(UQS(my_var) == 1) %>%
    summarize(N = n(), 
              mean_value = mean(mpg, na.rm = TRUE))
}
names_prep <- lapply(names(mtcars), parse_quosure)
list_result <- lapply(names_prep, my_fun, data = mtcars)

But:

  • I still don't understand
  • This seems way too hard for what I am trying to do. Is this a more elegant solution?
  • I'm an educator so I need to be able to explain this code to relative novices. How is this approach superior to the base R approach?

P.S. - I know this example doesn't make practical sense.

Does this help?

library(rlang)

vars <- syms(names(mtcars)[1:3])
lapply(vars, function(x) expr(mean(!!x, na.rm = TRUE)))
#> [[1]]
#> mean(mpg, na.rm = TRUE)
#> 
#> [[2]]
#> mean(cyl, na.rm = TRUE)
#> 
#> [[3]]
#> mean(disp, na.rm = TRUE)

If you could outline your real problem a bit more I could provide more hints - but in most cases it's a matter of capturing the variance names correctly, then combining lapply()/map() + expr(). (And if the environment is important, replace expr with quo)

This is the sort of syntax that would be nice to see abstracted into rlang. Specifically, if you took the body of functions that manipulate between a single expr, name, string, quosure and created a version that could handle a list of expr, names, strings, quosures that would really help. Some of the most basic functions like quos and UQS are there but there's no quos_expr.

1 Like