Pandas按ID和月份统计两时间戳列间隔小时数的实现方法
跨月休假按ID+月份统计小时数的实现方案
这个需求最容易踩的坑是:当单条休假记录跨越自然月边界时,不能直接把整段时长算到休假开始的月份,必须按自然月区间拆分每一段的时长,再逐段累加统计,刚好覆盖示例中员工x2从4月跨到5月的休假场景。
第一步:基础数据预处理
先把原始时间字符串转成pandas可识别的datetime格式,同时清洗掉示例中end列存在的尾随空格脏数据:
import pandas as pd # 复现你的示例DataFrame df = pd.DataFrame({ 'id': ['x1', 'x1', 'x1', 'x2', 'x2', 'x2', 'x2'], 'beg': [ '2021-01-01 00:00:00', '2021-02-03 00:00:00', '2021-02-04 00:00:00', '2021-02-05 00:00:00', '2021-02-06 00:00:00', '2021-03-05 00:00:00', '2021-04-01 00:00:00' ], 'end': [ '2021-01-02 00:00:00 ', '2021-02-03 12:00:00', '2021-02-04 10:00:00', '2021-02-05 10:00:00', '2021-02-06 10:00:00', '2021-03-07 10:00:00', '2021-05-08 00:00:00' ] }) # 字符串去空白后转datetime格式 df['beg'] = pd.to_datetime(df['beg'].str.strip()) df['end'] = pd.to_datetime(df['end'].str.strip())
第二步:核心跨月拆分逻辑
对每一条休假记录,自动识别区间内所有自然月的分界点(每个月1号的零点),把整段跨月的休假拆成若干个不跨月的小段,逐段计算小时数:
def get_split_segments(start_time, end_time): # 生成覆盖整个休假区间所有自然月的月初时间点 month_edges = pd.date_range( start=start_time - pd.offsets.MonthBegin(1), end=end_time + pd.offsets.MonthBegin(1), freq='MS' ) # 把休假起点、终点和所有月边界合并、去重、排序,得到拆分节点 split_points = sorted(set([start_time, end_time] + month_edges.tolist())) # 筛出真正在休假区间内的节点,组成每一段的起止对 valid_points = [p for p in split_points if start_time <= p <= end_time] segments = [] for i in range(len(valid_points)-1): seg_start = valid_points[i] seg_end = valid_points[i+1] seg_hour = (seg_end - seg_start).total_seconds() / 3600 # 记录这段时长所属的月份,格式和预期一致 segments.append({ 'month': seg_start.strftime('%m/%Y'), 'hours': seg_hour }) return segments # 逐行拆分所有休假记录 detail_rows = [] for _, row in df.iterrows(): segs = get_split_segments(row['beg'], row['end']) for seg in segs: detail_rows.append({ 'id': row['id'], 'month': seg['month'], 'leave_hours': seg['hours'] }) split_detail = pd.DataFrame(detail_rows)
第三步:分组聚合得到最终结果
按员工ID、所属月份分组,累加对应小时数:
final_result = split_detail.groupby(['id', 'month'], as_index=False)['leave_hours'].sum()
按照自然月拆分的结果规则和你的预期完全匹配:
- x1 01/2021:24小时
- x1 02/2021:22小时(12小时+10小时)
- x2 02/2021:20小时(10小时+10小时)
- x2 03/2021:58小时(3月5日到3月7日10点的累计时长)
- x2 04/2021:4月自然月内的累计休假时长
- x2 05/2021:168小时(5月1日到5月8日的7天累计时长)
生产环境优化提示
- 如果数据量超过十万行,逐行循环的效率会偏低,可以改用pandas矢量化的时间区间合并逻辑,处理速度可以提升一个数量级
- 如果需要排除非工作日的休假统计,只需要在拆分出每段时间之后,加一层工作日标签校验,过滤掉非工作时间的时长即可
- 原始数据清洗时一定要处理字符串前后的空白字符,不然时间解析阶段会出现隐性错误
内容的提问来源于stack exchange,提问作者biwia
相关产品推荐
相关产品推荐

