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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 12:27:13