R语言:高效计算DataFrame连续零值段的累计时间差
高效处理大规模时序数据的时间差计算需求
原始数据
拥有包含就诊ID、时间戳及对应数值的DataFrame:
df <- data.frame(encounterId = c(1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026, 1026), chartTime = c("2015-09-16 14:11:00 CEST", "2015-09-16 14:13:00 CEST", "2015-09-16 15:00:00 CEST", "2015-09-16 15:08:00 CEST", "2015-09-16 16:00:00 CEST", "2015-09-16 16:22:00 CEST", "2015-09-16 17:00:00 CEST", "2015-09-16 17:41:00 CEST", "2015-09-16 18:00:00 CEST", "2015-09-16 18:01:00 CEST", "2015-09-16 19:00:00 CEST", "2015-09-16 19:53:00 CEST"), value = c(2.0, 2.0, 0.0, 2.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 2.0, 2.0))
当前已实现代码
已编写计算每行与下一行时间差的代码,但未满足最终需求:
df <- df %>% group_by(encounterId) %>% mutate(time_diff = as.numeric(difftime(lead(chartTime), chartTime), units = 'mins'))
核心需求
- 计算每行与下一行的时间差(分钟为单位)
- 当连续出现多个
value=0的行时,仅在该连续段的第一个0行显示该段的累计时间差(即从该0行到下一个非0行的总时间差),后续0行的time_diff设为NA - 最后一行的
time_diff为空(或NA)
预期结果
| encounterId | chartTime | value | time_diff |
|---|---|---|---|
| 1026 | 2015-09-16 14:11:00 CEST | 2 | 2 |
| 1026 | 2015-09-16 14:13:00 CEST | 2 | 47 |
| 1026 | 2015-09-16 15:00:00 CEST | 0 | 8 |
| 1026 | 2015-09-16 15:08:00 CEST | 2 | 52 |
| 1026 | 2015-09-16 16:00:00 CEST | 2 | 22 |
| 1026 | 2015-09-16 16:22:00 CEST | 0 | 99 |
| 1026 | 2015-09-16 17:00:00 CEST | 0 | NA |
| 1026 | 2015-09-16 17:41:00 CEST | 0 | NA |
| 1026 | 2015-09-16 18:00:00 CEST | 0 | NA |
| 1026 | 2015-09-16 18:01:00 CEST | 2 | 59 |
| 1026 | 2015-09-16 19:00:00 CEST | 2 | 53 |
| 1026 | 2015-09-16 19:53:00 CEST | 2 |
高效解决方案
针对10亿行级别的大规模数据,必须使用向量化操作避免循环,以下提供两种高效实现方案:
方案1:dplyr + data.table 混合实现
library(dplyr) library(data.table) # 转换时间类型,确保时区正确 df <- df %>% mutate(chartTime = as.POSIXct(chartTime, tz = "Europe/Paris")) %>% group_by(encounterId) %>% # 生成连续相同value的分组ID mutate(grp = rleid(value)) %>% # 计算每个分组的结束时间:0分组取下一个非0行的时间,非0分组取下一行时间 mutate(end_time = case_when( value == 0 ~ { next_non_zero_pos <- which(lead(value) != 0)[1] if (is.na(next_non_zero_pos)) last(chartTime) else lead(chartTime, n = next_non_zero_pos) }, TRUE ~ lead(chartTime) )) %>% # 计算时间差 mutate(time_diff = as.numeric(difftime(end_time, chartTime, units = "mins"))) %>% # 连续0分组的非第一行设为NA mutate(time_diff = ifelse(value == 0 & row_number() != first(row_number()), NA, time_diff)) %>% # 最后一行设为NA mutate(time_diff = ifelse(row_number() == n(), NA, time_diff)) %>% # 移除辅助列 select(-grp, -end_time) %>% ungroup()
方案2:data.table 纯实现(性能更优)
data.table的内存管理和运算效率更适合超大规模数据:
library(data.table) setDT(df) # 转换时间类型 df[, chartTime := as.POSIXct(chartTime, tz = "Europe/Paris")] # 按encounterId分组,生成连续相同value的分组ID df[, grp := rleid(value), by = encounterId] # 计算每个分组的结束时间 df[, end_time := { if (value[1] == 0) { next_non_zero <- which(shift(value, type = "lead") != 0)[1] if (is.na(next_non_zero)) last(chartTime) else chartTime[next_non_zero] } else { shift(chartTime, type = "lead") } }, by = .(encounterId, grp)] # 计算时间差 df[, time_diff := as.numeric(difftime(end_time, chartTime, units = "mins"))] # 连续0分组的非第一行设为NA df[value == 0 & seq_len(.N) != 1, time_diff := NA, by = .(encounterId, grp)] # 每个encounterId的最后一行设为NA df[.N, time_diff := NA, by = encounterId]
内容的提问来源于stack exchange,提问作者a6tole
相关产品推荐
相关产品推荐

