使用 dplyr 对多个变量的所有可能组合进行分组

Posted

技术标签:

【中文标题】使用 dplyr 对多个变量的所有可能组合进行分组【英文标题】:Grouping Over All Possible Combinations of Several Variables With dplyr 【发布时间】:2015-05-13 13:29:11 【问题描述】:

给定如下情况

library(dplyr)
myData <- tbl_df(data.frame( var1 = rnorm(100), 
                             var2 = letters[1:3] %>%
                                    sample(100, replace = TRUE) %>%
                                    factor(), 
                             var3 = LETTERS[1:3] %>%
                                    sample(100, replace = TRUE) %>%
                                    factor(), 
                             var4 = month.abb[1:3] %>%
                                    sample(100, replace = TRUE) %>%
                                    factor()))

我想对“myData”进行分组,最终找到按 var2、var3 和 var4 的所有可能组合进行分组的汇总数据。

我可以使用

创建一个列表,其中包含所有可能的变量组合作为字符值
groupNames <- names(myData)[2:4]

myGroups <- Map(combn, 
              list(groupNames), 
              seq_along(groupNames),
              simplify = FALSE) %>%
              unlist(recursive = FALSE)

我的计划是使用 for() 循环为每个变量组合制作单独的数据集,类似于

### This Does Not Work
for (i in 1:length(myGroups))
     assign( myGroups[i]%>%
             unlist() %>%
             paste0(collapse = "")%>%
             paste0("Data"), 
               myData %>% 
               group_by_(lapply(myGroups[[i]], as.symbol)) %>%
               summarise( n = length(var1), 
                             avgVar2 = var2 %>%
                                       mean()))

诚然,我不太擅长列表,而且查找这个问题有点困难,因为 dpyr 更新已经改变了分组的工作方式。

如果有比单独的数据集更好的方法,我很想知道。

当我只按单个变量分组时,我得到了一个类似于上面的循环。

非常感谢任何和所有帮助!谢谢!

【问题讨论】:

我喜欢实现group_by(var1) and group_by(var2) and group_by(var1, var2)等的结果......我想把所有可能的数据分组三个变量的组合(大小为 1、2 和 3)。 对不起,这实际上很清楚,我没有仔细阅读就得出了关于您的目标的结论。 您可以使用 SAS 过程摘要轻松完成此操作。我从没想过我会打这些字。 【参考方案1】:

这似乎令人费解,并且可能有一种方法可以简化或使用do 对其进行修饰,但它确实有效。使用您的myDatamyGroups

results = lapply(myGroups, FUN = function(x) 
    do.call(what = group_by_, args = c(list(myData), x)) %>%
        summarise( n = length(var1), 
                   avgVar1 = mean(var1))
    
)

> results[[1]]
Source: local data frame [3 x 3]

  var2  n     avgVar1
1    a 31  0.38929738
2    b 31 -0.07451717
3    c 38 -0.22522129

> results[[4]]
Source: local data frame [9 x 4]
Groups: var2

  var2 var3  n    avgVar1
1    a    A 11 -0.1159160
2    a    B 11  0.5663312
3    a    C  9  0.7904056
4    b    A  7  0.0856384
5    b    B 13  0.1309756
6    b    C 11 -0.4192895
7    c    A 15 -0.2783099
8    c    B 10 -0.1110877
9    c    C 13 -0.2517602

> results[[7]]
# I won't paste them here, but it has all 27 rows, grouped by var2, var3 and var4.

我将您的 summarise 呼叫更改为平均 var1,因为 var2 不是数字。

【讨论】:

老实说,再次感谢您。我分别计算所有这些摘要,代码变得非常长,我很难跟踪所有数据集。虽然这为我提供了解决当前问题的方法,但它确实向我展示了列表的力量,并让我意识到这是我真正的弱点。 很好的答案。我会在最后打一个do.call(plyr::rbind.fill,results) 很好的答案,另一种方法是使用 .dots 参数而不是 do.call,例如lapply(myGroups, function(g) group_by_(myData, .dots = as.list(g)) %&gt;% ...) 另一种选择是使用group_by_at()one_of() 组合,将过滤器的字符向量传递给它。见***.com/questions/21208801/…【参考方案2】:

我根据@Gregor 的回答和随后的 cmets 创建了一个函数:

library(magrittr)
myData <- tbl_df(data.frame( var1 = rnorm(100), 
                         var2 = letters[1:3] %>%
                                sample(100, replace = TRUE) %>%
                                factor(), 
                         var3 = LETTERS[1:3] %>%
                                sample(100, replace = TRUE) %>%
                                factor(), 
                         var4 = month.abb[1:3] %>%
                                sample(100, replace = TRUE) %>%
                                factor()))

