我在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
答案 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