如何用向量化方法计算多重叠/间断时段的办公室总占用天数?
问题描述
我们需要计算办公室的总占用天数,给定不同角色的出勤时间段(如下表),需合并重叠/连续的占用时段后计算总天数。已知合并后的连续时段为1-20日、31-35日、41-55日,总天数为40天。
| type | day_in | day_out |
|---|---|---|
| A | 1 | 10 |
| A | 5 | 15 |
| A | 31 | 35 |
| B | 5 | 15 |
| C | 10 | 20 |
| C | 45 | 55 |
| D | 41 | 50 |
现有基于for循环的实现,但希望找到向量化的更优解法。
方法1:dplyr合并重叠区间
核心思路是提取所有时间段并排序,通过分组标记合并重叠/连续区间,最后计算总天数。
library(dplyr) df_raw <- read.table( header = TRUE, text = " type day_in day_out A 1 10 A 5 15 A 31 35 B 5 15 C 10 20 C 45 55 D 41 50 " ) # 合并重叠/连续区间 merged_intervals <- df_raw %>% select(day_in, day_out) %>% arrange(day_in) %>% mutate( # 标记新的独立区间:当前起始日 > 上一个结束日+1时,分组编号+1 group = cumsum(day_in > lag(day_out, default = -Inf) + 1) ) %>% group_by(group) %>% summarise( start = min(day_in), end = max(day_out) ) # 计算总占用天数 total_days <- sum(merged_intervals$end - merged_intervals$start + 1) total_days # 输出40
思路说明
- 提取所有出勤时段,按起始日排序。
- 用
cumsum生成分组:当当前区间的起始日与上一个区间不连续时,视为新分组。 - 按分组合并,取每组的最小起始日和最大结束日,得到合并后的连续区间。
- 每个区间的天数为
end - start + 1,求和得到总占用天数。
方法2:data.table高效合并
针对大数据量场景,data.table的向量化操作效率更高:
library(data.table) setDT(df_raw) # 排序并合并区间 merged_intervals <- df_raw[, .(day_in, day_out)][ order(day_in), .(start = min(day_in), end = max(day_out)), by = .(group = cumsum(day_in > shift(day_out, fill = -Inf) + 1)) ] # 计算总天数 total_days <- sum(merged_intervals$end - merged_intervals$start + 1) total_days # 输出40
方法3:lubridate区间原生处理
利用lubridate的区间操作函数,更直观地处理重叠/连续逻辑:
library(lubridate) library(dplyr) # 转换为lubridate区间对象 intervals <- df_raw %>% mutate(interval = interval(day_in, day_out)) %>% pull(interval) # 迭代合并所有重叠/连续区间 merged_intervals <- reduce(intervals, function(x, y) { if (int_overlaps(x, y) | int_adjacent(x, y)) union(x, y) else x }) # 计算总天数:int_length返回天数差,每个区间需+1包含起始日 total_days <- sum(int_length(merged_intervals)) + length(merged_intervals) total_days # 输出40
思路说明
int_overlaps判断区间是否重叠,int_adjacent判断是否连续(如前一个结束日为10,后一个起始日为11)。reduce迭代合并所有区间,最终得到无重叠的连续区间集合。int_length返回区间的天数差,因此每个区间需加1来覆盖起始日,再求和得到总天数。
方案优势对比
- 完全向量化操作,避免
for循环的性能瓶颈,数据量越大效率提升越明显。 - 代码逻辑清晰简洁,减少手动状态管理的复杂度,易读易维护。
- 依赖成熟的第三方包内置函数,稳定性更高。
内容的提问来源于stack exchange,提问作者moreQthanA
相关产品推荐
相关产品推荐

