R tidyverse自定义规则处理数据框:按组优先保留val=1的行
问题描述
需要按自定义规则对数据框进行分组去重:按ID分组后每组仅保留一行,优先保留组内val=1的行;若组内没有val=1的行,则保留val最小的行。
数据示例
创建示例数据框的代码:
x = data.frame(ID=c("a", "a", "b", "b", "c", "c", "d", "d"), val=c(1, 2, 0.5, 2, 1, 0.5, 5, 20))
数据框内容:
ID val 1 a 1.0 2 a 2.0 3 b 0.5 4 b 2.0 5 c 1.0 6 c 0.5 7 d 5.0 8 d 20.0
已尝试的方法及问题
- 按
val排序后去重
x %>% group_by(ID) %>% arrange(val) %>% distinct(ID, .keep_all = T) %>% arrange(ID)
结果:
ID val 1 a 1 2 b 0.5 3 c 0.5 4 d 5
问题:ID为c的组错误保留了val=0.5的行,未优先选择val=1的行。
- 使用
slice_min函数
x %>% group_by(ID) %>% slice_min(order_by = tibble(val != 1, val), n = 1, with_ties = FALSE) %>% ungroup()
结果及警告:
# A tibble: 3 × 2 ID val <chr> <dbl> 1 a 1 2 c 1 3 d 5 Warning messages: 1: In xtfrm.data.frame(x) : cannot xtfrm data frames 2: In xtfrm.data.frame(x) : cannot xtfrm data frames 3: In xtfrm.data.frame(x) : cannot xtfrm data frames 4: In xtfrm.data.frame(x) : cannot xtfrm data frames
问题:丢失了ID为b的行,且出现无效警告。
期望输出
ID val 1 a 1 2 b 0.5 3 c 1 4 d 5
解决方案
方法1:自定义排序后取首行
通过优先级排序,先把val=1的行排在每组最前面,再按val升序排序,最后每组取第一行:
library(dplyr) x %>% group_by(ID) %>% arrange(desc(val == 1), val) %>% # 优先val=1的行,再按val从小到大排序 slice_head(n = 1) %>% ungroup() %>% arrange(ID)
方法2:分条件筛选合并
先提取所有val=1的行,再补充无val=1组的最小val行,最后合并排序:
library(dplyr) # 提取所有val=1的行 val1_rows <- x %>% filter(val == 1) # 提取无val=1的组,每组取val最小的行 min_val_rows <- x %>% anti_join(val1_rows, by = "ID") %>% group_by(ID) %>% slice_min(val, n = 1, with_ties = FALSE) %>% ungroup() # 合并结果并按ID排序 bind_rows(val1_rows, min_val_rows) %>% arrange(ID)
两种方法均可得到符合要求的输出,且无异常警告。
内容的提问来源于stack exchange,提问作者Yan
相关产品推荐
相关产品推荐

