问题
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?
回答1:
The issue is not anonymous functions, but the operator precedence of !!
. 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, the unquoting then takes precedence over other things, such as creation of anonymous function environments.
In your case the !!
unquoting is done with respect to the outer mutate()
, which then attempts to find column x1
inside d
, not data
. There are two possible solutions:
1) Pull the expression involving !!
into a standalone function (as you've done in your question):
res1 <- d %>% mutate(tmp = map2(x, y, get_it))
2) Replace !!
with eval
to delay expression evaluation:
res2 <- d %>% mutate(tmp = map2(x, y, function(a, b){
data %>%
mutate(y1 = eval(rlang::sym(a))) %>%
mutate(y2 = eval(rlang::sym(b))) %>%
select(y1, y2, val)
}))
identical(res1, res2) #TRUE
来源:https://stackoverflow.com/questions/51902438/rlangsym-in-anonymous-functions