如何按日期间隔拆分分组并合并个体地点的连续时间区间?
合并个体在同一地点的连续时间区间
我正在处理日期相关数据,每行代表一个个体在某地点的一个时间区间,数据示例如下:
# 示例数据 library(tidyverse) library(lubridate) df <- tibble( ind = c(1,1,1,1,1,1,2,2,3,3,3,3), place = c(1,1,1,4,4,4,2,2,3,3,3,3), start_date = ymd(c("2011-01-04", "2011-01-05", "2011-01-10", "2010-12-30", "2010-12-31", "2011-01-01", "2018-02-17", "2018-02-19", "2018-02-08", "2018-02-13", "2018-02-16", "2018-03-27")), end_date = ymd(c("2011-01-05", "2011-01-06", "2011-01-11", "2010-12-31", "2011-01-01", "2011-01-03", "2018-02-19", "2018-02-23", "2018-02-13", "2018-02-16", "2018-02-20", "2018-03-29")) )
可以看到,部分行的start_date与上一行的end_date相同,属于连续的时间区间;但有些行和上一行之间存在间隔,需要先将这些组拆分为多个子组,再合并同一子组内的时间区间。
我需要先生成分组标识(group列),得到中间结果:
# 中间结果 # ind place start_date end_date group # <int> <int> <date> <date> <int> # 1 1 1 2011-01-04 2011-01-05 1 # 2 1 1 2011-01-05 2011-01-06 1 # 3 1 1 2011-01-10 2011-01-11 2 # 4 1 4 2010-12-30 2010-12-31 3 # 5 1 4 2010-12-31 2011-01-01 3 # 6 1 4 2011-01-01 2011-01-03 3 # 7 2 2 2018-02-17 2018-02-19 4 # 8 2 2 2018-02-19 2018-02-23 4 # 9 3 3 2018-02-08 2018-02-13 5 # 10 3 3 2018-02-13 2018-02-16 5 # 11 3 3 2018-02-16 2018-02-20 5 # 12 3 3 2018-03-27 2018-03-29 6
最终合并后得到:
# 最终结果 # ind place start_date end_date # <int> <int> <date> <date> # 1 1 1 2011-01-04 2011-01-06 # 2 1 1 2011-01-10 2011-01-11 # 3 1 4 2010-12-30 2011-01-03 # 4 2 2 2018-02-17 2018-02-23 # 5 3 3 2018-02-08 2018-02-20 # 6 3 3 2018-03-27 2018-03-29
请问该如何实现?
解决方案(使用tidyverse)
可以通过分组后标记连续区间,再按新的分组合并时间:
步骤1:生成连续区间的分组标识
先按ind和place分组,然后判断当前行的start_date是否等于上一行的end_date,如果不等则标记为新组,最后累计求和生成唯一的group值:
df_with_group <- df %>% arrange(ind, place, start_date) %>% # 确保数据按个体、地点、时间排序 group_by(ind, place) %>% mutate( # 标记是否为新组:第一行或当前start_date不等于上一行end_date is_new_group = ifelse(row_number() == 1 | start_date != lag(end_date), 1, 0), # 组内累计求和生成组ID group = cumsum(is_new_group) ) %>% ungroup() %>% # 全局统一group编号(和示例中的全局group一致) mutate(group = cumsum(is_new_group))
步骤2:按新分组合并时间区间
按ind、place和group分组,取每组的最小start_date和最大end_date:
final_df <- df_with_group %>% group_by(ind, place, group) %>% summarise( start_date = min(start_date), end_date = max(end_date), .groups = "drop" ) %>% select(-group) # 移除临时分组列
运行后就能得到你需要的最终结果。
说明
- 必须先确保数据按
ind、place、start_date排序,否则判断连续区间会出错; is_new_group列用来标记每个新的连续区间的起始行,cumsum会把连续的行归为同一个组;- 若不需要全局唯一的
group编号(仅在ind+place内唯一即可),可以去掉最后一行的mutate(group = cumsum(is_new_group))。
内容的提问来源于stack exchange,提问作者jrdavalos
相关产品推荐
相关产品推荐

