使用dplyr按组删除某列首次出现指定值后的行及后续行
解决方案
需求
按id分组、组内按name排序后,删除char列首次出现1的行及其后续所有行。
示例数据
df <- data.frame( id = c(1001, 1001, 1002, 1002, 1002, 1005, 1005, 1005, 1005, 1005), name = c("monkey", "gorilla", "chimp", "monkey", "giraffe", "tarzan", "whale", "princess", "phone", "kindle"), char = c(0, 1, 0, 1, 0, 0, 0, 1, 0, 0) ) # 目标结果 df_desired <- data.frame( id = c(1001, 1002, 1005, 1005), name = c("monkey", "chimp", "tarzan", "whale"), char = c(0, 0, 0, 0) )
方法1:使用tidyverse(dplyr)
library(dplyr) df_result <- df %>% group_by(id) %>% arrange(name, .by_group = TRUE) %>% # 处理边界情况:组内无1时保留全部行 filter(ifelse(is.na(which(char == 1)[1]), TRUE, row_number() < which(char == 1)[1])) %>% ungroup() # 查看结果 df_result
关键步骤说明
- 分组排序:
group_by(id)按id分组后,arrange(name, .by_group = TRUE)确保组内按name字母顺序排序 - 过滤行:
which(char == 1)[1]定位组内char首次为1的行号- 用
ifelse处理无1的分组:这类分组直接保留所有行;有1的分组只保留首次出现1之前的行
方法2:使用base R(无需额外包)
# 先按id和name全局排序 df_sorted <- df[order(df$id, df$name), ] # 分组处理并合并 df_result <- do.call(rbind, lapply(split(df_sorted, df_sorted$id), function(group) { first_one_pos <- which(group$char == 1)[1] if (!is.na(first_one_pos)) { group[1:(first_one_pos - 1), ] } else { group } })) # 重置行名 rownames(df_result) <- NULL # 查看结果 df_result
关键步骤说明
- 排序拆分:
order(df$id, df$name)完成全局排序,split将数据按id拆分为分组列表 - 分组过滤:对每个分组判断是否存在
char=1,存在则保留首次出现前的行,否则保留全部分组 - 合并结果:
do.call(rbind, ...)将处理后的分组重新合并为数据框
验证结果
两种方法输出均与目标结果一致:
all.equal(df_result, df_desired) #> [1] TRUE
内容的提问来源于stack exchange,提问作者taimishu
相关产品推荐
相关产品推荐

