将多个调用参数传递给 dplyr 自定义函数中的形式参数而不使用“...”

Pass multiple calling arguments to a formal argument in dplyr custom function without using "..."

为了使自定义函数灵活地接收每个形式参数的一个或多个调用参数,我目前依赖于“...”:

library(dplyr)

foo <- function(data, ..., dv){
  groups <- enquos(...)
  dv <- enquo(dv)
  data %>% 
    group_by(!!!groups) %>% 
    summarise(group_mean = mean(!!dv))
}

mtcars %>% foo(am, dv = mpg)
mtcars %>% foo(vs, am, dv = mpg)

但是“...”模糊了函数的逻辑,它不能用于具有 2 个或更多形式参数且需要多个调用参数的自定义函数。

有没有办法编写上述函数以利用形式参数(例如,"groups")而不是“...”,它可以接收单个矢量名称或矢量名称的矢量作为其参数参数?类似于:

foo <- function(data, groups, dv){
  groups <- enquos(groups)
  dv <- enquo(dv)

  data %>% 
    group_by(!!!groups) %>% 
    summarise(group_mean = mean(!!dv))
}

# Failing code
mtcars %>% foo(groups = c(vs, am), dv = mpg)

请注意,此代码可以运行,但需要用户记住在函数体中使用 quos():

foo <- function(data, groups, dv){
  dv <- enquo(dv)

  data %>% 
    group_by(!!!groups) %>% 
    summarise(group_mean = mean(!!dv))
}

mtcars %>% foo(groups = quos(vs, am), dv = mpg)

我更愿意在函数体中依赖 enquos() 。

我们可以把...放在最后

foo <- function(data,  dv, ...){
   groups <- enquos(...)
   dv <- enquo(dv)
   data %>% 
     group_by(!!!groups) %>% 
     summarise(group_mean = mean(!!dv))
  }

如果我们想传递 'group' 的 vector,那么一个选项是 group_by_at

foo <- function(data, groups, dv){
  dv <- enquo(dv)

  data %>% 
     group_by_at(vars(groups)) %>% 
     summarise(group_mean = mean(!!dv))
  }

mtcars %>% 
    foo(groups = c("vs", "am"), dv = mpg)
# A tibble: 4 x 3
# Groups:   vs [?]
#     vs    am group_mean
#  <dbl> <dbl>      <dbl>
#1     0     0       15.0
#2     0     1       19.8
#3     1     0       20.7
#4     1     1       28.4

如果我们想通过 c 传递不带引号的表达式,一个选项是将其转换为表达式,然后对其求值

foo <- function(data, groups, dv){

 groups <- as.list(rlang::enexpr(groups))[-1]
 dv <- enquo(dv)
   data %>% 
      group_by(!!! groups) %>% 
      summarise(group_mean = mean(!!dv))
 }

mtcars %>% 
      foo(groups = c(vs, am), dv = mpg)
# A tibble: 4 x 3
# Groups:   vs [?]
#     vs    am group_mean
#  <dbl> <dbl>      <dbl>
#1     0     0       15.0
#2     0     1       19.8
#3     1     0       20.7
#4     1     1       28.4

或者正如@Joe 在评论中提到的那样,enquo 也应该与 group_by_at

一起使用
foo <- function(data, groups, dv){
   dv <- enquo(dv) 
   groups <- enquos(groups) 
   data %>% 
        group_by_at(vars(!!!groups)) %>% 
        summarise(group_mean = mean(!!dv))
   } 

mtcars %>% 
     foo(groups = c(vs, am), dv = mpg)
# A tibble: 4 x 3
# Groups:   vs [?]
#     vs    am group_mean
#  <dbl> <dbl>      <dbl>
#1     0     0       15.0
#2     0     1       19.8
#3     1     0       20.7
#4     1     1       28.4