在 tidyeval 中将单个参数作为点传递
Pass a single argument as dots in tidyeval
我试图将 dplyr::filter
包装在一个函数中,当存在多个 filter
条件时,它们将作为向量或列表传递。请参阅这个最小示例:
filter_wrap <- function(x, filter_args) {
filter_args_enquos <- rlang::enquos(filter_args)
dplyr::filter(x, !!!filter_args_enquos)
}
当有一个条件时,我可以让它工作:
data(iris)
message("Single condition works:")
expected <- dplyr::filter(iris, Sepal.Length > 5)
obtained <- filter_wrap(iris, filter_args = Sepal.Length > 5)
stopifnot(identical(expected, obtained))
当我尝试通过多个条件时,我遇到了问题。我原以为 dplyr::filter
调用中的 !!!
运算符会拼接我的参数,但鉴于错误消息,我想我理解错了。
message("Multiple conditions fail:")
expected <- dplyr::filter(iris, Sepal.Length > 5, Petal.Length > 5)
obtained <- filter_wrap(iris, c(Sepal.Length > 5, Petal.Length > 5))
# Error in filter_impl(.data, quo) : Result must have length 150, not 300
# Called from: filter_impl(.data, quo)
stopifnot(identical(expected, obtained))
使用列表确实会更改错误消息:
obtained <- filter_wrap(iris, list(Sepal.Length > 5, Petal.Length > 5))
# Error in filter_impl(.data, quo) :
# Argument 2 filter condition does not evaluate to a logical vector
# Called from: filter_impl(.data, quo)
我不想使用 ...
,因为我的函数会有其他参数,我可能想将点用于其他用途。
如何在将 filter_args
参数传递给 dplyr::filter
时扩展它?
希望我没有理解错,这是一个快速的解决方案:
问题是/是通过组合由 c 组合的逻辑查询,这会产生一个与 x * n 比较一样长的向量。
filter_wrap <- function(x, filter_args) {
filter_args_enquos <- rlang::enquos(filter_args)
LogVec <- rowwise(x) %>% mutate(LogVec = all(!!!filter_args_enquos)) %>%
pull(LogVec)
dplyr::filter(x, LogVec)
}
expected <- dplyr::filter(iris, Sepal.Length > 5, Petal.Length > 5)
obtained <- filter_wrap(iris, c(Sepal.Length > 5, Petal.Length > 5))
stopifnot(identical(expected, obtained))
基本上你的问题是,当你在单个参数上调用 enquos()
时,你还引用了 list()
调用(这是一个单一的调用)。所以基本上你正在创造
filter_args_enquos <- quo(list(Sepal.Length > 5, Petal.Length > 5))
当你打电话时
dplyr::filter(iris, !!!filter_args_enquos)
与
相同
dplyr::filter(iris, list(Sepal.Length > 5, Petal.Length > 5))
这不是有效的 dplyr 语法。 !!!
需要在适当的类似列表的对象上工作,而不是对列表的未评估调用,请注意这会工作
filter_args_enquos <- list(quo(Sepal.Length > 5), quo(Petal.Length > 5))
dplyr::filter(iris, !!!filter_args_enquos)
因为这里我们实际上是在评估列表,并且只引用列表里面的东西。这基本上是 enquos 在使用时创建的对象类型 ...
filter_wrap <- function(x, ...) {
filter_args_enquos <- rlang::enquos(...)
dplyr::filter(x, !!!filter_args_enquos)
}
filter_wrap(iris, Sepal.Length > 5, Petal.Length > 5)
enquos()
函数需要多个参数,而不仅仅是一个列表。这就是为什么它要与 ...
一起使用,因为它会扩展到多个参数。如果你想传入一个列表,你可以编写一个辅助函数来查找这种特殊情况并适当地扩展 quosure。例如
expand_list_quos <- function(x) {
expr <- rlang::quo_get_expr(x)
if (expr[[1]]==as.name("list")) {
expr[[1]] <- as.name("quos")
return(rlang::eval_tidy(expr, env = rlang::quo_get_env(x)))
} else {
return(x)
}
}
然后你就可以使用它了
filter_wrap <- function(x, filter_args) {
filter_args <- expand_list_quos(rlang::enquo(filter_args))
dplyr::filter(x, !!!filter_args)
}
这两个都可以
filter_wrap(iris, Petal.Length > 5)
filter_wrap(iris, list(Sepal.Length > 5, Petal.Length > 5))
但这并不是 enquo
东西 "meant" 的真正使用方式。 ...
方法更为惯用。或者,如果您需要更多控制权,则显式调用 quos()
filter_wrap <- function(x, filter_args) {
dplyr::filter(x, !!!filter_args)
}
filter_wrap(iris, quo(Petal.Length > 5))
filter_wrap(iris, quos(Sepal.Length > 5, Petal.Length > 5))
我试图将 dplyr::filter
包装在一个函数中,当存在多个 filter
条件时,它们将作为向量或列表传递。请参阅这个最小示例:
filter_wrap <- function(x, filter_args) {
filter_args_enquos <- rlang::enquos(filter_args)
dplyr::filter(x, !!!filter_args_enquos)
}
当有一个条件时,我可以让它工作:
data(iris)
message("Single condition works:")
expected <- dplyr::filter(iris, Sepal.Length > 5)
obtained <- filter_wrap(iris, filter_args = Sepal.Length > 5)
stopifnot(identical(expected, obtained))
当我尝试通过多个条件时,我遇到了问题。我原以为 dplyr::filter
调用中的 !!!
运算符会拼接我的参数,但鉴于错误消息,我想我理解错了。
message("Multiple conditions fail:")
expected <- dplyr::filter(iris, Sepal.Length > 5, Petal.Length > 5)
obtained <- filter_wrap(iris, c(Sepal.Length > 5, Petal.Length > 5))
# Error in filter_impl(.data, quo) : Result must have length 150, not 300
# Called from: filter_impl(.data, quo)
stopifnot(identical(expected, obtained))
使用列表确实会更改错误消息:
obtained <- filter_wrap(iris, list(Sepal.Length > 5, Petal.Length > 5))
# Error in filter_impl(.data, quo) :
# Argument 2 filter condition does not evaluate to a logical vector
# Called from: filter_impl(.data, quo)
我不想使用 ...
,因为我的函数会有其他参数,我可能想将点用于其他用途。
如何在将 filter_args
参数传递给 dplyr::filter
时扩展它?
希望我没有理解错,这是一个快速的解决方案: 问题是/是通过组合由 c 组合的逻辑查询,这会产生一个与 x * n 比较一样长的向量。
filter_wrap <- function(x, filter_args) {
filter_args_enquos <- rlang::enquos(filter_args)
LogVec <- rowwise(x) %>% mutate(LogVec = all(!!!filter_args_enquos)) %>%
pull(LogVec)
dplyr::filter(x, LogVec)
}
expected <- dplyr::filter(iris, Sepal.Length > 5, Petal.Length > 5)
obtained <- filter_wrap(iris, c(Sepal.Length > 5, Petal.Length > 5))
stopifnot(identical(expected, obtained))
基本上你的问题是,当你在单个参数上调用 enquos()
时,你还引用了 list()
调用(这是一个单一的调用)。所以基本上你正在创造
filter_args_enquos <- quo(list(Sepal.Length > 5, Petal.Length > 5))
当你打电话时
dplyr::filter(iris, !!!filter_args_enquos)
与
相同dplyr::filter(iris, list(Sepal.Length > 5, Petal.Length > 5))
这不是有效的 dplyr 语法。 !!!
需要在适当的类似列表的对象上工作,而不是对列表的未评估调用,请注意这会工作
filter_args_enquos <- list(quo(Sepal.Length > 5), quo(Petal.Length > 5))
dplyr::filter(iris, !!!filter_args_enquos)
因为这里我们实际上是在评估列表,并且只引用列表里面的东西。这基本上是 enquos 在使用时创建的对象类型 ...
filter_wrap <- function(x, ...) {
filter_args_enquos <- rlang::enquos(...)
dplyr::filter(x, !!!filter_args_enquos)
}
filter_wrap(iris, Sepal.Length > 5, Petal.Length > 5)
enquos()
函数需要多个参数,而不仅仅是一个列表。这就是为什么它要与 ...
一起使用,因为它会扩展到多个参数。如果你想传入一个列表,你可以编写一个辅助函数来查找这种特殊情况并适当地扩展 quosure。例如
expand_list_quos <- function(x) {
expr <- rlang::quo_get_expr(x)
if (expr[[1]]==as.name("list")) {
expr[[1]] <- as.name("quos")
return(rlang::eval_tidy(expr, env = rlang::quo_get_env(x)))
} else {
return(x)
}
}
然后你就可以使用它了
filter_wrap <- function(x, filter_args) {
filter_args <- expand_list_quos(rlang::enquo(filter_args))
dplyr::filter(x, !!!filter_args)
}
这两个都可以
filter_wrap(iris, Petal.Length > 5)
filter_wrap(iris, list(Sepal.Length > 5, Petal.Length > 5))
但这并不是 enquo
东西 "meant" 的真正使用方式。 ...
方法更为惯用。或者,如果您需要更多控制权,则显式调用 quos()
filter_wrap <- function(x, filter_args) {
dplyr::filter(x, !!!filter_args)
}
filter_wrap(iris, quo(Petal.Length > 5))
filter_wrap(iris, quos(Sepal.Length > 5, Petal.Length > 5))