如何在dplyr管道语句中保留每组最完整信息的行?
问题描述
输入数据集的每个ID组内存在冗余的info信息:部分行包含带_detail后缀的完整信息,部分行只有基础信息;还有部分ID组从未出现过完整信息。需要按ID和信息类型保留每组中最完整的行,且全程使用单个dplyr管道完成,无需中间变量。
输入示例
df <- data.frame(ID = c(1, 1, 1, 2, 3, 3, 3, 3), info = c("info1#info1_detail", "info1", "info3", "info1", "info1#info1_detail", "info1", "info2#info2_detail", "info2")) # 输出结果: # ID info # 1 1 info1#info1_detail # 2 1 info1 # 3 1 info3 # 4 2 info1 # 5 3 info1#info1_detail # 6 3 info1 # 7 3 info2#info2_detail # 8 3 info2
期望输出示例
result <- data.frame(ID = c(1, 1, 2, 3, 3), info = c("info1#info1_detail", "info3", "info1", "info1#info1_detail", "info2#info2_detail")) # 输出结果: # ID info # 1 1 info1#info1_detail # 2 1 info3 # 3 2 info1 # 4 3 info1#info1_detail # 5 3 info2#info2_detail
解决方案
使用dplyr结合stringr的管道操作即可实现需求,代码如下:
library(dplyr) library(stringr) df %>% # 提取info的基础部分(去除#及后续的_detail内容) mutate(base_info = str_remove(info, "#.*")) %>% # 按ID和基础信息分组,确保同一ID下的同类型信息归为一组 group_by(ID, base_info) %>% # 筛选每组中info字符长度最长的行(优先保留带_detail的完整信息) slice_max(nchar(info), n = 1) %>% # 取消分组 ungroup() %>% # 移除临时生成的base_info列 select(-base_info)
逻辑说明
- 生成基础信息列:通过
str_remove提取info字段中#之前的内容,作为同类型信息的分组依据; - 分组筛选:按
ID和base_info分组后,用slice_max选择每组中字符长度最长的行——带_detail的信息长度更长,会被优先保留;如果某组只有基础信息,则直接保留该行; - 清理结果:取消分组并移除临时的
base_info列,得到最终的去重后保留完整信息的数据集。
内容的提问来源于stack exchange,提问作者Patrick
相关产品推荐
相关产品推荐

