基于16小时步长筛选时间差DataFrame的R语言技术求助
16小时步长DataFrame筛选方案实现
需求说明
我有一个包含diff_h列(存储当前行与前一行的小时时间差)的DataFrame,行是连续序列。需筛选出构成16小时规则步长的行,删除非16小时步长的中间行,规则如下:
- NA会中断累计(如8、NA、8无法凑成16)
- 无法凑成16的4/8小时差需设为NA
此前提供的代码仅保留少量有效步长,现给出输入示例、现有代码及期望输出,寻求正确实现方案。
输入示例
ID Datetime diff_h 134_18 7/27/2018 0:00 NA 134_18 7/27/2018 19:00 19 134_18 7/28/2018 0:00 5 134_18 7/28/2018 8:00 8 134_18 7/28/2018 16:00 8 134_18 7/29/2018 0:00 8 134_18 7/29/2018 8:00 8 134_18 7/29/2018 12:00 4 134_18 7/30/2018 16:00 4 134_18 7/30/2018 20:00 4 134_18 7/30/2018 16:00 8 134_18 7/31/2018 0:00 8 134_18 7/31/2018 8:00 8 134_18 7/31/2018 16:00 8 134_18 8/1/2018 8:00 16 134_18 8/1/2018 16:00 8 134_18 8/2/2018 0:00 8 134_18 8/2/2018 8:00 8 134_18 8/2/2018 16:00 8
现有代码及问题
现有代码(已删除报错的%>% select(-grp) %>%):
data1 <- test %>% group_by(ID, grp = rleid(diff_h %/% 16)) %>% mutate(diff_h = cumsum(diff_h), diff_h = replace(diff_h, n() ==1 & diff_h != 16, NA_real_)) %>% ungroup %>% filter(diff_h == 16|is.na(diff_h))
现有输出仅保留了少量有效步长,不符合需求:
134_18 7/27/2018 0:00 NA 1 134_18 7/27/2018 19:00 NA 2 134_18 8/1/2018 8:00 16 4 134_18 8/2/2018 0:00 16 5
期望输出
ID Datetime diff_h 134_18 7/27/2018 0:00 NA 134_18 7/27/2018 19:00 NA 134_18 7/28/2018 0:00 NA 134_18 7/28/2018 16:00 16 134_18 7/29/2018 8:00 16 134_18 7/29/2018 12:00 NA 134_18 7/30/2018 16:00 16 134_18 7/31/2018 8:00 16 134_18 7/31/2018 16:00 NA 134_18 8/1/2018 8:00 16 134_18 8/2/2018 0:00 16 134_18 8/2/2018 16:00 16
正确实现方案
使用dplyr结合data.table的rleid函数,按规则分组累计并筛选:
library(dplyr) library(data.table) result <- test %>% # 按ID分组,同时用rleid标记NA中断的连续组 group_by(ID, grp = rleid(is.na(diff_h))) %>% mutate( # 计算每组内的累计小时差(NA行不计入累计) cum_diff = cumsum(ifelse(is.na(diff_h), 0, diff_h)), # 标记累计差刚好达到16倍数的行 is_16_step = cum_diff %in% seq(16, max(cum_diff, na.rm = TRUE), 16), # 生成最终的diff_h列 new_diff_h = case_when( # NA行保持NA is.na(diff_h) ~ NA_real_, # 凑成16的行设为16 is_16_step ~ 16, # 每组最后一行若无法凑成16,设为NA row_number() == n() & (cum_diff %% 16) != 0 ~ NA_real_, # 其他中间行标记为待删除 TRUE ~ NA_real_ ), # 标记需要保留的行:NA行、16步长节点、每组最后无法凑成的行 keep = is.na(diff_h) | is_16_step | (row_number() == n() & (cum_diff %% 16) != 0) ) %>% ungroup() %>% # 过滤保留目标行 filter(keep) %>% # 选择需要的列并重命名 select(ID, Datetime, diff_h = new_diff_h)
代码逻辑说明
- 分组规则:用
rleid(is.na(diff_h))确保NA会中断累计分组,每组内是连续的非NA时间差序列。 - 累计计算:
cum_diff计算每组内的累计小时差,NA行不参与累计。 - 步长标记:
is_16_step定位累计差刚好达到16的倍数的行,这些行就是16小时步长的节点。 - 值处理:
new_diff_h按规则赋值:NA行保留NA,16步长节点设为16,每组最后无法凑成16的行设为NA。 - 行筛选:
keep标记需要保留的行,过滤后得到符合期望的结果。
内容的提问来源于stack exchange,提问作者Beardedant
相关产品推荐
相关产品推荐

