如何参数化 dplyr 0.7 中的函数调用?
How to parametrize function calls in dplyr 0.7?
dplyr 0.7 的发布包括 major overhaul 使用 dplyr 进行编程。我仔细阅读了这份文件,并试图了解它将如何影响我对 dplyr 的使用。
这是我在使用 dplyr 构建报告和聚合函数时常用的习惯用法:
my_report <- function(data, grouping_vars) {
data %>%
group_by_(.dots=grouping_vars) %>%
summarize(x_mean=mean(x), x_median=median(x), ...)
}
这里,grouping_vars
是一个字符串向量。
我喜欢这个习惯用法,因为我可以从其他地方传入字符串向量,比如文件或 Shiny 应用程序的反应式 UI,但对于交互式工作来说也不错。
但是,在新的 programming with dplyr vignette 中,我没有看到有关如何使用新的 dplyr 完成此类操作的示例。我只看到传递字符串不再是正确方法的示例,我必须改用 quosures。
我很高兴采用 quosures,但是我如何从字符串中得到 dplyr 期望的 quosures 呢?期望整个 R 生态系统为 dplyr 提供 quosures 似乎不可行 - 很多时候我们要获取字符串并且必须对其进行转换。
下面是一个示例,展示了您现在应该做什么,以及我的老习惯如何不起作用:
library(dplyr)
grouping_vars <- quo(am)
mtcars %>%
group_by(!!grouping_vars) %>%
summarise(mean_cyl=mean(cyl))
#> # A tibble: 2 × 2
#> am mean_cyl
#> <dbl> <dbl>
#> 1 0 6.947368
#> 2 1 5.076923
grouping_vars <- "am"
mtcars %>%
group_by(!!grouping_vars) %>%
summarise(mean_cyl=mean(cyl))
#> # A tibble: 1 × 2
#> `"am"` mean_cyl
#> <chr> <dbl>
#> 1 am 6.1875
如果你想按可能不止一列进行分组,你可以使用quos
grouping_vars <- quos(am, gear)
mtcars %>%
group_by(!!!grouping_vars) %>%
summarise(mean_cyl=mean(cyl))
# am gear mean_cyl
# <dbl> <dbl> <dbl>
# 1 0 3 7.466667
# 2 0 4 5.000000
# 3 1 4 4.500000
# 4 1 5 6.000000
目前,似乎没有将字符串转换为 quos 的好方法。这是一种可行的方法
cols <- c("am","gear")
grouping_vars <- rlang::parse_quosures(paste(cols, collapse=";"))
mtcars %>%
group_by(!!!grouping_vars) %>%
summarise(mean_cyl=mean(cyl))
# am gear mean_cyl
# <dbl> <dbl> <dbl>
# 1 0 3 7.466667
# 2 0 4 5.000000
# 3 1 4 4.500000
# 4 1 5 6.000000
dplyr
将有一个专门的 group_by 函数 group_by_at
来处理多个分组变量。使用 _at
家族的新成员会容易得多:
# using the pre-release 0.6.0
cols <- c("am","gear")
mtcars %>%
group_by_at(.vars = cols) %>%
summarise(mean_cyl=mean(cyl))
# Source: local data frame [4 x 3]
# Groups: am [?]
#
# am gear mean_cyl
# <dbl> <dbl> <dbl>
# 1 0 3 7.466667
# 2 0 4 5.000000
# 3 1 4 4.500000
# 4 1 5 6.000000
.vars
参数接受由 vars
生成的 character/numeric 向量或列名称:
.vars
A list of columns generated by vars(), or a character vector of
column names, or a numeric vector of column positions.
这是我为自己写的快速而肮脏的参考。
# install.packages("rlang")
library(tidyverse)
dat <- data.frame(cat = sample(LETTERS[1:2], 50, replace = TRUE),
cat2 = sample(LETTERS[3:4], 50, replace = TRUE),
value = rnorm(50))
用字符串表示列名
使用 rlang::sym
和 rlang::syms
将字符串转换为符号对象。
summ_var <- "value"
group_vars <- c("cat", "cat2")
summ_sym <- rlang::sym(summ_var) # capture a single symbol
group_syms <- rlang::syms(group_vars) # creates list of symbols
dat %>%
group_by(!!!group_syms) %>% # splice list of symbols into a function call
summarize(summ = sum(!!summ_sym)) # slice single symbol into call
如果您在 dplyr
函数之外使用 !!
或 !!!
,您将得到一个错误。
rlang::sym
和 rlang::syms
在函数内部的用法相同。
summarize_by <- function(df, summ_var, group_vars) {
summ_sym <- rlang::sym(summ_var)
group_syms <- rlang::syms(group_vars)
df %>%
group_by(!!!group_syms) %>%
summarize(summ = sum(!!summ_sym))
}
然后我们可以使用字符串参数调用 summarize_by
。
summarize_by(dat, "value", c("cat", "cat2"))
对 column/variable 个名称使用非标准评估
summ_quo <- quo(value) # capture a single variable for NSE
group_quos <- quos(cat, cat2) # capture list of variables for NSE
dat %>%
group_by(!!!group_quos) %>% # use !!! with both quos and rlang::syms
summarize(summ = sum(!!summ_quo)) # use !! both quo and rlang::sym
内部函数使用 enquo
而不是 quo
。 quos
还好吧!?
summarize_by <- function(df, summ_var, ...) {
summ_quo <- enquo(summ_var) # can only capture a single value!
group_quos <- quos(...) # captures multiple values, also inside functions!?
df %>%
group_by(!!!group_quos) %>%
summarize(summ = sum(!!summ_quo))
}
然后我们的函数调用是
summarize_by(dat, value, cat, cat2)
dplyr 0.7 的发布包括 major overhaul 使用 dplyr 进行编程。我仔细阅读了这份文件,并试图了解它将如何影响我对 dplyr 的使用。
这是我在使用 dplyr 构建报告和聚合函数时常用的习惯用法:
my_report <- function(data, grouping_vars) {
data %>%
group_by_(.dots=grouping_vars) %>%
summarize(x_mean=mean(x), x_median=median(x), ...)
}
这里,grouping_vars
是一个字符串向量。
我喜欢这个习惯用法,因为我可以从其他地方传入字符串向量,比如文件或 Shiny 应用程序的反应式 UI,但对于交互式工作来说也不错。
但是,在新的 programming with dplyr vignette 中,我没有看到有关如何使用新的 dplyr 完成此类操作的示例。我只看到传递字符串不再是正确方法的示例,我必须改用 quosures。
我很高兴采用 quosures,但是我如何从字符串中得到 dplyr 期望的 quosures 呢?期望整个 R 生态系统为 dplyr 提供 quosures 似乎不可行 - 很多时候我们要获取字符串并且必须对其进行转换。
下面是一个示例,展示了您现在应该做什么,以及我的老习惯如何不起作用:
library(dplyr)
grouping_vars <- quo(am)
mtcars %>%
group_by(!!grouping_vars) %>%
summarise(mean_cyl=mean(cyl))
#> # A tibble: 2 × 2
#> am mean_cyl
#> <dbl> <dbl>
#> 1 0 6.947368
#> 2 1 5.076923
grouping_vars <- "am"
mtcars %>%
group_by(!!grouping_vars) %>%
summarise(mean_cyl=mean(cyl))
#> # A tibble: 1 × 2
#> `"am"` mean_cyl
#> <chr> <dbl>
#> 1 am 6.1875
如果你想按可能不止一列进行分组,你可以使用quos
grouping_vars <- quos(am, gear)
mtcars %>%
group_by(!!!grouping_vars) %>%
summarise(mean_cyl=mean(cyl))
# am gear mean_cyl
# <dbl> <dbl> <dbl>
# 1 0 3 7.466667
# 2 0 4 5.000000
# 3 1 4 4.500000
# 4 1 5 6.000000
目前,似乎没有将字符串转换为 quos 的好方法。这是一种可行的方法
cols <- c("am","gear")
grouping_vars <- rlang::parse_quosures(paste(cols, collapse=";"))
mtcars %>%
group_by(!!!grouping_vars) %>%
summarise(mean_cyl=mean(cyl))
# am gear mean_cyl
# <dbl> <dbl> <dbl>
# 1 0 3 7.466667
# 2 0 4 5.000000
# 3 1 4 4.500000
# 4 1 5 6.000000
dplyr
将有一个专门的 group_by 函数 group_by_at
来处理多个分组变量。使用 _at
家族的新成员会容易得多:
# using the pre-release 0.6.0
cols <- c("am","gear")
mtcars %>%
group_by_at(.vars = cols) %>%
summarise(mean_cyl=mean(cyl))
# Source: local data frame [4 x 3]
# Groups: am [?]
#
# am gear mean_cyl
# <dbl> <dbl> <dbl>
# 1 0 3 7.466667
# 2 0 4 5.000000
# 3 1 4 4.500000
# 4 1 5 6.000000
.vars
参数接受由 vars
生成的 character/numeric 向量或列名称:
.vars
A list of columns generated by vars(), or a character vector of column names, or a numeric vector of column positions.
这是我为自己写的快速而肮脏的参考。
# install.packages("rlang")
library(tidyverse)
dat <- data.frame(cat = sample(LETTERS[1:2], 50, replace = TRUE),
cat2 = sample(LETTERS[3:4], 50, replace = TRUE),
value = rnorm(50))
用字符串表示列名
使用 rlang::sym
和 rlang::syms
将字符串转换为符号对象。
summ_var <- "value"
group_vars <- c("cat", "cat2")
summ_sym <- rlang::sym(summ_var) # capture a single symbol
group_syms <- rlang::syms(group_vars) # creates list of symbols
dat %>%
group_by(!!!group_syms) %>% # splice list of symbols into a function call
summarize(summ = sum(!!summ_sym)) # slice single symbol into call
如果您在 dplyr
函数之外使用 !!
或 !!!
,您将得到一个错误。
rlang::sym
和 rlang::syms
在函数内部的用法相同。
summarize_by <- function(df, summ_var, group_vars) {
summ_sym <- rlang::sym(summ_var)
group_syms <- rlang::syms(group_vars)
df %>%
group_by(!!!group_syms) %>%
summarize(summ = sum(!!summ_sym))
}
然后我们可以使用字符串参数调用 summarize_by
。
summarize_by(dat, "value", c("cat", "cat2"))
对 column/variable 个名称使用非标准评估
summ_quo <- quo(value) # capture a single variable for NSE
group_quos <- quos(cat, cat2) # capture list of variables for NSE
dat %>%
group_by(!!!group_quos) %>% # use !!! with both quos and rlang::syms
summarize(summ = sum(!!summ_quo)) # use !! both quo and rlang::sym
内部函数使用 enquo
而不是 quo
。 quos
还好吧!?
summarize_by <- function(df, summ_var, ...) {
summ_quo <- enquo(summ_var) # can only capture a single value!
group_quos <- quos(...) # captures multiple values, also inside functions!?
df %>%
group_by(!!!group_quos) %>%
summarize(summ = sum(!!summ_quo))
}
然后我们的函数调用是
summarize_by(dat, value, cat, cat2)