You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pandas数据转换未达预期:如何拆分同申请日的分段休假数据?

问题描述

现有如下结构的DataFrame:

Name        date    leave   marked_leave_bfr_days
   A    8/1/2021        1                       3
   A    8/2/2021        1                       4
   A    8/3/2021        1                       5
   A    8/4/2021        1                       5
   A    8/5/2021        1                       6
   A    8/6/2021        1                       7
   A    8/7/2021        1                       8
   A    8/8/2021        0                      -1
   A    8/9/2021        0                      -1
   A   8/10/2021        1                      12
   A   8/11/2021        1                      13
   A   8/12/2021        0                      -1
   B    8/4/2021        1                       1
   B    8/5/2021        1                       1
   B    8/6/2021        1                       3
   B    8/7/2021        0                      -1
   B    8/8/2021        0                      -1
   B    8/9/2021        0                      -1
   B   8/10/2021        0                      -1
   B   8/11/2021        0                      -1

字段说明:

  • Name:员工编码
  • leave:布尔字段(1=休假,0=不休假)
  • marked_leave_bfr_days:休假申请距离date的提前天数

需要转换为如下结构的DataFrame:

Name        date    leave   marked_leave_bfr_days   leave_applied   leave_start   leave_end no_of_leaves
   A    8/1/2021        1                       3       7/29/2021      8/1/2021    8/3/2021            3
   A    8/2/2021        1                       4       7/29/2021      8/1/2021    8/3/2021            3
   A    8/3/2021        1                       5       7/29/2021      8/1/2021    8/3/2021            3
   A    8/4/2021        1                       5       7/30/2021      8/4/2021    8/7/2021            4
   A    8/5/2021        1                       6       7/30/2021      8/4/2021    8/7/2021            4
   A    8/6/2021        1                       7       7/30/2021      8/4/2021    8/7/2021            4
   A    8/7/2021        1                       8       7/30/2021      8/4/2021    8/7/2021            4
   A    8/8/2021        0                      -1              -1            -1          -1           -1
   A    8/9/2021        0                      -1              -1            -1          -1           -1
   A   8/10/2021        1                      12       7/29/2021     8/10/2021   8/11/2021            2
   A   8/11/2021        1                      13       7/29/2021     8/10/2021   8/11/2021            2
   A   8/12/2021        0                      -1              -1            -1          -1           -1
   B    8/4/2021        1                       1        8/3/2021      8/4/2021    8/4/2021            1
   B    8/5/2021        1                       1        8/4/2021      8/5/2021    8/5/2021            1
   B    8/6/2021        1                       3        8/3/2021      8/6/2021    8/6/2021            1
   B    8/7/2021        0                      -1              -1            -1          -1           -1
   B    8/8/2021        0                      -1              -1            -1          -1           -1
   B    8/9/2021        0                      -1              -1            -1          -1           -1
   B   8/10/2021        0                      -1              -1            -1          -1           -1
   B   8/11/2021        0                      -1              -1            -1          -1           -1

用户当前使用的代码:

df.loc[df.leave==1, 'leave_applied'] = (df['date'] - df['marked_leave_bfr_days'].map(timedelta))
df = df[df.leave==1].groupby(['Name', 'leave_applied']).agg({'date':['min', 'max']}).reset_index()

问题:该代码会将同一员工、同一申请日的非连续休假日期段合并(比如员工A在7/29/2021申请的8/1-8/3和8/10-8/11会被合并成一个时间段),无法得到预期结果。


解决方案

核心思路:先识别同一员工、同一申请日内的连续休假日期段,再按这个分段进行聚合,最后合并回原表。

完整代码

import pandas as pd
from datetime import timedelta

# 1. 读取并预处理数据:转换date列为datetime格式
df = pd.read_csv('your_data.csv')  # 替换为你的数据读取方式
df['date'] = pd.to_datetime(df['date'])

# 2. 计算leave_applied列,非休假记录先设为NaN
df['leave_applied'] = df.apply(
    lambda row: row['date'] - timedelta(days=row['marked_leave_bfr_days']) if row['leave'] == 1 else pd.NA,
    axis=1
)

# 3. 仅处理休假记录,识别连续日期段
leave_df = df[df['leave'] == 1].copy()
# 按Name、leave_applied分组,判断当前日期与前一日是否连续,生成分组标签
leave_df['date_diff'] = leave_df.groupby(['Name', 'leave_applied'])['date'].diff().dt.days
leave_df['segment'] = (leave_df['date_diff'] != 1).cumsum()

# 4. 按Name、leave_applied、segment聚合,得到每个休假段的信息
agg_df = leave_df.groupby(['Name', 'leave_applied', 'segment']).agg(
    leave_start=('date', 'min'),
    leave_end=('date', 'max'),
    no_of_leaves=('date', 'count')
).reset_index(drop=['segment'])

# 5. 将聚合结果合并回原DataFrame
df = df.merge(agg_df, on=['Name', 'leave_applied'], how='left')

# 6. 处理非休假记录的填充,将NaN替换为'-1'
fill_values = {
    'leave_applied': '-1',
    'leave_start': '-1',
    'leave_end': '-1',
    'no_of_leaves': -1
}
df = df.fillna(fill_values)

# 可选:将日期列转换回原字符串格式(如需要)
df['date'] = df['date'].dt.strftime('%m/%d/%Y')
df['leave_applied'] = df.apply(
    lambda row: row['leave_applied'].strftime('%m/%d/%Y') if row['leave'] == 1 else row['leave_applied'],
    axis=1
)
df['leave_start'] = df.apply(
    lambda row: row['leave_start'].strftime('%m/%d/%Y') if row['leave'] == 1 else row['leave_start'],
    axis=1
)
df['leave_end'] = df.apply(
    lambda row: row['leave_end'].strftime('%m/%d/%Y') if row['leave'] == 1 else row['leave_end'],
    axis=1
)

print(df)

代码说明

  1. 日期预处理:将date列转换为datetime格式,确保日期计算的准确性。
  2. 计算申请日期:对休假记录计算leave_applied,非休假记录设为缺失值。
  3. 识别连续休假段:
    • 按Name和leave_applied分组,计算当前日期与前一日的差值。
    • 当差值不等于1时,说明是新的休假段,通过cumsum()生成分段标签。
  4. 聚合分段信息:按Name、leave_applied和分段标签聚合,得到每个休假段的开始/结束日期、天数。
  5. 合并与填充:将聚合结果合并回原表,非休假记录的新增字段填充为-1。
  6. 格式转换(可选):如果需要将日期列转回原字符串格式,添加最后几步的格式转换。

内容的提问来源于stack exchange,提问作者Lata

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 03:21:00