在R语言中创建连续无间隔的时间周期
合并重叠/连续日期区间的解决方案
原始数据
首先定义输入的数据框:
library(tidyverse) library(lubridate) df <- tribble( ~person, ~start, ~end, '1', '2000-01-01', '2000-12-31', '1', '2001-01-01', '2002-07-31', '1', '2001-04-01', '2001-06-30', '1', '2001-08-01', '2001-12-31', '1', '2002-01-01', '2002-12-31', '2', '2000-01-01', '2000-11-30', '2', '2001-01-01', '2001-12-31') |> mutate(start = lubridate::as_date(start), end = lubridate::as_date(end))
需求目标
需要将每个person的重叠或连续无间隔的日期区间合并,得到如下输出:
person start end 1 2000-01-01 2002-12-31 2 2000-01-01 2001-11-30 2 2001-01-01 2001-12-31
原代码问题
你之前的代码直接取了每个person的最早开始和最晚结束,忽略了区间间的实际间隔(比如person 2的两个区间存在间隔,不能合并):
df |> distinct() |> arrange(person, start, end) |> mutate( gap = start - lag(end, default = min(start)) ) |> group_by(person) |> summarise( MIN_START = min(start), MAX_END = max(end) )
正确解决方案
核心思路是:按person分组后,先排序日期,标记出所有独立的区间(当前行start晚于上一行end时,视为新区间),最后按标记的区间聚合最小start和最大end。
代码如下:
df |> distinct() |> arrange(person, start) |> group_by(person) |> # 生成区间组ID:当前start > 上一行end时,触发新组 mutate( group_id = cumsum(start > lag(end, default = start[1] - days(1))) ) |> # 按person和区间组聚合,取每组的起止日期 group_by(person, group_id) |> summarise( start = min(start), end = max(end), .groups = "drop" ) |> select(-group_id) # 移除临时组ID列
代码解释
distinct():去重,避免重复区间干扰计算arrange(person, start):确保每个用户的日期按开始时间排序group_by(person):按用户单独处理日期区间cumsum(start > lag(end, default = start[1] - days(1))):通过累加判断生成区间ID,当当前行开始日期晚于上一行结束日期时,生成新组;默认值设为start[1]-days(1),保证第一行的组ID从1开始- 再次分组聚合:按用户和区间ID取每组的最小开始日期和最大结束日期,最后移除临时的组ID列
运行这段代码即可得到预期的合并结果。
内容的提问来源于stack exchange,提问作者cowboy
相关产品推荐
相关产品推荐

