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

Pandas DataFrame按姓名累计工时拆分:达标38小时插入行调整

问题描述

针对Pandas DataFrame中的每个name字段,需按以下规则处理hours列:

  • 对每个name的hours按降序排序后进行累计求和
  • 当累计工时达到38小时时,执行以下操作:
    1. 在触发达标的原行(旧行)下方插入新行
    2. 将旧行的hours值调整为累计至38小时所需的剩余工时(例如Ankit的旧行原工时15,调整为4.5;Abhishek的旧行原工时13.5,调整为8)
    3. 旧行原工时与调整后工时的差值(余额)填入新行的hours字段
  • 其他字段(number、loc)在新行中保持与旧行一致

初始DataFrame代码示例:

import pandas as pd

df = pd.DataFrame()
df['number'] = (651,651,651,4267,4267,4267,4267,4267,4267,4267,8806,8806,8806,6841,6841,6841,6841)
df['name']=('Alex','Alex','Alex','Ankit','Ankit','Ankit','Ankit','Ankit','Ankit','Ankit','Abhishek','Abhishek','Abhishek','Blake','Blake','Blake','Blake')
df['hours']=(8.25,7.5,7.5,7.5,14,12,15,11,6.5,14,15,15,13.5,8,8,8,8)
df['loc']=('Nar','SCC','RSL','UNIT-C','UNIT-C','UNIT-C','UNIT-C','UNIT-C','UNIT-C','UNIT-C','UNI','UNI','UNI','UNKING','UNKING','UNKING','UNKING')
解决方案

以下是实现需求的完整代码:

import pandas as pd

# 初始化DataFrame
df = pd.DataFrame()
df['number'] = (651,651,651,4267,4267,4267,4267,4267,4267,4267,8806,8806,8806,6841,6841,6841,6841)
df['name']=('Alex','Alex','Alex','Ankit','Ankit','Ankit','Ankit','Ankit','Ankit','Ankit','Abhishek','Abhishek','Abhishek','Blake','Blake','Blake','Blake')
df['hours']=(8.25,7.5,7.5,7.5,14,12,15,11,6.5,14,15,15,13.5,8,8,8,8)
df['loc']=('Nar','SCC','RSL','UNIT-C','UNIT-C','UNIT-C','UNIT-C','UNIT-C','UNIT-C','UNIT-C','UNI','UNI','UNI','UNKING','UNKING','UNKING','UNKING')

# 定义处理单个员工分组的函数
def process_employee(group):
    # 按工时降序排序,优先处理工时多的记录
    sorted_group = group.sort_values('hours', ascending=False).reset_index(drop=True)
    # 计算累计工时
    sorted_group['cum_hours'] = sorted_group['hours'].cumsum()
    
    # 找到第一个让累计工时突破38的行索引
    exceed_idx = sorted_group[sorted_group['cum_hours'] >= 38].index.min()
    
    # 如果该员工总工时不足38,直接返回排序后的结果
    if pd.isna(exceed_idx):
        return sorted_group.drop('cum_hours', axis=1)
    
    # 计算凑够38小时需要用该行的工时数
    prev_cum = sorted_group.loc[exceed_idx-1, 'cum_hours'] if exceed_idx > 0 else 0
    remaining_hours = 38 - prev_cum
    original_hours = sorted_group.loc[exceed_idx, 'hours']
    # 拆分出的剩余工时
    balance_hours = original_hours - remaining_hours
    
    # 调整原行的工时为凑够38所需的数值
    sorted_group.loc[exceed_idx, 'hours'] = remaining_hours
    
    # 创建新行,复制原行的其他字段,工时设为拆分后的余额
    new_row = sorted_group.loc[exceed_idx].copy()
    new_row['hours'] = balance_hours
    
    # 拼接原行前的记录、调整后的原行、新行、原行后的记录
    processed_group = pd.concat([
        sorted_group.iloc[:exceed_idx+1],
        pd.DataFrame([new_row]),
        sorted_group.iloc[exceed_idx+1:]
    ], ignore_index=True)
    
    return processed_group.drop('cum_hours', axis=1)

# 按员工分组处理,合并结果
final_df = df.groupby('name', group_keys=False).apply(process_employee).reset_index(drop=True)

print(final_df)

代码逻辑拆解

  1. 分组排序与累计计算:把每个员工的工时从多到少排序,再逐行累加,快速定位到哪一行会让累计工时突破38小时。
  2. 拆分逻辑处理:如果员工总工时不够38就直接跳过;够的话,算出该行需要贡献多少工时凑够38,剩下的工时单独拆成一行,其他信息和原行保持一致。
  3. 结果合并:把每个员工处理后的记录拼接起来,得到最终的DataFrame。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 01:35:16