我最近注意到,rlang::sym似乎不适用于匿名函数,我也不明白为什么。这里有一个例子,它非常笨拙和丑陋,但我认为它说明了这一点
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")这定义了一些玩具数据和一个(可怕的)函数,它实际上是根据列名重命名列的。现在,我可以对a和b的不同组合做同样的事情:
d <- tibble(x = c("x1", "x2"),
y = c("x2", "x1"))
d %>% mutate(tmp = map2(x, y, get_it))但是,如果我试图对匿名函数执行完全相同的操作,则无法工作:
d %>% mutate(tmp = map2(x, y, function(a, b){
data %>%
mutate(y1 = !!rlang::sym(a)) %>%
mutate(y2 = !!rlang::sym(b)) %>%
select(y1, y2, val)
}))这在object 'a' not found中失败了,尽管函数完全相同,只是在这里它是匿名的。有人能解释原因吗?
发布于 2018-08-18 06:47:03
问题不是匿名函数,而是!!的操作符优先级。!!的帮助页面声明:
那个!操作符取消引用它的参数。它将立即在周围的上下文中得到的值。
这意味着,当您编写一个复杂的NSE表达式时,例如select在mutate中,将在整个表达式的环境中进行取消引用。正如@lionel所指出的,取消引用优先于其他事情,例如创建匿名函数环境。
在您的示例中,!!取消引用是针对外部mutate()执行的,然后尝试在d中查找列x1,而不是data。有两种可能的解决办法:
1)将涉及!!的表达式拉到独立函数中(正如您在问题中所做的那样):
res1 <- d %>% mutate(tmp = map2(x, y, get_it))2)将!!替换为eval,以延迟表达式评估:
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) #TRUEhttps://stackoverflow.com/questions/51902438
复制相似问题