使用rlang允许参数列表,然后在自定义函数中使用它

时间:2018-02-18 01:00:35

标签: r dplyr tidyverse rlang

我正在编写一个自定义函数,我希望其中一个参数获取变量列表。我已设法使用rlang以及对...的一些初步了解,以便在函数中正确阅读此列表。但我不知道如何将此列表作为参数分配给另一个函数(如dplyr::group_by)。我完全可以重复下面的例子以及我想要的最终结果。

# loading the needed libraries
library(dplyr)
library(rlang)
library(datasets)

# defining the custom function
prac.fn <- function(data, vars = ..., measure) {
  # getting the dataframe ready
  df <-
    dplyr::select(.data = data,
                  !!rlang::enquo(vars),
                  !!rlang::enquo(measure))
  # print to see if all variables are included
  print(head(df))

  # summarize by specified grouping variables
  df %>%
    dplyr::group_by(.data = ., c(!!rlang::enquo(vars))) %>%
    dplyr::summarise(mean = mean(!!rlang::enquo(measure)))

}

# use the function (doesn't work)
prac.fn(data = mtcars,
        vars = c(cyl, am),
        measure = wt)
#>                   cyl am    wt
#> Mazda RX4           6  1 2.620
#> Mazda RX4 Wag       6  1 2.875
#> Datsun 710          4  1 2.320
#> Hornet 4 Drive      6  0 3.215
#> Hornet Sportabout   8  0 3.440
#> Valiant             6  0 3.460
#> Error in mutate_impl(.data, dots): Column `c(c(cyl, am))` must be length 32 (the number of rows) or one, not 64

# output I want
mtcars %>%
  dplyr::group_by(cyl, am) %>%
  dplyr::summarise(mean = mean(wt))
#> # A tibble: 6 x 3
#> # Groups:   cyl [?]
#>     cyl    am  mean
#>   <dbl> <dbl> <dbl>
#> 1  4.00  0     2.94
#> 2  4.00  1.00  2.04
#> 3  6.00  0     3.39
#> 4  6.00  1.00  2.76
#> 5  8.00  0     4.10
#> 6  8.00  1.00  3.37

reprex package(v0.2.0)创建于2018-02-17。

1 个答案:

答案 0 :(得分:2)

group_by中,在将'vars'转换为quosure(enquo)之后,使用quo_squash展开表达式,将其转换为list({{1} })并删除第一个元素即。 as.list,然后c对其进行评估

!!!

-testing

prac.fn <- function(data,  vars, measure) {
    data %>%
        select(!!rlang::enquo(vars),
         !!rlang::enquo(measure)) %>%
        dplyr::group_by(!!! as.list(quo_squash(rlang::enquo(vars)))[-1]) %>%
        dplyr::summarise(mean = mean(!!rlang::enquo(measure)))      

 }

检查更多组

prac.fn(data = mtcars,
        vars = c(cyl, am),
         measure = wt)
# A tibble: 6 x 3
# Groups: cyl [?]
#    cyl    am  mean
#  <dbl> <dbl> <dbl>
#1  4.00  0     2.94
#2  4.00  1.00  2.04
#3  6.00  0     3.39
#4  6.00  1.00  2.76
#5  8.00  0     4.10
#6  8.00  1.00  3.37

目前尚不清楚OP是否总是希望prac.fn(data = mtcars, vars = c(cyl, am, gear), measure = wt) # A tibble: 10 x 4 # Groups: cyl, am [?] # cyl am gear mean # <dbl> <dbl> <dbl> <dbl> # 1 4.00 0 3.00 2.46 # 2 4.00 0 4.00 3.17 # 3 4.00 1.00 4.00 2.11 # 4 4.00 1.00 5.00 1.83 # 5 6.00 0 3.00 3.34 # 6 6.00 0 4.00 3.44 # 7 6.00 1.00 4.00 2.75 # 8 6.00 1.00 5.00 2.77 # 9 8.00 0 3.00 4.10 #10 8.00 1.00 5.00 3.37 用于c()参数,即如果存在单个分组变量,则该函数在传递参数的行为相同时有效

vars

但是,如果我们必须改变行为,即prac.fn(data = mtcars, vars = c(cyl), measure = wt) #<quosure> # expr: ^c(cyl) # env: global # A tibble: 3 x 2 # cyl mean # <dbl> <dbl> #1 4.00 2.29 #2 6.00 3.12 #3 8.00 4.00 而没有vars = cyl,则需要用c()声明来解决,即

if/else

-testing

prac.fnN <- function(data,  vars, measure) {
    vars <- as.list(quo_squash(enquo(vars)))
    vars <- if(length(vars) ==1) vars else vars[-1]
    data %>%
        select(!!! vars,
         !!rlang::enquo(measure)) %>%
        dplyr::group_by(!!! vars) %>%
        dplyr::summarise(mean = mean(!!rlang::enquo(measure))) 


 }

除了上述方法之外,自然选项是将参数传递为prac.fnN(data = mtcars, vars = cyl, measure = wt) # A tibble: 3 x 2 # cyl mean # <dbl> <dbl> #1 4.00 2.29 #2 6.00 3.12 #3 8.00 4.00 prac.fnN(data = mtcars, vars = c(cyl), measure = wt) # A tibble: 3 x 2 # cyl mean # <dbl> <dbl> #1 4.00 2.29 #2 6.00 3.12 #3 8.00 4.00 prac.fnN(data = mtcars, vars = c(cyl, am), measure = wt) # A tibble: 6 x 3 # Groups: cyl [?] # cyl am mean # <dbl> <dbl> <dbl> #1 4.00 0 2.94 #2 4.00 1.00 2.04 #3 6.00 0 3.39 #4 6.00 1.00 2.76 #5 8.00 0 4.10 #6 8.00 1.00 3.37 ,然后我们就不必考虑quos/quo和其他enquo

if/else

-testing

prac.fnQ <- function(data,  vars, measure) {
   stopifnot(is_quosures(vars)) 
   stopifnot(is_quosure(measure))

    data %>%
        select(!!! vars, !! measure) %>%
        dplyr::group_by(!!! vars) %>%
        dplyr::summarise(mean = mean(!! measure))   

 }

如果我们还需要检查'度量'变量(假设我们有多个'度量'变量)是prac.fnQ(data = mtcars, vars = quos(cyl, am), measure = quo(wt)) # A tibble: 6 x 3 # Groups: cyl [?] # cyl am mean # <dbl> <dbl> <dbl> #1 4.00 0 2.94 #2 4.00 1.00 2.04 #3 6.00 0 3.39 #4 6.00 1.00 2.76 #5 8.00 0 4.10 #6 8.00 1.00 3.37

numeric