如何在dplyr中实现含重叠类别的复杂分组(替代rbind)
问题描述
我有一个包含gr1、gr2和both分组的数据集,需要按c("gr1", "both")和c("gr2", "both")对gr列进行分组计算x的总和。目前我通过拆分过滤再rbind的方式实现了需求,但想知道能不能在dplyr里直接指定这些分组组合,不用手动拼接结果?
原始实现代码:
library(tidyverse) set.seed(1234) df = data.frame(x = 1:10, id = sample(LETTERS[1:3], size = 10, replace = TRUE), gr = c(rep("gr1",3), rep("gr2",4),rep("both",3))) sum.gr1 = df %>% filter(gr %in% c("gr1", "both")) %>% group_by(id) %>% summarize(x.sum = sum(x)) %>% mutate(gr.filt = "gr1.both") sum.gr2 = df %>% filter(gr %in% c("gr2", "both")) %>% group_by(id) %>% summarize(x.sum = sum(x))%>% mutate(gr.filt = "gr2.both") df.gr = rbind(sum.gr1, sum.gr2) df.gr
解决方案
可以用以下几种方法在dplyr中直接实现,无需手动拆分再拼接:
方法1:用map_dfr遍历分组组合
预先定义好需要的分组组合列表,用map_dfr自动遍历处理并合并结果,.id参数直接生成分组标识列:
library(tidyverse) set.seed(1234) df = data.frame(x = 1:10, id = sample(LETTERS[1:3], size = 10, replace = TRUE), gr = c(rep("gr1",3), rep("gr2",4),rep("both",3))) # 定义分组组合,键作为分组标识 gr_groups = list( gr1.both = c("gr1", "both"), gr2.both = c("gr2", "both") ) # 遍历组合并自动合并结果 df.gr = map_dfr(gr_groups, function(target_grs) { df %>% filter(gr %in% target_grs) %>% group_by(id) %>% summarize(x.sum = sum(x)) }, .id = "gr.filt") # 查看结果 df.gr
运行后得到的结果和原始方法完全一致,而且新增分组组合时只需要修改gr_groups列表即可,扩展性更强。
方法2:用crossing生成组合后逐行计算
先生成所有id和分组类型的笛卡尔积,再按行匹配对应的分组规则计算总和:
df.gr = crossing( id = unique(df$id), gr.filt = c("gr1.both", "gr2.both") ) %>% mutate( # 为每个分组类型匹配对应的gr列表 target_grs = case_when( gr.filt == "gr1.both" ~ list(c("gr1", "both")), gr.filt == "gr2.both" ~ list(c("gr2", "both")) ) ) %>% rowwise() %>% mutate( # 计算当前id在对应分组下的x总和 x.sum = sum(df$x[df$id == id & df$gr %in% target_grs]) ) %>% ungroup() %>% select(-target_grs) df.gr
这种方法不需要多次过滤原数据集,适合数据量较大的场景。
内容的提问来源于stack exchange,提问作者M. Beausoleil
相关产品推荐
相关产品推荐

