如何使用np.where为Pandas DataFrame创建多条件分段工时占比列
解决方案
问题分析
你当前代码的问题是:只要某行匹配到目标分组,就把所有Hours列的总和除以8,而不是只累加对应Group匹配目标分组的Hours值。比如ID=1的Seg_Maintenance,应该只取Hours1的4(因为Group1是Maintenance),而不是把Hours1+Hours2+Hours3全加起来。
方法一:循环遍历分组,精准匹配累加
直接针对每个目标分组,遍历3组Group-Hours对,提取匹配的Hours值求和后除以8:
import pandas as pd import numpy as np df = pd.DataFrame({ 'ID': [1, 2, 3, 4], 'Group1': ['Maintenance', 'Shop', 'Admin', 'Shop'], 'Hours1': [4, 4, 8, 8], 'Group2': ['Admin', 'Customer', '0', '0'], 'Hours2': [4.0, 2.0, 0.0, 0.0], 'Group3': ['0', 'Admin', '0', '0'], 'Hours3': [0.0, 2.0, 0.0, 0.0], }) segment_list = ["Maintenance", "Shop", "Admin", "Customer"] for seg in segment_list: # 初始化累加列 total_hours = pd.Series(0, index=df.index) # 遍历3组Group-Hours对 for n in range(1, 4): # 找到当前Group列等于目标分组的行,取出对应Hours值累加 mask = df[f'Group{n}'] == seg total_hours += df.loc[mask, f'Hours{n}'].reindex(df.index, fill_value=0) # 计算结果并赋值给新列 df[f'Seg_{seg}'] = total_hours / 8 print(df)
运行后得到的结果符合预期:
ID Group1 Hours1 Group2 Hours2 Group3 Hours3 Seg_Maintenance Seg_Shop Seg_Admin Seg_Customer 0 1 Maintenance 4 Admin 4.0 0 0.0 0.5 0.0 0.5 0.0 1 2 Shop 4 Customer 2.0 Admin 2.0 0.0 0.5 0.25 0.25 2 3 Admin 8 0 0.0 0 0.0 0.0 0.0 1.0 0.0 3 4 Shop 8 0 0.0 0 0.0 0.0 1.0 0.0 0.0
方法二:用melt重塑数据(更高效的Pandas风格写法)
如果分组数量较多,推荐用数据重塑的方式,避免嵌套循环:
# 重塑数据:把Group和Hours列转成行 melted = df.melt( id_vars=['ID'], value_vars=[f'Group{n}' for n in range(1,4)] + [f'Hours{n}' for n in range(1,4)], var_name='Type', value_name='Value' ) # 拆分出分组编号(1/2/3) melted['Num'] = melted['Type'].str.extract('(\d+)') # 拆分出类型(Group/Hours) melted['Type'] = melted['Type'].str.replace('\d+', '') # 转成宽表:每个ID+Num对应Group和Hours pivoted = melted.pivot(index=['ID', 'Num'], columns='Type', values='Value').reset_index() # 过滤掉Group为0的行 pivoted = pivoted[pivoted['Group'] != '0'] # 把Hours转成数值类型 pivoted['Hours'] = pd.to_numeric(pivoted['Hours']) # 计算每个ID+Group的总Hours,再除以8 result = pivoted.groupby(['ID', 'Group'])['Hours'].sum().unstack(fill_value=0) / 8 # 重命名列并合并回原DataFrame result.columns = [f'Seg_{col}' for col in result.columns] df = df.merge(result, on='ID', how='left') print(df)
这个方法逻辑更清晰,也更适合扩展到更多Group-Hours对的场景。
内容的提问来源于stack exchange,提问作者user12715151
相关产品推荐
相关产品推荐

