R语言大样本数据:带豁免规则的连续0值区间识别需求
问题需求
我有按id分组的count数据(约1.5亿条观测),需要识别每个id下满足以下条件的区间:
- 区间内至少有60行连续的
count值为0 - 允许区间内存在最多2行连续的
count值介于0到100之间(即0 < count ≤ 100)
示例数据中id=1的区间符合要求,id=3的区间不符合。已用cumsum实现基础逻辑,但无法处理上述豁免规则,由于数据量庞大,优先采用data.table方案。
示例数据
test_df <- tibble::tibble( id = c(rep(1,100), rep(2,100), rep(3,100)), count = c(rep(0,2), 22,55, rep(0,69), 33,44,55,66,77, rep(0,22), rep(0,61), rep(250, 39), rep(0,2), 22,55, 22,55, rep(0,94)) )
数据预览:
id count <dbl> <dbl> 1 1 0 2 1 0 3 1 22 4 1 55 5 1 0 6 1 0 7 1 0
现有dplyr代码
library(dplyr) test_df %>% group_by(id) %>% mutate(check = cumsum(c(F, abs(diff(count)) > 0))) %>% group_by(id, check) %>% mutate(create_identifier = ifelse((count == 0 & n() >= 60), 1,0))
解决方案(data.table)
核心思路
- 按
count值给每行打分类标签:0:count == 0(合规行)1:0 < count ≤ 100(允许的豁免行,最多连续2行)2:count > 100(无效行,直接打断区间)
- 用
rleid()按id和分类标签生成连续相同类型的块ID - 汇总每个块的类型、长度和起止位置
- 在每个
id内筛选符合条件的块序列:总合规行长度≥60,无无效块,且所有豁免块长度≤2 - 批量标记原始数据中符合条件的行
代码实现
library(data.table) # 转换为data.table格式(处理大数据的基础) setDT(test_df) # 1. 给每行打分类标签 test_df[, type := fcase( count == 0, 0, count > 0 & count <= 100, 1, count > 100, 2 )] # 2. 生成连续相同类型的块ID test_df[, rl := rleid(type), by = id] # 3. 汇总每个块的关键信息 block_summary <- test_df[, .( type = first(type), block_len = .N, start_row = .I[1], end_row = .I[.N] ), by = .(id, rl)] # 4. 计算块序列的累积指标,筛选有效块组 block_summary[, `:=`( # 累积合规块(type=0)的总长度 cum_valid_len = cumsum(ifelse(type == 0, block_len, 0)), # 标记是否出现过无效块(type=2) has_invalid = cumsum(ifelse(type == 2, 1, 0)) > 0, # 标记是否出现过长于2行的豁免块 has_over_exempt = cumsum(ifelse(type == 1 & block_len > 2, 1, 0)) > 0 ), by = id] # 反向累积检查,确保后续块也没有无效内容 block_summary[, `:=`( reverse_has_invalid = rev(cumsum(rev(ifelse(type == 2, 1, 0)))) > 0, reverse_has_over_exempt = rev(cumsum(rev(ifelse(type == 1 & block_len > 2, 1, 0)))) > 0 ), by = id] # 筛选所有符合条件的块 valid_blocks <- block_summary[ cum_valid_len >= 60 & !has_invalid & !has_over_exempt & !reverse_has_invalid & !reverse_has_over_exempt ] # 5. 批量标记原始数据中的有效行 test_df[, is_valid := FALSE] for (idx in seq_len(nrow(valid_blocks))) { test_df[valid_blocks$start_row[idx]:valid_blocks$end_row[idx], is_valid := TRUE] } # 查看结果示例 # test_df[id == 1, .(id, count, is_valid)] # test_df[id == 3, .(id, count, is_valid)]
效率说明
data.table的fcase()和rleid()都是针对大数据优化的函数,比dplyr对应操作快数倍- 采用块级汇总而非逐行计算,避免了1.5亿行的循环操作,大幅提升效率
- 最后通过块起止索引批量更新,减少内存开销
内容的提问来源于stack exchange,提问作者Julian
相关产品推荐
相关产品推荐

