I recently notices that rlang::sym
doesn't seem to work in anonymous functions and I don't understand why. Here an example, it's pretty clumsy and ugly but I think it illustrates the point
require(tidyverse)
data <- tibble(x1 = letters[1:3],
x2 = letters[4:6],
val = 1:3)
get_it <- function(a, b){
data %>%
mutate(y1 = !!rlang::sym(a)) %>%
mutate(y2 = !!rlang::sym(b)) %>%
select(y1, y2, val)
}
get_it("x1", "x2")
This defines some toy data and a (horrible) function that essentially renames the columns based on column names. Now I can do the same thing for different combinations of a and b:
d <- tibble(x = c("x1", "x2"),
y = c("x2", "x1"))
d %>% mutate(tmp = map2(x, y, get_it))
However, if I try to do the exact same thing with an anonymous function it doesn't work:
d %>% mutate(tmp = map2(x, y, function(a, b){
data %>%
mutate(y1 = !!rlang::sym(a)) %>%
mutate(y2 = !!rlang::sym(b)) %>%
select(y1, y2, val)
}))
This fails with object 'a' not found
even though the functions are exactly the same just here it is anonymous. Can anyone explain why?
Note that the following code has rlang::sym()
inside an anonymous function and works as intended:
map2(c("x1","x2"), c("x2","x1"), function(a, b){
data %>%
mutate(y1 = !!rlang::sym(a)) %>%
mutate(y2 = !!rlang::sym(b)) %>%
select(y1, y2, val)})
The original issue has to do with the unquoting operator !!
and its precedence relative to anonymous function creation inside non-standard evaluation (NSE) functions, such as mutate
. Consider a nested data frame
XX <- data_frame( dfs = list(
data_frame( x = letters[1:3], y = 1:3 ),
data_frame( x = letters[4:6], y = 4:6 )) )
If I attempt to select the first column inside each of the internal data frames using
XX %>% mutate( dfs1 = map( dfs, function(df) {
i <- 1
df %>% select(!!i)} ))
I get an object 'i' not found
error, because the !!
unquoting happens relative to the outer mutate
, before the environment for the anonymous function (containing select
) is created. If I move the anonymous function outside of mutate
map( XX$dfs, function(df) {
i <- 1
df %>% select(!!i)} )
it works without problems because map
follows standard evaluation rules and the anonymous function environment is now created before the unquoting.
The help page for !!
states that
The !! operator unquotes its argument. It gets evaluated immediately in the surrounding context.
This implies that when you write a complex NSE expression, such as select
inside mutate
, the unquoting will take place in the environment of the expression as a whole. As pointed out by @lionel in the comments, the unquoting in NSE takes precedence over other things, such as creation of anonymous function environments.
来源:https://stackoverflow.com/questions/51902438/rlangsym-in-anonymous-functions