函数在dplyr中为group_by调用变量名-如何在函数中向量化此变量?

时间:2018-09-21 05:58:52

标签: r function group-by dplyr

我在R中创建了一个函数,该函数采用固定的数据帧,并使用dplyr为我提供按所选参数变量分组的摘要统计信息(例如,特定变量的均值)。这是一些显示玩具数据框和我的功能的代码:

#Create data frame for analysis
DF <- data.frame(Type1  = c(0,0,1,1,0,1,1,0,1,0,1,1,1,0),
                 Type2  = c(1,1,1,1,1,1,2,2,2,2,3,3,3,3),
                 Output = c(4,2,7,5,1,1,7,8,3,2,5,4,3,6));

#Inspect the data-frame
DF;

   Type1 Type2 Output
1      0     1      4
2      0     1      2
3      1     1      7
4      1     1      5
5      0     1      1
6      1     1      1
7      1     2      7
8      0     2      8
9      1     2      3
10     0     2      2
11     1     3      5
12     1     3      4
13     1     3      3
14     0     3      6

#Create a function that summarises the mean output grouped by input variable
MEAN_OUT <- function(VAR) { DF %>% group_by(!! sym(VAR)) %>% 
                                   summarise(Mean = mean(Output)) %>% 
                                   as.data.frame(); }

#Call the function grouping by variable 'Type1'
MEAN_OUT('Type1')

  Type1     Mean
1     0 3.714286
2     1 4.444444

此刻,我可以调用MEAN_OUT('Type1')MEAN_OUT('Type2'),这些给了我正确的汇总,这些汇总按这些自变量中的任何一个分组。但是,我也希望能够调用MEAN_OUT(c('Type1','Type2'))来获得按两个变量分组的摘要。您可以在dplyr::group_by函数中执行此操作,但是当将此材料包装到函数中时,我无法弄清楚该如何执行。如果我使用当前函数(如上所示)尝试按两个变量进行分组,则会出现以下错误:

MEAN_OUT(c('Type1','Type2'))
Error: Only strings can be converted to symbols

2 个答案:

答案 0 :(得分:4)

如果要传递多个分组变量作为syms,最好使用vector

library(dplyr)
library(rlang)
MEAN_OUT <- function(VARS) { 
                 DF %>% 
                    group_by(!!! syms(VARS)) %>% 
                    summarise(Mean = mean(Output)) %>% 
                    as.data.frame() 
         }

但是,我们可以利用可以将字符串作为输入的group_by_at来避免syms和求值(!!!

MEAN_OUT2 <- function(VARS) {
                DF %>% 
                     group_by_at(VARS) %>% 
                     summarise(Mean = mean(Output)) %>% 
                     as.data.frame()
    }

-测试

identical(MEAN_OUT('Type1'), MEAN_OUT2('Type1'))
#[1] TRUE

identical(MEAN_OUT(c('Type1', 'Type2')), MEAN_OUT2(c('Type1', 'Type2')))
#[1] TRUE

除了传递带引号的字符串外,还有其他选择传递为quosure

MEAN_OUT3 <- function(VARS) {
                    DF %>% 
                        group_by(!!! VARS) %>% 
                               summarise(Mean = mean(Output)) %>% 
                               as.data.frame() 
                                  }

identical(MEAN_OUT('Type1'), MEAN_OUT3(quos(Type1)))
#[1] TRUE
identical(MEAN_OUT(c('Type1', 'Type2')), MEAN_OUT3(quos(Type1, Type2)))
#[1] TRUE

或通过将参数传递为quos来调用函数内的...

MEAN_OUT4 <- function(...) {

                    DF %>% 
                        group_by(!!! quos(...)) %>% 
                               summarise(Mean = mean(Output)) %>% 
                               as.data.frame() 
                                  }

identical(MEAN_OUT('Type1'), MEAN_OUT4(Type1))
#[1] TRUE

identical(MEAN_OUT(c('Type1', 'Type2')), MEAN_OUT4(Type1, Type2))
#[1] TRUE

答案 1 :(得分:1)

@akrun的答案提供了一个可行的解决方案,但是我认为这是将函数参数包装在vars()中的理想情况,将您要分组的变量作为准引号传递,dplyr可以解释该引号而无需任何显式的tidyeval代码在功能的主体中。

library(tidyverse)
#> -- Attaching packages ------------------------------------ tidyverse 1.2.1 --
#> v ggplot2 3.0.0     v purrr   0.2.5
#> v tibble  1.4.2     v dplyr   0.7.6
#> v tidyr   0.8.0     v stringr 1.3.1
#> v readr   1.1.1     v forcats 0.3.0
#> -- Conflicts --------------------------------------- tidyverse_conflicts() --
#> x dplyr::filter() masks stats::filter()
#> x dplyr::lag()    masks stats::lag()
# Create data frame for analysis
dat <- data.frame(
  Type1  = c(0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0),
  Type2  = c(1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3),
  Output = c(4, 2, 7, 5, 1, 1, 7, 8, 3, 2, 5, 4, 3, 6)
)
# using the dplyr::vars() quoting function has 3 main advantages: 
# 1. It makes functions neater
mean_out <- function(.vars) {

  dat %>% 

    # group_by will continue to work for basic selections
    # group_by_at allows for full tidyselect functionality 
    group_by_at(.vars) %>% 
    summarise(mean = mean(Output)) 
}
# 2. It lets us harness the power of tidyselect
mean_out(vars(Type1))
#> # A tibble: 2 x 2
#>   Type1  mean
#>   <dbl> <dbl>
#> 1     0  3.83
#> 2     1  4.38
mean_out(vars(Type1, Type2))
#> # A tibble: 6 x 3
#> # Groups:   Type1 [?]
#>   Type1 Type2  mean
#>   <dbl> <dbl> <dbl>
#> 1     0     1  2.33
#> 2     0     2  5   
#> 3     0     3  6   
#> 4     1     1  4.33
#> 5     1     2  5   
#> 6     1     3  4
mean_out(vars(-Output))
#> # A tibble: 6 x 3
#> # Groups:   Type1 [?]
#>   Type1 Type2  mean
#>   <dbl> <dbl> <dbl>
#> 1     0     1  2.33
#> 2     0     2  5   
#> 3     0     3  6   
#> 4     1     1  4.33
#> 5     1     2  5   
#> 6     1     3  4
mean_out(vars(matches("Type")))
#> # A tibble: 6 x 3
#> # Groups:   Type1 [?]
#>   Type1 Type2  mean
#>   <dbl> <dbl> <dbl>
#> 1     0     1  2.33
#> 2     0     2  5   
#> 3     0     3  6   
#> 4     1     1  4.33
#> 5     1     2  5   
#> 6     1     3  4
# 3. It doesn't demand that we load rlang, since it's built into dplyr