Функция вызова имен переменных для group_by в dplyr - как мне векторизовать эту переменную в функции? - PullRequest
0 голосов
/ 21 сентября 2018

Я создал функцию в 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 голосов
/ 25 сентября 2018

@ ответ akrun предлагает рабочее решение, но я думаю, что это идеальная ситуация, чтобы обернуть параметры функции в vars (), передав переменные, по которым вы хотите сгруппировать, как квази-цитату, которую dplyr может интерпретировать без какого-либо явного кода приведения в порядок.в теле функции.

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
0 голосов
/ 21 сентября 2018

Было бы лучше использовать 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()
    }

-testing

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
...