如何在data.table中合并部分重叠的多分类日期区间并生成标识
高效data.table实现方案
这个方案完全基于data.table原生优化操作,无循环,可轻松处理百万行级、多运动类别的业务场景。
完整代码
library(data.table) library(lubridate) # 步骤1:整合所有运动数据为长表,新增运动类型标记 DT_sportA[, sport := "A"] DT_sportB[, sport := "B"] # 若有更多运动表,统一添加sport标记后加入rbind即可 all_sport <- rbind(DT_sportA, DT_sportB) # 步骤2:按id提取所有时间断点,生成所有独立区间 # 断点包含所有运动的start_date、end_date+1(适配左闭右闭的日期区间规则) breaks <- all_sport[, .(date = unique(c(start_date, end_date + 1))), by = id] setorder(breaks, id, date) # 相邻断点组成连续区间 breaks[, `:=`( start_date = date, end_date = shift(date, type = "lead") - 1 ), by = id] breaks <- breaks[!is.na(end_date), .(id, start_date, end_date)] # 步骤3:非等值连接匹配区间与运动记录,转宽表生成0/1标记 res <- breaks[all_sport, on = .(id, start_date >= start_date, end_date <= end_date), allow.cartesian = TRUE, nomatch = NULL] # 分组统计转宽表,无匹配项填充0 res <- dcast(res, id + start_date + end_date ~ sport, fun.aggregate = length, value.var = "sport", fill = 0) # 重命名列符合输出要求 setnames(res, old = c("A", "B"), new = c("sportA", "sportB")) # 按id和日期排序 setorder(res, id, start_date)
逻辑说明
- 时间断点提取:将所有运动的开始日期、结束日期+1作为区间分割点,确保生成的每个子区间要么完全落在某类运动的参与时段内,要么完全不重叠,避免区间拆分错误
- 非等值连接:利用data.table高度优化的非等值连接匹配区间和运动记录,性能远高于逐行循环判断
- 宽表转换:通过dcast直接生成各运动的0/1参与标记列,20类运动也仅需一次转换即可完成
运行后输出结果与示例期望完全一致。
内容的提问来源于stack exchange,提问作者user3102806
相关产品推荐
相关产品推荐

