What is the meaning of '~' and '.' inside the function map? [duplicate]

What is the meaning of '~' and '.' inside the function map?

Example:

map(dataset1[vector_input], ~ summary(., quantile.type = 5))

The tilda (~) is a decorator that simply means that whatever follows it is a formula. In the case of map() (and most other purrr functions), we see it simply means that whatever follows it will be applied to each element of the first argument of the map() function.

Example

Here are some simple but instructive examples from the ?map docs:

# You can also use an anonymous function
1:10 %>%
  map(function(x) rnorm(10, x))

# Or a formula
1:10 %>%
  map(~ rnorm(10, .x))

Note that these approaches are identical, except the second approach is a little more elegant, hence why you may like to use the ~.


Background

More from ?map:

.x A list or atomic vector.

.f A function, formula, or vector (not necessarily atomic).

If a function, it is used as is.

If a formula, e.g. ~ .x + 2, it is converted to a function. There are three ways to refer to the arguments:

For a single argument function, use .

For a two argument function, use .x and .y

For more arguments, use ..1, ..2, ..3 etc


The ~ is a shorthand notation for an anonymous (lambda) function and arguments. Inside tidyverse the anonymous argument can be written as .x, .y Consider the following cases, and compare their output:

map(1:5, ~ .x + 1) # default
map(1:5, ~ . + 1) 
map(1:5, ~ ..1 + 1) # same as .
map(1:5, ~ ... + 1) # careful with this
map(1:5, function(x) x + 1)
map(1:5, `+`, 1)

one = 1
map(1:5, ~ .x + !!one)

All evaluate to a list of length 5, with values 2 to 6.

However, when the anonymous function is not formatted properly, an error is thrown, here specifically because z (or .z) is not one of .x or .y.

map(1:5, ~ z + 1)
#> Error in .f(.x[[i]], ...) : object 'z' not found

Some functions have more than one placeholder, such as map2. The ?map help page tells us that for the second placeholder we can write .y.

map2(1:5, 5:1, ~ .x * .y)

map relies on as_mapper which in turn calls rlang::as_function. To see what your function is turned into (helpful for debugging), use rlang::as_function(<your (anonymous?) function>).

Note that use of non-standard evaluation inside anonymous functions should be handled with care, as internal handling of arguments may conflict with the forced evaluation. Case in point:

y = 1:10

# these results can be confusing
map(1:5, ~ .x + !!y)
map(1:5, ~x + !!!y

# but this works
map(1:5, ~ .x + {{ y }})

# see the following for why:
rlang::qq_show(map(x, !!y)
#> map(x, <int: 1L, 2L, 3L, 4L, 5L, ...>)
rlang::qq_show(map(x, !!!y))
#> map(x, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L)
rlang::qq_show(map(x, {{ y }})
#> map(x, ^<int: 1L, 2L, 3L, 4L, 5L, ...>)