我正在编写一个自定义函数,该函数应同时使用unquoted
和"quoted"
输入。我可以使用rlang
来实现它。但是当使用colnames
提供"quoted"
参数时,它似乎不起作用。
关于如何解决这个问题,有什么想法吗?
library(tidyverse)
# function
cor_foo <- function(data, x1, x2) {
x1 <- rlang::ensym(x1)
x2 <- rlang::ensym(x2)
df <- dplyr::select(data, {{x1}}, {{x2}})
cor(df %>% dplyr::pull({{x1}}), df %>% dplyr::pull({{x2}}))
}
# works
cor_foo(mtcars, wt, mpg)
#> [1] -0.8676594
# works
cor_foo(mtcars, "wt", "mpg")
#> [1] -0.8676594
# checking strings that will be passed to the function as arguments
colnames(mtcars)[1]
#> [1] "mpg"
colnames(mtcars)[6]
#> [1] "wt"
# doesn't work with these inputs
cor_foo(mtcars, colnames(mtcars)[6], colnames(mtcars)[1])
#> Error: Only strings can be converted to symbols
创建于2019-11-12由reprex包(v0.3.0(
您希望在此处使用enquo
。ensym
没有捕获引用环境,事实上,它试图将colnames(mtcars)[6]
和colnames(mtcars)[1]
本身转换为符号,这会产生错误,因为它们不是字符串。
如果我们使用enquo
,我们会捕获报价环境,并将其转化为待评估的quosore。你可以用它来检查每个人在做什么:
cor_sym <- function(data, x1) {
x1 <- rlang::ensym(x1)
x1
}
cor_sym(mtcars, colnames(mtcars)[6])
# Run traceback on the error
cor_quo <- function(data, x1) {
x1 <- rlang::enquo(x1)
x1
}
cor_quo(mtcars, colnames(mtcars)[6])
您将看到cor_quo
正在返回一个quosure,并将环境返回为全局。因此,如果我们使用enquo
而不是ensym
,则会评估quosore,并为select
和pull
调用提供字符串值。
cor_foo <- function(data, x1, x2) {
x1 <- rlang::enquo(x1)
x2 <- rlang::enquo(x2)
df <- dplyr::select(data, {{x1}}, {{x2}})
cor(df %>% dplyr::pull({{x1}}), df %>% dplyr::pull({{x2}}))
}
cor_foo(mtcars, colnames(mtcars)[6], colnames(mtcars)[1])
你可以找到比我更了解这一点的聪明人来解释这里的差异:当使用dplyr编程时,ensym和enquo之间的区别是什么?
您试图混合标准和非标准评估,这几乎总是导致模糊行为。考虑以下数据变体:
X <- mtcars %>% mutate(`colnames(mtcars)[6]` = 1:n(), `colnames(mtcars)[1]` = 1:n())
在这种情况下,函数应该返回什么?
cor_foo(X, colnames(mtcars)[6], colnames(mtcars)[1])
如果参数2和3是用标准求值(SE(来解释的,那么在传递给cor_foo
之前,它们应该被解析为字符串"mpg"
和"wt"
。另一方面,如果参数2和3要遵循非标准求值(NSE(,那么它们应该被视为已经包含列名的未求值表达式。
我的建议是承诺SE或NSE。rlang::ensym()
通过同时使用字符串和符号来将两者进行一点桥接。但是,它不适用于任意表达式,因为这些表达式是否已经包含列名或需要进行求值才能获得列名是不明确的。
一个可能为您提供所需行为的解决方案是放弃ensym()
而不是enquo()
。注意,{{.}}
是!!enquo(.)
的简写,因此您可以简单地删除ensym
行:
cor_foo <- function(data, x1, x2) {
df <- dplyr::select(data, {{x1}}, {{x2}})
cor(df %>% dplyr::pull({{x1}}), df %>% dplyr::pull({{x2}}))
}
cor_foo(X, "mpg", "wt")
# [1] -0.8676594
cor_foo(X, mpg, wt)
# [1] -0.8676594
cor_foo(X, colnames(mtcars)[6], colnames(mtcars)[1])
# [1] -0.8676594
cor_foo(X, `colnames(mtcars)[6]`, `colnames(mtcars)[1]`)
# [1] 1
请注意,这是对NSE解释的承诺,用户必须使用!!
强制对表达式进行原位评估:
cyl <- colnames(mtcars)[1] # Effectively cyl <- "mpg"
cor_foo(X, cyl, wt)
# [1] 0.7824958
cor_foo(X, !!cyl, wt)
# [1] -0.8676594