如何在dplyr 0.7中参数化函数调用?

时间:2017-04-14 16:26:15

标签: r dplyr rlang

dplyr 0.7的发布包括使用dplyr进行major overhaul编程。我仔细阅读了本文档,并试图了解它对我使用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预期的这些情况?期望整个R生态系统向dplyr提供数据似乎是不可行的 - 很多时候我们会得到字符串并且它们必须被转换。

这是一个示例,显示您现在应该做什么,以及我的旧习惯用法不起作用:

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

3 个答案:

答案 0 :(得分:11)

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生成的字符/数字向量或列名:

  

.vars

     

由vars()生成的列列表,或者是字符向量   列名称或列位置的数字向量。

答案 1 :(得分:10)

这是我为自己写的快速而肮脏的参考资料。

rlang::sym

用字符串

表示列名

使用rlang::symssumm_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 将字符串转换为符号对象。

!!

如果您在!!!功能之外使用dplyrrlang::sym,则会收到错误。

rlang::symssummarize_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"))

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而不是quossummarize_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)

然后我们的函数调用是

{{1}}

答案 2 :(得分:6)

如果您想按多列分组,可以使用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

现在,似乎没有一种方法可以将字符串变成混乱。这是一种有效的方法

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