基于日期区间为分组动态分配ep_id的实现方法
按ID分组并基于90天间隔分配ep_id
数据集示例
library(tidyverse) id <- c(1,1,1,2,2,2,3,3,4) date <- c("2020-01-01","2020-02-04", "2021-06-08","2022-05-08","2021-08-09","2021-11-29","2020-04-12", "2022-09-12", "2022-09-22") df <- data.frame(id,date) # 查看数据集 df %>% as_tibble()
输出:
# A tibble: 9 × 2 id date <dbl> <chr> 1 1 2020-01-01 2 1 2020-02-04 3 1 2021-06-08 4 2 2022-05-08 5 2 2021-08-09 6 2 2021-11-29 7 3 2020-04-12 8 3 2022-09-12 9 4 2022-09-22
预期输出
# A tibble: 9 × 3 id date ep_id <dbl> <chr> <dbl> 1 1 2020-01-01 1 2 1 2020-02-04 1 3 1 2021-06-08 2 4 2 2022-05-08 2 5 2 2021-08-09 1 6 2 2021-11-29 2 7 3 2020-04-12 1 8 3 2022-09-12 2 9 4 2022-09-22 1
解决方案
核心思路
- 将字符串格式的日期转为日期类型,便于计算间隔
- 按
id分组后,先对每组内的日期排序 - 计算当前日期与前一个日期的间隔,超过90天则标记为新周期
- 累加标记生成连续的
ep_id
代码实现
df %>% mutate(date = as.Date(date)) %>% group_by(id) %>% arrange(date, .by_group = TRUE) %>% mutate( # 计算与前一个日期的间隔,第一个日期默认与自身间隔为0 days_diff = date - lag(date, default = first(date)), # 间隔超过90天标记为新周期 new_ep = ifelse(days_diff > 90, 1, 0), # 累加标记生成ep_id ep_id = cumsum(new_ep) + 1 ) %>% ungroup() %>% # 恢复原数据的行顺序 arrange(match(row_number(), as.integer(rownames(df)))) %>% select(id, date, ep_id)
结果说明
运行上述代码后,id=2的2022-05-08对应的ep_id为3,与你给出的预期不同——原因是该日期与前一个日期2021-11-29间隔160天,超过90天,按逻辑应开启新周期。如果你的预期是笔误,上述代码符合“相邻日期间隔超90天则分配新ep_id”的规则;如果需求是其他逻辑,可调整判断条件。
扩展:基于聚类的分组(任意日期间隔≤90天归为同一ep_id)
如果需要将同一ep_id内所有日期两两间隔不超过90天,可使用聚类算法实现:
library(dbscan) df %>% mutate(date = as.Date(date)) %>% group_by(id) %>% mutate( # 将日期转为数值(自1970-01-01的天数) date_num = as.numeric(date), # 以90天为聚类半径,分配ep_id ep_id = dbscan(matrix(date_num, ncol=1), eps=90, minPts=1)$cluster ) %>% ungroup() %>% select(id, date, ep_id) %>% arrange(match(row_number(), as.integer(rownames(df))))
内容的提问来源于stack exchange,提问作者a nony mouse
相关产品推荐
相关产品推荐

