Python中非唯一Product ID按先进先出规则查表赋值方法
实现方案
核心思路
你提出的库存批次耗尽标记思路方向正确,这类FIFO库存分配本质是按产品分组后的双指针逐量扣减逻辑,无法直接用merge()/map()实现——这两个方法要求匹配键唯一/可直接映射,不支持按数量拆分批次的场景。
整体流程:
- 预处理阶段先给两张表加临时状态字段,排序保证FIFO顺序,提前初始化需要的3个库龄列
- 按
Product ID拆分处理单元(避免跨产品错配),每个产品组内用两个指针分别追踪当前待分配的最早库存批次、当前待满足的最前需求 - 循环对比当前库存剩余量、当前需求剩余量,扣减对应数量,把匹配到的批次库龄填入需求行的对应列,某一侧数量清零就移动对应指针,直到该产品组内所有库存、需求匹配完成(你已确认总库存=总需求,无需处理余量异常)
可直接运行的参考代码
import pandas as pd # 1. 数据读取与预处理 # 替换成你实际的xlsx读取代码 # df_inventory = pd.read_excel("库存表路径.xlsx") # df_demand = pd.read_excel("需求表路径.xlsx") # 库存表排序:同产品按入库日期升序,保证先入库的排前面 df_inventory = df_inventory.sort_values( by=["Product ID", "Inventory Receipt Date"], ascending=[True, True] ).reset_index(drop=True) # 新增临时字段:记录每个库存批次的剩余可分配数量 df_inventory["remaining_qty"] = df_inventory["Quantity"] # 需求表初始化:新增剩余待分配量字段、3个空的库龄结果列 df_demand = df_demand.reset_index(drop=True) df_demand["remaining_demand"] = df_demand["Required Quantity"] for col_idx in range(1, 4): df_demand[f"Age days {col_idx}"] = None # 2. 按产品分组逐组做FIFO分配 for product_id in df_demand["Product ID"].unique(): # 筛选当前产品的所有库存批次、所有需求行的索引 inv_indexes = df_inventory[df_inventory["Product ID"] == product_id].index.tolist() demand_indexes = df_demand[df_demand["Product ID"] == product_id].index.tolist() inv_pointer = 0 # 库存指针:从第一个(最早入库)批次开始 demand_pointer = 0 # 需求指针:从第一个(优先级最高)需求开始 age_col_pos = 1 # 记录当前需求要填第几个库龄列 # 两个指针都没遍历完时持续分配 while inv_pointer < len(inv_indexes) and demand_pointer < len(demand_indexes): cur_inv_idx = inv_indexes[inv_pointer] cur_demand_idx = demand_indexes[demand_pointer] cur_col = f"Age days {age_col_pos}" inv_left = df_inventory.loc[cur_inv_idx, "remaining_qty"] demand_left = df_demand.loc[cur_demand_idx, "remaining_demand"] if inv_left >= demand_left: # 当前库存足够覆盖剩余需求:填库龄、扣库存、当前需求完成,切到下一个需求 df_demand.loc[cur_demand_idx, cur_col] = df_inventory.loc[cur_inv_idx, "Age Days"] df_inventory.loc[cur_inv_idx, "remaining_qty"] -= demand_left df_demand.loc[cur_demand_idx, "remaining_demand"] = 0 demand_pointer += 1 age_col_pos = 1 # 新需求从第一个库龄列开始填 else: # 当前库存不够覆盖剩余需求:填库龄、扣需求、当前库存耗尽,切到下一个批次 df_demand.loc[cur_demand_idx, cur_col] = df_inventory.loc[cur_inv_idx, "Age Days"] df_demand.loc[cur_demand_idx, "remaining_demand"] -= inv_left df_inventory.loc[cur_inv_idx, "remaining_qty"] = 0 inv_pointer += 1 age_col_pos += 1 # 同一个需求填下一个库龄位 # 清理临时字段,得到最终结果 df_demand = df_demand.drop(columns=["remaining_demand"])
注意事项
- 排序环节不能省略:如果库存表没按
Inventory Receipt Date升序排列,会出现晚入库批次先分配的问题,违反FIFO规则 - 不要跨产品处理:必须按
Product ID拆分后再做匹配,否则会出现不同产品的库存、需求错配 - 如果后续需求调整为单条需求可能跨超过3个批次,只要修改初始化库龄列的循环范围即可,核心逻辑不用改动
相关学习检索关键词
后续要查找同类场景的实现方案,可以用这些关键词检索:
- pandas FIFO 库存分配
- pandas 双指针 逐行扣减
- pandas 批次拆分 先进先出匹配
- pandas 一对多 数量分摊
内容的提问来源于stack exchange,提问作者Oscar_Wong
相关产品推荐
相关产品推荐

