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

如何用Pandas按班次类型分组日期并计算员工班次信息

问题描述

我有某公司员工不同班次的模拟数据,班次以每8天为一个周期,包含工作/休息组合,如5-3(工作5天、休息3天)、6-2、4-4等。

原始数据

DateEmployeeHours worked
2022-01-01Alice8
2022-01-02Alice8
2022-01-03Alice8
2022-01-03Bob8
2022-01-04Alice8
2022-01-04Bob8
2022-01-05Alice8
2022-01-05Bob8
2022-01-06Bob8
2022-01-07Bob8
2022-01-08Bob8
2022-01-09Alice8
2022-01-10Alice4
2022-01-11Alice8
2022-01-11Bob8

其中Alice的初始班次为5-3(1月1日至5日工作,6日至8日休息),Bob的初始班次为6-2(1月3日至8日工作,9日至10日休息)。

需求

用Pandas计算每位员工每天所属的班次类型,以及该班次内的具体天数(例如Alice1月3日是班次第3天,Bob则是第1天),且班次类型可能在每次周期结束后变更。

尝试过的方法

先处理单个员工:

  1. 将日期设为索引并补全日期间隙:
df.index = pd.DatetimeIndex(df['Date'])
df = df.reindex(pd.date_range("2022/01/01","2022/03/31"))
  1. 创建"working"列,工作日报1,休息日报0:
df['working'] = 1
df['working'][df['Hours worked'].isnull()] = 0

原本想使用可重置的1日滚动求和,但无法实现,也不知道如何无需循环推广到所有员工。

期望输出

DateEmployeeHours workedShift typeShift day
2022-01-01Alice85-31
2022-01-02Alice85-32
2022-01-03Alice85-33
2022-01-03Bob86-21
2022-01-04Alice85-34
2022-01-04Bob86-22
2022-01-05Alice85-35
2022-01-05Bob86-23
2022-01-06Bob86-24
2022-01-07Bob86-25
2022-01-08Bob86-26
2022-01-09Alice86-21
2022-01-10Alice46-22
2022-01-11Alice86-23
2022-01-11Bob85-31

解决方案

核心思路是按员工分组处理,利用Pandas的groupby+自定义函数,结合日期差划分周期,同时识别班次类型、计算班次内天数。

步骤1:数据预处理

先补全每个员工的所有日期记录,避免遗漏休息天:

import pandas as pd

# 转换日期格式
df['Date'] = pd.to_datetime(df['Date'])

# 生成目标日期范围
date_range = pd.date_range(start='2022-01-01', end='2022-03-31')

# 生成员工和日期的笛卡尔积,确保每个员工每天都有记录
full_index = pd.MultiIndex.from_product(
    [date_range, df['Employee'].unique()],
    names=['Date', 'Employee']
).to_frame(index=False)

# 合并原始数据,补全缺失工时为0
full_df = pd.merge(full_index, df, on=['Date', 'Employee'], how='left')
full_df['Hours worked'] = full_df['Hours worked'].fillna(0)
# 标记是否工作
full_df['working'] = (full_df['Hours worked'] > 0).astype(int)

步骤2:分组计算班次信息

为每个员工单独计算周期、班次类型和班次内天数:

def process_single_employee(group):
    # 按日期排序
    group = group.sort_values('Date').reset_index(drop=True)
    # 以员工第一条记录为起点,每8天划分一个周期
    group['cycle_num'] = ((group['Date'] - group['Date'].min()).dt.days) // 8
    
    # 统计每个周期的工作/休息天数,生成班次类型
    cycle_stats = group.groupby('cycle_num')['working'].agg(['sum', 'count'])
    cycle_stats['shift_type'] = cycle_stats['sum'].astype(str) + '-' + (cycle_stats['count'] - cycle_stats['sum']).astype(str)
    group = group.merge(cycle_stats['shift_type'], on='cycle_num', how='left')
    
    # 计算班次内天数:每个周期从1开始计数
    group['shift_day'] = group.groupby('cycle_num').cumcount() + 1
    return group

# 按员工分组处理
result_df = full_df.groupby('Employee').apply(process_single_employee).reset_index(drop=True)

# 整理成期望的输出格式
result_df = result_df[['Date', 'Employee', 'Hours worked', 'shift_type', 'shift_day']]
result_df.columns = ['Date', 'Employee', 'Hours worked', 'Shift type', 'Shift day']

# 筛选到目标日期范围(可选,匹配示例输出)
result_df = result_df[result_df['Date'] <= '2022-01-11'].sort_values(['Date', 'Employee'])

补充说明

如果班次类型是预设规则(而非通过工时统计),可以提前维护一个员工班次变更表(包含员工、生效日期、班次类型),再通过日期区间匹配的方式关联班次类型,之后再按周期计算班次内天数。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 22:05:18