gpt4 book ai didi

r - 将单个参数作为 tidyeval 中的点传递

转载 作者:行者123 更新时间:2023-12-04 14:54:02 28 4
gpt4 key购买 nike

我正在尝试包装 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 中的运算符call 会拼接我的论点,但鉴于错误消息,我想我理解错了。
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 时的参数?

最佳答案

基本上你的问题是当你打电话时 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东西是“打算”使用的。 ...方法更为惯用。或调用 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))

关于r - 将单个参数作为 tidyeval 中的点传递,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53280843/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com