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