R语言:计算日期差值时排除周末及各州专属节假日
计算跨州专属节假日的工作日天数(R语言实现)
这个需求很常见呀,尤其是做跨州业务的数据分析时,得精准对应每个州的节假日来算工作日。我给你两种实用的解法,你可以根据自己的数据量和习惯选择:
先明确示例数据
先把你给出的示例数据补全整理好,方便后续测试:
# 节假日表:日期 + 对应州 holiday <- data.frame( h = as.Date(c("2016/05/3", "2016/05/3"), '%Y/%m/%d'), s = c('state1','state2'), stringsAsFactors = FALSE ) # 待计算数据集:起始日期、结束日期、所属州 d <- data.frame( d1 = as.Date(c('2016/05/2','2016/05/2'), '%Y/%m/%d'), d2 = as.Date(c('2016/05/10','2016/05/10'), '%Y/%m/%d'), s = c('state1','state3'), stringsAsFactors = FALSE )
方法一:用dplyr+lubridate逐行处理(直观易理解)
这种方法适合数据量不大的场景,步骤清晰,容易调试:
步骤说明
- 为每条记录生成起止日期之间的所有日期序列
- 过滤掉周末(仅保留周一到周五)
- 过滤掉当前州对应的专属节假日
- 统计剩余的工作日数量
代码实现
# 加载需要的包 library(dplyr) library(lubridate) # 逐行计算工作日天数 result <- d %>% rowwise() %>% # 生成当前记录的日期序列 mutate(date_seq = list(seq(d1, d2, by = "day"))) %>% # 把日期序列展开成每行一个日期 unnest(date_seq) %>% # 过滤周末:wday()返回1=周日,7=周六,保留2-6(周一到周五) filter(wday(date_seq) %in% 2:6) %>% # 左连接节假日表,匹配州和日期 left_join(holiday, by = c("s" = "s", "date_seq" = "h")) %>% # 去掉匹配到的节假日(即h列不为空的行) filter(is.na(h)) %>% # 按原记录分组,统计剩余天数 group_by(d1, d2, s) %>% summarise(work_days = n(), .groups = "drop") # 查看结果 print(result)
输出结果
# A tibble: 2 × 4 d1 d2 s work_days <date> <date> <chr> <int> 1 2016-05-02 2016-05-10 state1 6 2 2016-05-02 2016-05-10 state3 7
- state1因为有5月3日的节假日,再加上两个周末,总共排除3天,剩余6个工作日
- state3没有专属节假日,只排除两个周末,剩余7个工作日
方法二:用bizdays包创建专属日历(高效适合大数据)
如果你的数据集很大,生成所有日期序列会比较耗时,bizdays包专门处理工作日计算,效率更高:
步骤说明
- 创建基础工作日历(默认排除周末)
- 为每个有专属节假日的州创建自定义日历
- 注册这些日历,然后批量计算每条记录的工作日天数
代码实现
# 加载需要的包 library(bizdays) library(dplyr) library(purrr) # 1. 创建基础日历:排除周末 cal_base <- create.calendar( name = "base_cal", weekdays = c("saturday", "sunday"), adjust.from = adjust.next, adjust.to = adjust.previous ) # 2. 为每个州创建专属日历(添加对应州的节假日) state_cals <- holiday %>% group_by(s) %>% summarise(holidays = list(h), .groups = "drop") %>% mutate( cal_name = paste0("cal_", s), # 基于基础日历,添加当前州的节假日 calendar = map2(cal_name, holidays, ~create.calendar( name = .x, holidays = .y, weekdays = c("saturday", "sunday"), adjust.from = adjust.next, adjust.to = adjust.previous, base.calendar = "base_cal" )) ) # 3. 注册所有州的日历到bizdays中 walk(state_cals$calendar, register.calendar) # 4. 批量计算每条记录的工作日天数 result <- d %>% mutate( # 匹配当前州对应的日历,没有专属节假日的用基础日历 cal_name = ifelse(s %in% state_cals$s, paste0("cal_", s), "base_cal"), # 计算工作日天数 work_days = bizdays(d1, d2, cal_name) ) %>% select(d1, d2, s, work_days) # 查看结果 print(result)
这个方法的输出和方法一完全一致,但处理大数据时速度会快很多。
内容的提问来源于stack exchange,提问作者Choc_waffles
相关产品推荐
相关产品推荐

