基于条件忽略行的特定值连续运行计数器构建(R语言)
问题解决:基于规则的连续组计数与行过滤
原始数据集
# A tibble: 987 × 2 time counts <time> <chr> 1 07:33 1358 2 07:34 1072 3 07:35 112 4 07:36 316 5 07:37 0 6 07:38 16 7 07:39 32 8 07:40 0 9 07:41 0 10 07:42 92 # … with 977 more rows
需求说明
- 创建
counter变量:为连续运行的组分配唯一值,当counts等于0时重置计数 - 规则化标记“视为0”的行:
- 若
counts == 0的行后紧跟2行counts < 100,这2行需当作0处理 - 若
counts == 0的行后紧跟1行counts > 1000,这行需当作0处理
- 若
- 最终目标:删除包含上述“视为0”行在内的连续60行及以上的0值行
已尝试的代码
用rleid分组(无法处理规则化“视为0”的行)
db %>% mutate(consec_id = rleid(counts==0))
循环实现(未得到预期结果)
# Loop through the dataset and update the "wear_status" variable for (i in 2:nrow(day1)) { # Check if current counts = 0 if (db$counts[i] == 0) { consec_counts <- 0 db$wear_status[i] <- 0 } else { # Check if current counts are between 1 and 100 if (db$counts[i] > 1 || db$counts[i] < 100) { consec_counts <- consec_counts + 1 } else { consec_counts <- 0 } # Check if current counts > 100 or 3 consecutive rows of counts = 1 or 100 if (db$counts[i] > 100 || consec_counts <= 2) { consec_counts <- 0 db$wear_status[i] <- 1 } } }
预期输出
# A tibble: 987 × 3 time counts counter <time> <chr> <dbl> 1 07:33 1358 1 2 07:34 1072 1 3 07:35 112 1 4 07:36 316 1 5 07:37 0 2 6 07:38 16 2 7 07:39 32 2 8 07:40 0 2 9 07:41 0 2 10 07:42 92 3 11 07:43 80 3 12 07:44 78 3 13 07:45 0 4 # … with 977 more rows
解决方案
使用dplyr结合data.table::rleid实现规则标记与分组,步骤如下:
1. 加载依赖包
library(dplyr) library(data.table) # 用于rleid函数,也可替换为dplyr::consecutive_id
2. 数据处理与分组代码
db_clean <- db %>% # 先将字符型counts转为数值型,方便计算 mutate(counts_num = as.numeric(counts)) %>% # 标记原始0值行 mutate(is_raw_zero = counts_num == 0) %>% # 标记需要视为0的行: # - 原始0值 # - 前1/2行是原始0,且当前counts<100 # - 前1行是原始0,且当前counts>1000 mutate( prev1_raw_zero = lag(is_raw_zero, n = 1, default = FALSE), prev2_raw_zero = lag(is_raw_zero, n = 2, default = FALSE), treat_as_zero = is_raw_zero | ((prev1_raw_zero | prev2_raw_zero) & counts_num < 100) | (prev1_raw_zero & counts_num > 1000) ) %>% # 生成counter:非0组(不满足视为0规则)连续为同一组,切换时counter递增 mutate(counter = rleid(!treat_as_zero)) %>% # 按counter分组,计算每组行数 group_by(counter) %>% mutate(group_size = n()) %>% ungroup() %>% # 过滤掉连续60行及以上的视为0的组 filter(!(treat_as_zero & group_size >= 60)) %>% # 保留需要的列 select(time, counts, counter)
代码逻辑说明
- 类型转换:将字符型的
counts转为数值型,避免字符比较的错误 - 规则标记:通过
lag函数查看前1、2行是否为原始0值,结合当前行的counts值,标记出所有需要视为0的行 - 分组计数:用
rleid对!treat_as_zero分组,连续的非0行(或不满足视为0规则的行)为同一组,每次状态切换时counter自动递增 - 过滤长序列:计算每组的行数,删除连续60行及以上的视为0的组
如果需要调整规则(比如仅针对连续紧跟的行),可修改treat_as_zero的判断条件,例如仅考虑连续0之后的行:
# 调整为仅标记连续0之后的1-2行满足条件的情况 db_clean <- db %>% mutate(counts_num = as.numeric(counts)) %>% mutate(zero_group = rleid(counts_num == 0)) %>% group_by(zero_group) %>% mutate(is_zero_group = first(counts_num == 0)) %>% ungroup() %>% mutate(prev_zero_group = ifelse(!is_zero_group, lag(zero_group), NA)) %>% group_by(prev_zero_group) %>% mutate(pos_after_zero = row_number()) %>% ungroup() %>% mutate( treat_as_zero = counts_num == 0 | (!is_zero_group & !is.na(prev_zero_group) & pos_after_zero %in% 1:2 & counts_num < 100) | (!is_zero_group & !is.na(prev_zero_group) & pos_after_zero == 1 & counts_num > 1000) ) %>% mutate(counter = rleid(!treat_as_zero)) %>% group_by(counter) %>% mutate(group_size = n()) %>% ungroup() %>% filter(!(treat_as_zero & group_size >= 60)) %>% select(time, counts, counter)
内容的提问来源于stack exchange,提问作者marcela1510
相关产品推荐
相关产品推荐

