Search code examples
rpurrrtilde

What is meaning of first tilde in purrr::map


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 = .))

Solution

  • 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.