基于分组条件,用Pandas实现带系数的双序列分配与累积求和优化
基于Pandas原生函数实现分组容量分配计算
需求说明
现有包含Annual Volume、Annual Capacity和Lane列的数据集,需按Lane列的L1/L2分组,计算Allocation和Cum Allocation列,规则如下:
- 当
Cum Allocation<Annual Volume时,Allocation= min(Annual Volume* 分配系数,Annual Capacity) - 当需要让
Cum Allocation刚好等于Annual Volume时,Allocation=Annual Volume-Cum Allocation.shift(1) - 当
Cum Allocation>Annual Volume时,Allocation= 0 Cum Allocation计算规则:初始值等于对应行的Allocation,后续行 = 上一行Cum Allocation+ 当前行Allocation
当前使用自定义函数处理大型数据集时效率低下,且需频繁调整分配系数(如示例中的0.5),需用Pandas原生函数实现该逻辑。示例中L1组的Annual Volume为4917.0,L2组为5604.0。
实现方案
核心思路
利用Pandas的groupby结合cumsum、clip等原生函数实现向量化计算,避免循环或自定义函数的低效问题,同时方便快速调整分配系数。
代码实现
import pandas as pd # 示例数据 data = pd.DataFrame({ 'Lane': ['L1', 'L1', 'L1', 'L2', 'L2', 'L2'], 'Annual Volume': [4917.0, 4917.0, 4917.0, 5604.0, 5604.0, 5604.0], 'Annual Capacity': [3000.0, 3000.0, 3000.0, 3500.0, 3500.0, 3500.0] }) # 定义分配系数,可直接修改调整 allocation_coeff = 0.5 def calculate_allocation(group): # 计算初始候选分配值:取"Annual Volume*系数"和"Annual Capacity"的较小值 candidate_alloc = group['Annual Volume'].mul(allocation_coeff).clip(upper=group['Annual Capacity']) # 计算累计候选分配值 cum_candidate = candidate_alloc.cumsum() # 计算当前行可分配的剩余量(未达标的情况下) remaining = group['Annual Volume'] - cum_candidate.shift(fill_value=0) # 确定最终Allocation:累计未达标时取候选值和剩余量的较小值,达标后设为0 group['Allocation'] = candidate_alloc.where(cum_candidate.shift(fill_value=0) < group['Annual Volume'], 0) group['Allocation'] = group['Allocation'].clip(upper=remaining) # 计算累计分配值 group['Cum Allocation'] = group['Allocation'].cumsum() return group # 按Lane分组执行计算 result = data.groupby('Lane').apply(calculate_allocation).reset_index(drop=True) print(result)
代码说明
- 候选分配值计算:通过
mul和clip快速得到每一行的初始候选分配量,对应规则1的基础逻辑 - 剩余量计算:用
Annual Volume减去上一行的累计候选值,精准控制最后一行的分配量,满足规则2的边界要求 - 最终Allocation确定:通过
where过滤掉累计已达标的行(分配设为0),再用clip确保分配量不会超过剩余需求,覆盖规则3的场景 - 累计分配计算:直接调用
cumsum实现Cum Allocation的累加,完全基于Pandas原生向量化操作,处理大型数据集时效率远高于自定义循环
效果验证
以示例数据为例,L1组Annual Volume为4917.0,系数0.5时,初始候选分配为4917*0.5=2458.5(小于容量3000),前两行各分配2458.5,累计达4917.0,第三行分配0;L2组同理,前两行各分配2802(5604*0.5),累计达5604.0,第三行分配0,完全符合规则要求。
内容的提问来源于stack exchange,提问作者Rajib Lochan Sarkar
相关产品推荐
相关产品推荐

