寻求R语言中分组级distinct()的更直接高效实现方法
你要实现的是按分组的procedure集合去重——即保留所有procedure集合完全相同的分组中的任意一组,丢弃其他重复集合的分组。针对23000行的数据集,以下是几种更高效直接的方法:
方法一:dplyr 简化高效版
核心思路是先为每个分组生成唯一的"签名"(用排序后的procedure拼接成字符串),再筛选出每个签名对应的一个分组,最后从原数据中提取这些分组的所有行。
library(dplyr) # 第一步:生成每个分组的procedure集合签名,确定要保留的分组 keep_groups <- data %>% group_by(group) %>% # 排序后拼接,保证相同procedure集合的签名完全一致 summarise(proc_signature = paste0(sort(procedure), collapse = ","), .groups = "drop") %>% # 每个签名仅保留第一个出现的分组 distinct(proc_signature, .keep_all = TRUE) %>% pull(group) # 第二步:从原数据中筛选目标分组的所有行 result <- data %>% filter(group %in% keep_groups) # 如果需要添加示例中的group_id临时列: keep_groups_with_id <- data %>% group_by(group) %>% summarise(proc_signature = paste0(sort(procedure), collapse = ","), .groups = "drop") %>% distinct(proc_signature, .keep_all = TRUE) %>% mutate(group_id = row_number()) %>% select(group, group_id) result_with_id <- data %>% inner_join(keep_groups_with_id, by = "group")
优势:
- 避免了原代码中
separate_rows的字符串拆分操作,直接基于原数据筛选,减少了不必要的计算开销; - 逻辑清晰,每一步都围绕分组集合的唯一性处理,适合大数据量的分组场景。
方法二:data.table 极致高效版
如果数据集包含大量分组,data.table的底层C语言优化会带来更明显的速度提升:
library(data.table) setDT(data) # 为每个分组生成procedure集合的唯一签名 data[, proc_signature := paste0(sort(procedure), collapse = ","), by = group] # 提取每个签名对应的第一个分组 keep_groups <- unique(data[, .(proc_signature, group)], by = "proc_signature")$group # 筛选目标行并添加group_id result <- data[group %in% keep_groups, ] result[, group_id := .GRP, by = proc_signature] # 移除临时签名列并调整列顺序 result[, proc_signature := NULL] setcolorder(result, c("group", "procedure", "group_id"))
优势:
- data.table的分组、筛选操作均经过底层优化,在23000行+大量分组的场景下,运行速度远快于基础dplyr方法;
- 全程在同一张表上操作,减少了数据复制的内存开销。
对比原代码的优化点:
原代码中toString(sort(procedure))会生成带空格的字符串(如"A, B"),后续separate_rows又要拆分回行,这两步在大数据量下会产生额外的字符串处理开销。而上面的方法用paste0(sort(...), collapse = ",")生成紧凑的签名,直接通过分组筛选提取目标行,避免了不必要的字符串拆分,效率提升明显。
内容的提问来源于stack exchange,提问作者TarJae
相关产品推荐
相关产品推荐

