创建带重置功能的连续无事件计数变量(R语言)
带重置的分组递推计数问题(R语言)
需求说明
按labs分组统计连续无事件(event=0)的时间步数:
- 事件发生(
event=1)时,计数重置为0 - 初始无事件的行计数从0开始递增
- 保留
event为NA对应的NA值
示例数据
labs <- c("A", "A", "A", "A", "B", "B", "B", "B", "C", "C", "C", "C", "D", "D", "D", "D", "D") time <- c(1,2,3,4 ,1,2,3,4 ,1,2,3,4 ,1,2,3,4,5) event <- c(0,0,0,0, 0,1,0,0, 1,1,0,0, NA,0,0,1,0) desiredOutcome <- c(0,1,2,3,0,0,0,1,0,0,0,1,NA,0,1,0,0) # 目标输出 exDF <- data.frame(labs,time, event, desiredOutcome)
已尝试代码
library(dplyr) exDF <- exDF %>% group_by(labs) %>% mutate(pe1 = lag(event, order_by=time)) # 创建前一事件变量 exDF$count2 <- ifelse( ((exDF$pe1 == 1) & (exDF$event == 0)), # 前一步有事件且当前无事件 0, # 满足条件设为0 NA) # 否则为NA exDF$count <- ifelse( (is.na(exDF$pe1) & (exDF$event == 0)), # 无前置步且当前无事件 0, # 满足条件设为0 exDF$count2) # 否则取count2的值
解决方案
方法1:dplyr + purrr(tidyverse生态)
利用purrr::accumulate实现递推计算,它可以用上一步结果生成当前值,完美适配带重置的计数场景:
library(dplyr) library(purrr) exDF <- exDF %>% group_by(labs) %>% mutate( # 清理event的NA,保留NA类型 event_clean = ifelse(is.na(event), NA_integer_, event), # 递推计算计数 count = accumulate(event_clean, .init = NA_integer_, ~ if (is.na(.y)) { NA_integer_ } else if (.y == 1) { 0L } else if (is.na(.x)) { 0L } else { .x + 1L })[-1] # 去掉初始的NA ) %>% ungroup() %>% select(-event_clean) # 验证结果 all.equal(exDF$count, exDF$desiredOutcome, na.rm = TRUE)
方法2:data.table(高效大数据处理)
如果数据集较大,data.table的分组和rleid函数能提供更高的运行效率:
library(data.table) setDT(exDF) exDF[, count := { # 生成重置点的分组ID:event=1或NA时分割 rle_group <- rleid(event == 1 | is.na(event)) # 对每个分组计算计数 lapply(split(.SD, rle_group), function(sub_df) { if (any(is.na(sub_df$event))) { rep(NA, nrow(sub_df)) } else if (any(sub_df$event == 1)) { rep(0, nrow(sub_df)) } else { seq_len(nrow(sub_df)) - 1 } }) %>% unlist() }, by = labs] # 验证结果 all.equal(exDF$count, exDF$desiredOutcome, na.rm = TRUE)
说明
- 两种方法均能精准匹配目标输出,覆盖NA处理、事件重置和连续计数的全部逻辑
- tidyverse方法可读性强,适合日常使用dplyr的场景
- data.table方法在大数据集上的运行效率更有优势
内容的提问来源于stack exchange,提问作者MyopicMycroft
相关产品推荐
相关产品推荐

