我有一个创建数据框的函数,但更改了进程中的名称。我试图用dplyr quosures处理空列名。我的测试套件如下所示:
dataframe <- data_frame(
a = 1:5,
b = 6:10
)
my_fun <- function(df, col_name, new_var_name = NULL) {
target <- enquo(col_name)
c <- df %>% pull(!!target) * 3 # here may be more complex calculations
# handling NULL name
if (is.null(new_var_name)) {
new_name <- quo(default_name)
} else{
new_name <- enquo(new_name)
}
data_frame(
abc = df %>% pull(!!target),
!!quo_name(new_name) := c
)
}
如果我这样称呼我的功能:
my_fun(dataframe, a)
我按预期获得默认名称:
# A tibble: 5 x 2
abc default_name
<int> <dbl>
1 1 3
2 2 6
3 3 9
4 4 12
5 5 15
如果我试图传递名字,我会收到错误:
my_fun(dataframe, a, NEW_NAME)
Error in my_fun(dataframe, a, NEW_NAME) : object 'NEW_NAME' not found
我哪里错了?
答案 0 :(得分:6)
这个问题与quo
和enquo
返回不同的东西并不真正有关,它实际上是关于在你真正想要之前评估对象。如果您使用browser()
逐步执行函数,则会在if (is.null(new_var_name))
语句中看到错误。
执行is.null(new_var_name)
时,您正在评估作为new_var_name
传递的变量,因此enquo
为时已晚。那是因为is.null
需要查看变量的值而不仅仅是变量名本身。
不评估传递给函数的参数但检查它是否存在missing()
的函数。
my_fun <- function(df, col_name, new_var_name=NULL) {
target <- enquo(col_name)
c <- df %>% pull(!!target) * 3 # here may be more complex calculations
# handling NULL name
if (missing(new_var_name)) {
new_name <- "default_name"
} else{
new_name <- quo_name(enquo(new_var_name))
}
data_frame(
abc = df %>% pull(!!target),
!!new_name := c
)
}
然后你可以运行这两个
my_fun(dataframe, a)
my_fun(dataframe, a, NEW_NAME)
答案 1 :(得分:1)
MrFlick 概述的方法不适用于嵌套函数调用。我们可以改用 rlang::quo_is_null
。
来自 rlang::quo_is_null
的文档:“当缺少的参数被捕获为 quosure 时,无论是通过 enquo() 还是 quos(),它们都会作为空的 quosure 返回”。所以当我们用空的quosures嵌套函数调用时,内部函数中对missing
的调用最终会检查一个空的quosures是否为NULL
,并且总是返回FALSE
,因为它是NULL
的 quosure 而不是 quosure 本身。
我将以下详细函数放在一起以显示正在发生的事情:
library(dplyr)
library(rlang)
f1 <- function(var = NULL) {
print(paste("substitute shows:", paste(substitute(var), collapse = " ")))
print(paste("missing returns:", missing(var)))
enquo_var <- enquo(var)
print(paste("after enquo:", quo_get_expr(enquo_var)))
print(paste("quo_is_null returns:", rlang::quo_is_null(enquo_var)))
rlang::quo_is_null(enquo_var)
}
f2 <- function(var = NULL) {
f1({{var}})
}
f1(Sepal.Length)
f1()
f2(Sepal.Length)
f2() # this is where `missing` fails.
注意:我欢迎对此解释进行更正或补充。非常感谢 mrflick、Lionel Henry 和 Hugh。 See here 相关问题。