23

I was looking at this example that uses map. Here it is:

mtcars %>%
  split(.$cyl) %>% # from base R
  map(~ lm(mpg ~ wt, data = .))

What is the meaning of the first tilde in map(~ lm...? That is, how does R interpret the first tilde? (I understand that the second tilde indicates a function...). Another way of asking is, why doesn't the following work?

mtcars %>%
  split(.$cyl) %>% # from base R
  map(lm(mpg ~ wt, data = .))
iehrlich
  • 3,572
  • 4
  • 34
  • 43
CPak
  • 13,260
  • 3
  • 30
  • 48

1 Answers1

37

As per the map help documentation, map needs a function but it also accepts a formula, character vector, numeric vector, or list, the latter of which are converted to functions.

The ~ operator in R creates formula. So ~ lm(mpg ~ wt, data = .) is a formula. Formulas are useful in R because they prevent immediate evaluation of symbols. For example you can define

x <- ~f(a+b)

without f, a or b being defined anywhere. In this case ~ lm(mpg ~ wt, data = .) is basically a shortcut for function(x) {lm(mpg ~ wt, data = x)} because map can change the value of . in the formula as needed.

Without the tilde, lm(mpg ~ wt, data = .) is just an expression or call in R that's evaluated immediately. The . wouldn't be defined at the time that's called and map can't convert that into a function.

You can turn these formulas into functions outside of the map() with purrr::as_mapper() function. For example

myfun <- as_mapper(~lm(mpg ~ wt, data = .))
myfun(mtcars)
# Call:
# lm(formula = mpg ~ wt, data = .)
# 
# Coefficients:
# (Intercept)           wt  
#      37.285       -5.344  

myfun
# <lambda>
# function (..., .x = ..1, .y = ..2, . = ..1) 
# lm(mpg ~ wt, data = .)
# attr(,"class")
# [1] "rlang_lambda_function"

You can see how the . becomes the first parameter that's passed to that function.

MrFlick
  • 195,160
  • 17
  • 277
  • 295