函数combSummarise

combSummarise <- function(data, variables=..., summarise=...)


  # Get all different combinations of selected variables (credit to @Michael)
    myGroups <- lapply(seq_along(variables), function(x) 
    combn(c(variables), x, simplify = FALSE)) %>%
    unlist(recursive = FALSE)

  # Group by selected variables (credit to @konvas)
    df <- eval(parse(text=paste("lapply(myGroups, function(x)
               dplyr::group_by_(data, .dots=x) %>% 
               dplyr::summarize_( \"", paste(summarise, collapse="\",\""),"\"))"))) %>% 
          do.call(plyr::rbind.fill,.)

    groupNames <- c(myGroups[[length(myGroups)]])
    newNames <- names(df)[!(names(df) %in% groupNames)]

    df <- cbind(df[, groupNames], df[, newNames])
    names(df) <- c(groupNames, newNames)
    df


拨打combSummarise

combSummarise (myData, var=c("var2", "var3", "var4"), 
               summarise=c("length(var1)", "mean(var1)", "max(var1)"))

combSummarise (myData, var=c("var2", "var4"), 
               summarise=c("length(var1)", "mean(var1)", "max(var1)"))

combSummarise (myData, var=c("var2", "var4"), 
           summarise=c("length(var1)"))

【讨论】:

【参考方案3】:

受到 Gregor 和 dimitris_ps 答案的启发,我编写了一个 dplyr 风格的函数,该函数对组变量的所有组合运行汇总。

summarise_combo <- function(data, ...) 

  groupVars <- group_vars(data) %>% map(as.name)

  groupCombos <-  map( 0:length(groupVars), ~combn(groupVars, ., simplify=FALSE) ) %>%
    unlist(recursive = FALSE)

  results <- groupCombos %>% 
    map(function(x) data %>% group_by(!!! x) %>% summarise(...) ) %>%
    bind_rows()

  results %>% select(!!! groupVars, everything())

例子

library(tidyverse)
mtcars %>% group_by(cyl, vs) %>% summarise_combo(cyl_n = n(), mean(mpg))

【讨论】:

【参考方案4】:

使用unite新建列是最简单的方法

library(tidyverse)
df = tibble(
  a = c(1,1,2,2,1,1,2,2),
  b = c(3,4,3,4,3,4,3,4),
  val = c(1,2,3,4,5,6,7,8)
)
print(df)#output1
df_2 = unite(df, 'combined_header', a, b, sep='_', remove=FALSE) #remove=F doesn't remove existing columns
print(df_2)#output2

df_2 %>% group_by(combined_header) %>%
  summarize(avg_val=mean(val)) %>% print()#output3
#avg 1_3 = mean(1,5)=3 avg 1_4 = mean(2, 6) = 4

结果

Output:
output1
 a     b   val
  <dbl> <dbl> <dbl>
1     1     3     1
2     1     4     2
3     2     3     3
4     2     4     4
5     1     3     5
6     1     4     6
7     2     3     7
8     2     4     8

output2
  combined_header     a     b   val
  <chr>           <dbl> <dbl> <dbl>
1 1_3                 1     3     1
2 1_4                 1     4     2
3 2_3                 2     3     3
4 2_4                 2     4     4
5 1_3                 1     3     5
6 1_4                 1     4     6
7 2_3                 2     3     7
8 2_4                 2     4     8

output3
combined_header avg_val
  <chr>             <dbl>
1 1_3                   3
2 1_4                   4
3 2_3                   5
4 2_4                   6

【讨论】:

以上是关于使用 dplyr 对多个变量的所有可能组合进行分组的主要内容,如果未能解决你的问题,请参考以下文章

使用 dplyr 嵌套或分组两个变量,然后对数据执行 Cronbach 的 alpha 函数或其他统计

(更新)R语言 dplyr的group与summarise的使用

dplyr summarise :在循环中按多个变量分组并将结果添加到同一数据框中

pandas使用groupby函数基于指定分组变量对dataframe数据进行分组使用get_group函数获取指定分组变量的具体分类值下的所有样本(获取的是多个分组中的其中一个分组的内容)

pandas使用groupby函数进行分组聚合并使用agg函数将每个分组特定变量对应的多个内容组合到一起输出(merging content within a specific column of g

dplyr:子分组(group_by)数据帧上的colSums:优雅