如何将表示表达式的字符串传递给 dplyr 0.7 动词?
How to pass strings denoting expressions to dplyr 0.7 verbs?
我想了解如何将表示表达式的字符串传递给 dplyr,以便字符串中提到的变量被评估为数据框中列上的表达式。该主题的 main vignette 涵盖了传入 quosures,根本不讨论字符串。
很明显quosures在表示表达式时比字符串更安全和清晰,所以我们当然应该避免使用quosures代替字符串。但是,在使用 R 生态系统之外的工具(例如 javascript 或 YAML 配置文件)时,通常需要使用字符串而不是 quosures。
例如,假设我想要一个使用 user/caller 传入的表达式进行分组计数的函数。正如预期的那样,以下代码不起作用,因为 dplyr 使用非标准评估来解释 group_by
.
的参数
library(tidyverse)
group_by_and_tally <- function(data, groups) {
data %>%
group_by(groups) %>%
tally()
}
my_groups <- c('2 * cyl', 'am')
mtcars %>%
group_by_and_tally(my_groups)
#> Error in grouped_df_impl(data, unname(vars), drop): Column `groups` is unknown
在 dplyr 0.5 中,我们将使用标准评估,例如 group_by_(.dots = groups)
来处理这种情况。既然下划线动词被弃用了,我们应该如何在 dplyr 0.7 中做这种事情?
在表达式只是列名的特殊情况下,我们可以使用 的解决方案,但它们不适用于更复杂的表达式,例如 2 * cyl
列名。
请务必注意,在这个简单的示例中,我们可以控制表达式的创建方式。所以传递表达式的最好方法是直接使用 quos()
:
构造和传递 quosures
library(tidyverse)
library(rlang)
group_by_and_tally <- function(data, groups) {
data %>%
group_by(UQS(groups)) %>%
tally()
}
my_groups <- quos(2 * cyl, am)
mtcars %>%
group_by_and_tally(my_groups)
#> # A tibble: 6 x 3
#> # Groups: 2 * cyl [?]
#> `2 * cyl` am n
#> <dbl> <dbl> <int>
#> 1 8 0 3
#> 2 8 1 8
#> 3 12 0 4
#> 4 12 1 3
#> 5 16 0 12
#> 6 16 1 2
但是,如果我们从外部源接收到字符串形式的表达式,我们可以先简单地解析表达式,将它们转换为 quosures:
my_groups <- c('2 * cyl', 'am')
my_groups <- my_groups %>% map(parse_quosure)
mtcars %>%
group_by_and_tally(my_groups)
#> # A tibble: 6 x 3
#> # Groups: 2 * cyl [?]
#> `2 * cyl` am n
#> <dbl> <dbl> <int>
#> 1 8 0 3
#> 2 8 1 8
#> 3 12 0 4
#> 4 12 1 3
#> 5 16 0 12
#> 6 16 1 2
同样,只有当我们从以字符串形式提供的外部源获取表达式时,我们才应该这样做——否则我们应该直接在 R 源代码中进行 quosures。
使用字符串很诱人,但使用表达式几乎总是更好。现在你有了准引用,你可以轻松地以灵活的方式构建表达式:
lhs <- "cyl"
rhs <- "disp"
expr(!!sym(lhs) * !!sym(rhs))
#> cyl * disp
vars <- c("cyl", "disp")
expr(sum(!!!syms(vars)))
#> sum(cyl, disp)
软件包 friendlyeval
可以帮助您:
library(tidyverse)
library(friendlyeval)
group_by_and_tally <- function(data, groups) {
data %>%
group_by(!!!friendlyeval::treat_strings_as_exprs(groups)) %>%
tally()
}
my_groups <- c('2 * cyl', 'am')
mtcars %>%
group_by_and_tally(my_groups)
# # A tibble: 6 x 3
# # Groups: 2 * cyl [?]
# `2 * cyl` am n
# <dbl> <dbl> <int>
# 1 8 0 3
# 2 8 1 8
# 3 12 0 4
# 4 12 1 3
# 5 16 0 12
# 6 16 1 2
我想了解如何将表示表达式的字符串传递给 dplyr,以便字符串中提到的变量被评估为数据框中列上的表达式。该主题的 main vignette 涵盖了传入 quosures,根本不讨论字符串。
很明显quosures在表示表达式时比字符串更安全和清晰,所以我们当然应该避免使用quosures代替字符串。但是,在使用 R 生态系统之外的工具(例如 javascript 或 YAML 配置文件)时,通常需要使用字符串而不是 quosures。
例如,假设我想要一个使用 user/caller 传入的表达式进行分组计数的函数。正如预期的那样,以下代码不起作用,因为 dplyr 使用非标准评估来解释 group_by
.
library(tidyverse)
group_by_and_tally <- function(data, groups) {
data %>%
group_by(groups) %>%
tally()
}
my_groups <- c('2 * cyl', 'am')
mtcars %>%
group_by_and_tally(my_groups)
#> Error in grouped_df_impl(data, unname(vars), drop): Column `groups` is unknown
在 dplyr 0.5 中,我们将使用标准评估,例如 group_by_(.dots = groups)
来处理这种情况。既然下划线动词被弃用了,我们应该如何在 dplyr 0.7 中做这种事情?
在表达式只是列名的特殊情况下,我们可以使用 2 * cyl
列名。
请务必注意,在这个简单的示例中,我们可以控制表达式的创建方式。所以传递表达式的最好方法是直接使用 quos()
:
library(tidyverse)
library(rlang)
group_by_and_tally <- function(data, groups) {
data %>%
group_by(UQS(groups)) %>%
tally()
}
my_groups <- quos(2 * cyl, am)
mtcars %>%
group_by_and_tally(my_groups)
#> # A tibble: 6 x 3
#> # Groups: 2 * cyl [?]
#> `2 * cyl` am n
#> <dbl> <dbl> <int>
#> 1 8 0 3
#> 2 8 1 8
#> 3 12 0 4
#> 4 12 1 3
#> 5 16 0 12
#> 6 16 1 2
但是,如果我们从外部源接收到字符串形式的表达式,我们可以先简单地解析表达式,将它们转换为 quosures:
my_groups <- c('2 * cyl', 'am')
my_groups <- my_groups %>% map(parse_quosure)
mtcars %>%
group_by_and_tally(my_groups)
#> # A tibble: 6 x 3
#> # Groups: 2 * cyl [?]
#> `2 * cyl` am n
#> <dbl> <dbl> <int>
#> 1 8 0 3
#> 2 8 1 8
#> 3 12 0 4
#> 4 12 1 3
#> 5 16 0 12
#> 6 16 1 2
同样,只有当我们从以字符串形式提供的外部源获取表达式时,我们才应该这样做——否则我们应该直接在 R 源代码中进行 quosures。
使用字符串很诱人,但使用表达式几乎总是更好。现在你有了准引用,你可以轻松地以灵活的方式构建表达式:
lhs <- "cyl"
rhs <- "disp"
expr(!!sym(lhs) * !!sym(rhs))
#> cyl * disp
vars <- c("cyl", "disp")
expr(sum(!!!syms(vars)))
#> sum(cyl, disp)
软件包 friendlyeval
可以帮助您:
library(tidyverse)
library(friendlyeval)
group_by_and_tally <- function(data, groups) {
data %>%
group_by(!!!friendlyeval::treat_strings_as_exprs(groups)) %>%
tally()
}
my_groups <- c('2 * cyl', 'am')
mtcars %>%
group_by_and_tally(my_groups)
# # A tibble: 6 x 3
# # Groups: 2 * cyl [?]
# `2 * cyl` am n
# <dbl> <dbl> <int>
# 1 8 0 3
# 2 8 1 8
# 3 12 0 4
# 4 12 1 3
# 5 16 0 12
# 6 16 1 2