我一直在研究a number of sources的tidyeval
语义,但得到的结果我无法解释。 / p>
我正在使用mutate_at
和case_when
来变换某些变量,方法是:(1)使用引号检索其名称,(2)使用gsub
修改其名称,以及(3)引用与修改后的名称关联的数据。
在我的最小示例中,我将foo$c
创建为foo$b
的转换,这意味着仅使用foo$a
中的值。步骤(1)和(2)似乎很简单:
library(tidyverse)
library(rlang)
foo <- data.frame(a = 1, b = 2)
foo %>%
mutate_at(vars(c = b),
funs(case_when(
TRUE ~ gsub("b", "a", as_name(quo(.)))
)))
#> a b c
#> 1 1 2 a
foo$c
包含我们要查看的变量的正确名称。我了解我需要使用symbol
将字符串转换为sym()
,然后对其求值。如果我使用简单的mutate()
,!!
和sym()
可以正常工作:
foo %>%
mutate(c := !!sym(gsub("b", "a", as_name(quo(b)))))
#> a b c
#> 1 1 2 1
但是当我在mutate_at(case_when())
内执行此操作时,我没有得到正确的结果:
foo %>%
mutate_at(vars(c = b),
funs(case_when(
TRUE ~ !!sym(gsub("b", "a", as_name(quo(.))))
)))
#> a b c
#> 1 1 2 2
要查看发生了什么,我做了一个简单的打印功能。如果没有!!
,则从打印输出看,好像gsub()
和sym()
都能产生预期的结果:
look <- function(x) {
print(x)
print(typeof(x))
return(x)
}
foo %>%
mutate_at(vars(c = b),
funs(case_when(
TRUE ~ look(sym(look(gsub("b", "a", as_name(quo(.))))))
)))
#> [1] "a"
#> [1] "character"
#> a
#> [1] "symbol"
#> Error in mutate_impl(.data, dots): Evaluation error: object of type 'symbol' is not subsettable.
一旦我将!!
放在前面,打印输出似乎表明gsub()
和sym()
的结果有所不同:
foo %>%
mutate_at(vars(c = b),
funs(case_when(
TRUE ~ !!(look(sym(look(gsub("b", "a", as_name(quo(.)))))))
)))
#> [1] "."
#> [1] "character"
#> .
#> [1] "symbol"
#> [1] "."
#> [1] "character"
#> .
#> [1] "symbol"
#> a b c
#> 1 1 2 2
我不明白添加!!
会如何改变嵌套sym(gsub())
的结果。最后添加新操作不应更改先前/内部的结果。我已经读过!!
是“不是函数调用,而是语法操作”,但我不完全欣赏这种区别或这种区别如何改变结果。
使用eval_tidy
代替!!
似乎可以正常工作,尽管我无法解释原因:
foo %>%
mutate_at(vars(c = b),
funs(case_when(
TRUE ~ eval_tidy(look(sym(look(gsub("b", "a", as_name(quo(.)))))))
)))
#> [1] "a"
#> [1] "character"
#> a
#> [1] "symbol"
#> a b c
#> 1 1 2 1
答案 0 :(得分:1)
仅几条评论:
(a)范围动词目前可以用.
代词的怪异替换来工作。我们正在努力使用应该以更原则性的方式工作的功能(或功能清单)。我建议使用函数或purrr lambdas代替funs()
,这样可以消除一些怪异之处。
(b)!!
运算符与计时有关。对于您而言,是funs()
在替换发生之前立即对其进行处理。
(c)使用范围变体时,最好忘记整洁的评估,并从映射函数的角度进行思考。 这意味着您无权访问当前正在映射的列的名称。我们将来可能会将此功能添加为功能,但目前最好避免这种情况。< / p>
有关最近的相关讨论,另请参见https://github.com/tidyverse/dplyr/issues/4199。