求基于分组时间区间计算个体活跃月数的data.table/dplyr实现方案
计算个体累计活跃月数(去重重叠区间)
嗨,我帮你整理了两种分别用dplyr和data.table实现的方案,核心都是先合并每个个体的重叠/连续活动区间,再计算累计的活跃月数,完全匹配你的需求:
dplyr 实现方案
需要用到dplyr做分组处理,lubridate来处理日期区间:
# 加载所需包 library(dplyr) library(lubridate) # 你的示例数据 df <- data.frame( id=c("x", "y", "y", "z", "z"), activity=c("a1", "b1", "b2", "c1", "c2"), start=c(as.Date("2017-07-01"), as.Date("2018-04-01"), as.Date("2018-07-01"), as.Date("2017-07-01"), as.Date("2018-02-01")), end=c(as.Date("2018-07-31"), as.Date("2018-05-31"), as.Date("2018-07-31"), as.Date("2018-02-28"), as.Date("2018-07-31")) ) # 计算累计活跃月数 result_dplyr <- df %>% group_by(id) %>% # 将每条活动记录转为时间区间 mutate(activity_interval = interval(start, end)) %>% # 合并所有重叠或连续的区间 summarise(merged_intervals = reduce(activity_interval, ~union(..1, ..2))) %>% # 计算合并后区间的总活跃月数,取整得到整数 mutate(active_months = as.integer(round(sum(interval_length(merged_intervals, "month"))))) %>% select(id, active_months) print(result_dplyr)
运行后会得到你期望的结果:
# A tibble: 3 × 2 id active_months <chr> <int> 1 x 13 2 y 3 3 z 13
data.table 实现方案
用data.table的高效分组和操作来实现,同样依赖lubridate计算区间长度:
# 加载所需包 library(data.table) library(lubridate) # 转换为data.table格式 setDT(df) # 计算累计活跃月数 result_dt <- df[, .(start, end), by = id][ # 按id和开始日期排序 order(start), # 生成分组标识,合并重叠/连续区间 .(start = first(start), end = max(end)), by = .(id, grp = cumsum(c(TRUE, start[-1] > shift(end)[-1]))) ][ # 按id分组,计算合并后区间的总活跃月数 , .(active_months = as.integer(round(sum(interval_length(interval(start, end), "month"))))), by = id ] print(result_dt)
运行结果和dplyr方案一致:
id active_months 1: x 13 2: y 3 3: z 13
关键思路说明
两种方案的核心都是先合并重叠区间,避免重复计算活跃月份:
- 对于个体y,两条活动区间
2018-04-01至2018-05-31和2018-07-01至2018-07-31没有重叠,所以累计是2+1=3个月 - 对于个体z,两条区间
2017-07-01至2018-02-28和2018-02-01至2018-07-31有重叠,合并后是2017-07-01至2018-07-31,共13个月
内容的提问来源于stack exchange,提问作者Antoine
相关产品推荐
相关产品推荐

