R语言:如何合并跨周末/银行假日的事件日期?数据框处理求助
问题描述
我有如下结构的DataFrame:
| Event ID | Start Date | End Date |
|---|---|---|
| 1 | 06/02/2024 | 09/02/2024 |
| 1 | 22/05/2024 | 24/05/2024 |
| 1 | 28/05/2024 | 06/06/2024 |
| 2 | 27/06/2024 | 27/06/2024 |
| 2 | 28/06/2024 | 28/06/2024 |
我需要实现的效果是:将跨周末或银行假日的日期合并为单个事件,同时保留其他独立条目,处理后的DataFrame如下:
| Event ID | Start Date | End Date |
|---|---|---|
| 1 | 06/02/2024 | 09/02/2024 |
| 1 | 22/05/2024 | 06/06/2024 |
| 2 | 27/06/2024 | 28/06/2024 |
具体要求:
- 对于Event ID 1,合并因5月25日-27日长周末产生间隔的两个事件,保留2月的独立事件;
- 对于Event ID 2,将连续日期的两个事件合并为单个事件。
我尝试了以下代码,但未达到预期效果:
event_data <- data.frame( `Event ID` = c(1, 1, 1, 2, 2), `Start Date` = c("06/02/2024", "22/05/2024", "28/05/2024", "27/06/2024", "28/06/2024"), `End Date` = c("09/02/2024", "24/05/2024", "06/06/2024", "27/06/2024", "28/06/2024") ) consolidated_data <- event_data %>% arrange(Event_ID, Start_Date) %>% group_by(Event_ID) %>% mutate( # Identify if the current Start_Date is consecutive or overlapping with the previous End_Date previous_end_date = lag(End_Date), is_consecutive = ifelse(!is.na(previous_end_date) & (Start_Date <= previous_end_date + 1), TRUE, FALSE), date_group = cumsum(!is_consecutive) ) %>% group_by(Event_ID, date_group ) %>% summarise( Start_Date = min(Start_Date), End_Date = max(End_Date), .groups = 'drop' ) %>% ungroup() print(consolidated_data)
解决方案
你的代码存在两个核心问题:
- 日期列是字符类型,无法直接进行日期运算;
- 仅判断了连续1天的间隔,未考虑周末/银行假日的间隔情况。
以下是修正后的代码,核心思路是:
- 将字符型日期转换为日期类型;
- 自定义判断逻辑:如果当前事件的开始日期与上一个事件的结束日期之间的所有日期都是周末或银行假日,则视为可合并的事件;
- 这里以英国2024年的银行假日为例(5月27日是Spring Bank Holiday),你可以根据实际地区调整假日列表。
library(dplyr) library(lubridate) # 原始数据 event_data <- data.frame( `Event ID` = c(1, 1, 1, 2, 2), `Start Date` = c("06/02/2024", "22/05/2024", "28/05/2024", "27/06/2024", "28/06/2024"), `End Date` = c("09/02/2024", "24/05/2024", "06/06/2024", "27/06/2024", "28/06/2024") ) # 转换日期格式为lubridate日期类型 event_data_clean <- event_data %>% mutate( Start_Date = dmy(`Start Date`), End_Date = dmy(`End Date`), `Event ID` = as.integer(`Event ID`) ) %>% select(`Event ID`, Start_Date, End_Date) # 定义目标地区的银行假日(以英国2024年为例) uk_holidays_2024 <- as_date(c( "2024-01-01", "2024-03-29", "2024-04-01", "2024-05-06", "2024-05-27", "2024-08-26", "2024-12-25", "2024-12-26" )) # 自定义函数:判断两个日期之间的所有日期是否都是周末或银行假日 is_all_holiday_or_weekend <- function(start, end) { if (start > end) return(FALSE) date_seq <- seq(start, end, by = "day") all( wday(date_seq) %in% c(1,7) | # 周末(周日=1,周六=7) date_seq %in% uk_holidays_2024 ) } # 合并事件 consolidated_data <- event_data_clean %>% arrange(`Event ID`, Start_Date) %>% group_by(`Event ID`) %>% mutate( # 获取上一个事件的结束日期 prev_end = lag(End_Date), # 判断当前事件是否与上一个事件可合并 merge_with_prev = case_when( is.na(prev_end) ~ FALSE, # 如果当前开始日期 <= 上一个结束日期+1,直接合并(连续或重叠) Start_Date <= prev_end + days(1) ~ TRUE, # 否则判断间隔日期是否全是假日/周末 TRUE ~ is_all_holiday_or_weekend(prev_end + days(1), Start_Date - days(1)) ), # 生成分组ID:每次不可合并时分组+1 group_id = cumsum(!merge_with_prev) ) %>% group_by(`Event ID`, group_id) %>% summarise( Start_Date = min(Start_Date), End_Date = max(End_Date), .groups = "drop" ) %>% # 转换回原日期格式 mutate( `Start Date` = format(Start_Date, "%d/%m/%Y"), `End Date` = format(End_Date, "%d/%m/%Y") ) %>% select(`Event ID`, `Start Date`, `End Date`) print(consolidated_data)
代码说明
- 日期类型转换:用
lubridate::dmy()将字符日期转为可运算的日期类型,这是日期处理的基础; - 假日定义:你需要根据业务所在地区调整
uk_holidays_2024的内容,确保包含所有需要考虑的银行假日; - 合并逻辑:
- 先判断是否为连续/重叠日期(间隔≤1天),这类直接合并;
- 对于间隔超过1天的情况,检查间隔内的所有日期是否都是周末或银行假日,是则合并;
- 分组合并:通过
cumsum(!merge_with_prev)生成分组ID,同一组内取最小开始日期和最大结束日期。
运行上述代码后,输出结果将完全符合你的预期:
# A tibble: 3 × 3 `Event ID` `Start Date` `End Date` <int> <chr> <chr> 1 1 06/02/2024 09/02/2024 2 1 22/05/2024 06/06/2024 3 2 27/06/2024 28/06/2024
内容的提问来源于stack exchange,提问作者ds_1234
相关产品推荐
相关产品推荐

