如何合并DataFrame中连续及跨周末的年假记录并设置合并阈值?
解决年假记录合并问题的方案
核心思路
不再逐行更新字段,而是按员工分组后,将所有属于同一可合并区间的记录归为一组,最后一次性聚合,这样能完美处理连续多行单日休假的情况,同时支持自定义合并阈值。
步骤实现
1. 数据预处理
先确保日期列是datetime类型,并按员工ID和休假开始日期排序:
import pandas as pd import numpy as np # 假设你的原始数据是df df['StartDate'] = pd.to_datetime(df['StartDate']) df['EndDate'] = pd.to_datetime(df['EndDate']) df = df.sort_values(['员工ID', 'StartDate']).reset_index(drop=True)
2. 标记可合并的记录
按员工分组,判断当前行是否需要与上一行合并,覆盖你提到的两类场景+自定义阈值:
# 定义合并条件: # - 连续单日休假(当前开始日 = 上一行结束日+1) # - 周五结束后接周一休假(间隔2天,且上一行结束日是周五,当前开始日是周一) # - 自定义间隔阈值(比如间隔<3天,可改为5等) df['merge_flag'] = df.groupby('员工ID').apply( lambda x: ( (x['StartDate'] == x['EndDate'].shift(1) + pd.Timedelta(days=1)) | ((x['StartDate'] == x['EndDate'].shift(1) + pd.Timedelta(days=2)) & (x['EndDate'].shift(1).dt.weekday == 4) & # 周五的weekday是4(周一=0,周日=6) (x['StartDate'].dt.weekday == 0)) | ((x['StartDate'] - x['EndDate'].shift(1)).dt.days < 3) # 这里的3是自定义阈值,按需修改 ) ).reset_index(drop=True) # 第一行没有上一行,标记为False df['merge_flag'] = df['merge_flag'].fillna(False)
3. 生成合并组ID
通过反向标记(不需要合并的行为新组起点),用累计求和生成唯一组ID:
# 每遇到不需要合并的行,组ID加1 df['group_id'] = (~df['merge_flag']).cumsum()
4. 聚合合并结果
按员工ID和组ID聚合,取最早的开始日、最晚的结束日,累加工作日:
merged_df = df.groupby(['员工ID', 'group_id']).agg( StartDate=('StartDate', 'min'), EndDate=('EndDate', 'max'), WorkingDays=('WorkingDays', 'sum') ).reset_index(drop=True)
为什么之前的方法失效?
你之前用np.where逐行更新字段,只能处理相邻两行的合并,无法覆盖连续3行及以上的场景(比如3个单日休假,第一次合并前两行后,第三行无法关联到合并后的行)。而分组聚合的方式,会把所有属于同一合并区间的记录归为一组,一次性完成合并,也无需手动删除已合并行。
自定义阈值调整
如果需要将合并阈值改为间隔小于5天,只需把代码中(x['StartDate'] - x['EndDate'].shift(1)).dt.days < 3的3改成5即可。如果要按工作日间隔判断(忽略周末),可以用np.busday_count计算工作日差:
# 替换之前的间隔计算逻辑为工作日间隔 df['workday_diff'] = df.groupby('员工ID').apply( lambda x: np.busday_count(x['EndDate'].shift(1).dt.date, x['StartDate'].dt.date) ).reset_index(drop=True) # 合并条件改为工作日间隔<2(即中间无工作日) df['merge_flag'] = df.groupby('员工ID').apply( lambda x: (x['workday_diff'] == 0) ).reset_index(drop=True)
内容的提问来源于stack exchange,提问作者mzietal
相关产品推荐
相关产品推荐

