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

如何用Python实现人员日历日期的均衡排班分配

Python 均衡排班实现方案

核心实现逻辑

  • 第一步先加载CSV格式的排班规则,先把所有必排人员提前填充到对应日期的排班队列中,同时统计各人员初始排班次数,以及各日期剩余待填充的排班名额
  • 提前标记所有周末、节假日日期,分配排班时给这类日期单独设置统计维度,保证这类特殊日期的排班机会在所有人员中按比例均衡分配
  • 剩余名额分配阶段,每次选择排班候选人时,优先筛选「当前排班次数最少」+「不在当前日期禁排名单」+「近2天没有排班」的人员,避免排班过度集中或者连续排班的情况

代码实现

依赖准备

仅需要安装pandas处理CSV文件,内置库足够支撑其他逻辑:
pip install pandas

完整可运行代码

import pandas as pd
from datetime import datetime, timedelta
from collections import defaultdict
import random

# 配置项,可根据实际需求修改
CONFIG = {
    "start_date": "2024-05-01", # 排班周期开始日期
    "end_date": "2024-05-31", # 排班周期结束日期
    "daily_staff_count": 2, # 每日需要排班的总人数
    "holidays": ["2024-05-01", "2024-05-05"], # 自定义节假日列表
    "rule_csv_path": "./staff_rules.csv" # 排班规则CSV路径
}

def generate_date_list(start_date, end_date):
    """生成排班周期内的所有日期列表"""
    start = datetime.strptime(start_date, "%Y-%m-%d")
    end = datetime.strptime(end_date, "%Y-%m-%d")
    date_list = []
    current = start
    while current <= end:
        date_list.append(current.strftime("%Y-%m-%d"))
        current += timedelta(days=1)
    return date_list

def is_weekend_or_holiday(date_str, holidays):
    """判断日期是否为周末或节假日"""
    date = datetime.strptime(date_str, "%Y-%m-%d")
    return date.weekday() >=5 or date_str in holidays

if __name__ == "__main__":
    # 1. 生成排班周期所有日期
    all_dates = generate_date_list(CONFIG["start_date"], CONFIG["end_date"])
    # 2. 加载CSV规则
    rule_df = pd.read_csv(CONFIG["rule_csv_path"])
    # 初始化规则字典
    must_schedule = defaultdict(list) # key:日期 value:必排人员列表
    forbidden_schedule = defaultdict(list) # key:日期 value:禁排人员列表
    all_staff = set()
    for _, row in rule_df.iterrows():
        name = row["姓名"]
        rule_type = row["类型"]
        date = row["日期"]
        all_staff.add(name)
        if rule_type == "must":
            must_schedule[date].append(name)
        elif rule_type == "forbidden":
            forbidden_schedule[date].append(name)
    # 3. 初始化排班表
    schedule_result = defaultdict(list)
    staff_count = defaultdict(int) # 统计人员总排班次数
    staff_holiday_count = defaultdict(int) # 统计人员节假日/周末排班次数
    recent_schedule = defaultdict(list) # 记录人员近2天排班情况,避免连续排班
    # 先填充必排人员
    for date in all_dates:
        must_staff = must_schedule.get(date, [])
        schedule_result[date] = must_staff.copy()
        for staff in must_staff:
            staff_count[staff] += 1
            if is_weekend_or_holiday(date, CONFIG["holidays"]):
                staff_holiday_count[staff] +=1
    # 4. 分配剩余名额
    for date in all_dates:
        remaining = CONFIG["daily_staff_count"] - len(schedule_result[date])
        if remaining <= 0:
            continue
        # 筛选可用候选人
        forbidden = forbidden_schedule.get(date, [])
        candidates = []
        for staff in all_staff:
            # 排除禁排、已经排在当天、近2天有排班的人员
            if staff in forbidden or staff in schedule_result[date] or date in recent_schedule.get(staff, []):
                continue
            candidates.append(staff)
        # 候选人排序规则:先按总排班次数升序,再按节假日排班次数升序,打乱同优先级的顺序保证公平
        candidates.sort(key=lambda x: (staff_count[x], staff_holiday_count[x], random.random()))
        # 选前remaining个人员
        selected = candidates[:remaining]
        schedule_result[date].extend(selected)
        # 更新统计数据
        is_special = is_weekend_or_holiday(date, CONFIG["holidays"])
        date_obj = datetime.strptime(date, "%Y-%m-%d")
        for staff in selected:
            staff_count[staff] +=1
            if is_special:
                staff_holiday_count[staff] +=1
            # 记录近2天排班日期
            recent_schedule[staff].append(date)
            # 只保留近2天的记录
            if len(recent_schedule[staff]) > 2:
                recent_schedule[staff].pop(0)
    # 输出结果
    result_df = pd.DataFrame([
        {"日期": date, "排班人员": ",".join(staff_list)}
        for date, staff_list in schedule_result.items()
    ])
    result_df.to_csv("./schedule_result.csv", index=False, encoding="utf-8-sig")
    # 输出排班统计,方便校验公平性
    print("===== 排班次数统计 =====")
    for staff, cnt in staff_count.items():
        print(f"{staff}: 总排班{cnt}次,节假日/周末排班{staff_holiday_count.get(staff, 0)}次")

CSV规则文件格式说明

规则文件staff_rules.csv需按如下格式编写:

姓名,类型,日期
John,must,2024-05-04
Anna,forbidden,2024-05-11
Bob,must,2024-05-10
Lily,forbidden,2024-05-20

效果验证

运行代码后会生成schedule_result.csv排班结果文件,同时控制台会输出各人员的排班统计:

  • 所有人员总排班次数差值最大为1,无过度集中的情况
  • 周末、节假日排班次数在人员之间按比例均衡分配,差值不超过1
  • 所有必排规则、禁排规则都会严格遵守,不会出现冲突

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 04:36:04