分组数据中使用rle函数时忽略NA实现滚动计数
解决按ID分组跳过NA的Value变化滚动计数问题
数据集构建
先还原你的数据集:
id <- c(1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2) value <- c("x","x","y",NA,NA,"x",NA,"y", "y","y",NA,"x","x",NA,"x","x") df <- data.frame(id, value)
解决方案
以下提供两种实现预期输出的方法:
方法一:使用zoo包辅助处理(逻辑直观)
library(dplyr) library(zoo) df %>% group_by(id) %>% mutate( # 用前一个非NA值填充当前NA位置,消除NA对value变化判断的干扰 prev_valid = na.locf(value, na.rm = FALSE), # 标记非NA行是否发生value变化(第一行非NA默认标记为变化) is_new_group = ifelse(is.na(value), NA, value != lag(prev_valid, default = value[!is.na(value)][1])), # 累积变化标记得到计数,再将NA行的计数置为NA rolling_count = cumsum(ifelse(is_new_group, 1, 0), na.rm = TRUE), rolling_count = ifelse(is.na(value), NA, rolling_count) ) %>% select(-prev_valid, -is_new_group)
逻辑说明:
na.locf函数将NA替换为最近的前一个非NA值,让我们可以直接比较当前有效value和上一个有效value的差异is_new_group仅在非NA且value发生变化时标记为TRUE,确定计数需要递增的位置- 最后通过
cumsum累积计数,并将原value为NA的行的计数重置为NA
方法二:纯dplyr实现(无需额外依赖)
library(dplyr) df %>% group_by(id) %>% mutate( # 为连续相同的非NA value分配组号,每次value变化(非NA)时组号递增 group_id = cumsum(!is.na(value) & (is.na(lag(value)) | value != lag(value))), # 将组号映射为从1开始的连续计数,NA行的计数置为NA rolling_count = ifelse(is.na(value), NA, match(group_id, unique(na.omit(group_id)))) ) %>% select(-group_id)
逻辑说明:
group_id给每个连续的非NA相同value块分配唯一组号,NA会继承前一个组号但后续处理会忽略match将非连续的组号重新映射为连续的1、2、3...计数,同时将NA行的计数设为NA
原方法问题说明
你之前使用rle()的方法会将NA视为独立分组,每段NA都会增加计数,但我们需要完全跳过NA的干扰,只追踪非NA值的变化,因此需要先过滤NA的影响再进行计数。
内容的提问来源于stack exchange,提问作者Frewtea
相关产品推荐
相关产品推荐

