R语言按id分组过滤:移除末尾为连续yes的分组及前置无效行
R 按id分组过滤符合连续值规则的数据实现
需求规则
按id分组后需满足以下规则过滤数据:
- b列存在至少2个连续的
yes,且该段连续yes后至少还有1个其他取值(全组均为yes的情况例外) - 若分组内长度≥2的连续
yes出现在b列末尾,且分组内还存在其他取值,则移除整个分组 - 保留行从分组内首个符合要求的连续
yes开始,连续yes之前的行需全部删除
示例数据
data<-data.frame(id=c(1,1,1, 1,2,2,2,3,3,3, 3,4,4,4, 5,5,5,5), a=c(1,1,1,1,1,2,1,1,2,2,1,1,1,2,1,1,1,2), b=c("yes", "yes","no","no","no", "yes", "yes","no","yes","yes","no", "yes","yes","yes","yes","no","yes","yes" ))
现有代码问题
现有代码仅判断了分组内是否存在长度≥2的连续yes段,未实现以下逻辑:
- 未过滤「最后一段为长度≥2的
yes、且组内存在其他取值」的分组 - 未删除首个符合要求的连续
yes段之前的行,因此无法得到预期结果
# 现有错误代码 data1 <- data %>% group_by(id) %>% filter(any(with(rle(b == 'yes'), lengths[values] > 1)) ) %>% ungroup()
正确实现方案
使用dplyr的group_modify对每个分组单独处理,结合rle识别连续值段:
library(tidyverse) result <- data %>% group_by(id) %>% group_modify(~{ # 计算当前分组b列的连续值统计结果 rle_res <- rle(.x$b) # 找到所有长度≥2的yes段 valid_seg <- which(rle_res$values == "yes" & rle_res$lengths >= 2) # 不存在符合要求的段,直接移除整个分组 if(length(valid_seg) == 0) return(tibble()) # 检查最后一段是否为长度≥2的yes,且分组内存在其他取值,是则移除整个分组 last_seg_idx <- length(rle_res$values) last_is_valid_yes <- rle_res$values[last_seg_idx] == "yes" & rle_res$lengths[last_seg_idx] >=2 if(last_is_valid_yes & last_seg_idx != 1) return(tibble()) # 计算首个符合要求的段的起始行位置 start_row <- sum(rle_res$lengths[1:(valid_seg[1]-1)]) + 1 # 保留从起始行开始的所有行 .x[start_row:nrow(.x), ] }) %>% ungroup()
输出结果
运行上述代码得到的结果与期望输出完全一致:
# A tibble: 10 × 3 id a b <dbl> <dbl> <chr> 1 1 1 yes 2 1 1 yes 3 1 1 no 4 1 1 no 5 3 2 yes 6 3 2 yes 7 3 1 no 8 4 1 yes 9 4 1 yes 10 4 2 yes
内容的提问来源于stack exchange,提问作者Yebelay Berehan
相关产品推荐
相关产品推荐

