按ID分组,基于sBP/dBP各自条件删除后续行
问题与解决方案
需求说明
此前我曾解决过类似的简单问题,本次需求更复杂:现有包含sBP和dBP两列的数据框,需按Id分组,根据以下规则删除后续行:
- 若某行
sBP >= 140,删除该行之后的所有行; - 若某行
dBP >= 90先于sBP条件触发,同样删除该行之后的所有行; - 若两列均未满足条件,保留所有行。
示例数据
Id <- c(1,1,1,1,2,2,2,2,2,3,3,3,3,3,3,4,4,4,4) sBP <- c(130,130,140,131, 140, 120, 121, 121, 120, 130, 130, 130, 130, 130, 130, 130, 120, 140, 120) dBP <- c(89,87,88,70, 89, 90, 70, 70, 70, 72, 73, 74, 76, 90, 80, 70, 90, 80, 60 ) DF1 <- data.frame(Id, sBP, dBP)
解决方案(dplyr实现)
使用dplyr的分组和累计求和功能,可以高效实现需求:
library(dplyr) DF2 <- DF1 %>% group_by(Id) %>% mutate( # 标记每行是否触发任一条件 is_trigger = sBP >= 140 | dBP >= 90, # 累计触发次数,第一次触发后后续值均大于1 cum_trigger = cumsum(is_trigger) ) %>% # 保留未触发过的所有行,以及第一次触发的行 filter(cum_trigger <= 1 & (cum_trigger == 0 | is_trigger)) %>% # 移除辅助列并取消分组 select(-is_trigger, -cum_trigger) %>% ungroup() print(DF2)
代码逻辑解释
- 分组处理:通过
group_by(Id)确保每个ID独立计算; - 标记触发行:
is_trigger列标记当前行是否满足sBP>=140或dBP>=90; - 累计触发状态:
cum_trigger用累计求和,第一次触发后后续行的累计值都会大于1; - 筛选目标行:
filter条件保留两种情况:还未触发过的行(cum_trigger == 0),以及第一次触发的行(is_trigger为TRUE且cum_trigger <=1); - 清理结果:移除辅助列并取消分组,得到最终数据框。
运行后得到的结果完全符合需求:
- ID1:保留前3行(第三行
sBP=140触发条件); - ID2:保留第1行(第1行
sBP=140先触发); - ID3:保留前4行(第4行
dBP=90触发条件); - ID4:保留前2行(第2行
dBP=90先触发)。
内容的提问来源于stack exchange,提问作者19056530
相关产品推荐
相关产品推荐

