Rei*_*ica 4 group-by r function 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
Run Code Online (Sandbox Code Playgroud)
目前我可以调用MEAN_OUT('Type1')orMEAN_OUT('Type2')这些给我按这些参数变量中的任何一个分组的正确摘要。但是,我还希望能够调用MEAN_OUT(c('Type1','Type2'))以获取对两个变量进行分组的摘要。您可以在dplyr::group_by函数中执行此操作,但是当此材料包含在我的函数中时,我无法弄清楚如何执行此操作。如果我使用我的当前函数(如上所示)尝试按两个变量分组,我会收到以下错误:
MEAN_OUT(c('Type1','Type2'))
Error: Only strings can be converted to symbols
Run Code Online (Sandbox Code Playgroud)
这将是更好地使用syms,如果目的是要传递多个分组变量作为vector
library(dplyr)
library(rlang)
MEAN_OUT <- function(VARS) {
DF %>%
group_by(!!! syms(VARS)) %>%
summarise(Mean = mean(Output)) %>%
as.data.frame()
}
Run Code Online (Sandbox Code Playgroud)
但是,我们可以利用group_by_at可以将字符串作为输入的 避免syms和 评估 ( !!!)
MEAN_OUT2 <- function(VARS) {
DF %>%
group_by_at(VARS) %>%
summarise(Mean = mean(Output)) %>%
as.data.frame()
}
Run Code Online (Sandbox Code Playgroud)
-测试
identical(MEAN_OUT('Type1'), MEAN_OUT2('Type1'))
#[1] TRUE
identical(MEAN_OUT(c('Type1', 'Type2')), MEAN_OUT2(c('Type1', 'Type2')))
#[1] TRUE
Run Code Online (Sandbox Code Playgroud)
除了作为带引号的字符串传递之外,还有其他选项可以作为 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
Run Code Online (Sandbox Code Playgroud)
或者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
Run Code Online (Sandbox Code Playgroud)
小智 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
Run Code Online (Sandbox Code Playgroud)