Python pandas实现df1与df2匹配扣减库存WIP生成新数据框
问题解答
技术选型建议
用pandas实现更合适。你的需求属于结构化表格的分组匹配、带业务逻辑的逐行迭代处理场景,pandas对表格数据的分组、合并、自定义迭代处理的支持更贴合需求,numpy更适合纯数值矩阵的批量运算,处理这类带业务规则的拆分行、分类打标的场景会更繁琐。
核心实现思路
核心逻辑是按物料分组,按照交付日期从早到晚的顺序优先抵扣库存,再抵扣在制品,最后剩余部分标记为计划量,具体步骤如下:
- 预处理:将待交付表df1按
Item、Date升序排序,保证先到期的交付需求优先被抵扣;将库存表df2转换为以物料为key的字典,存储每个物料的可用Stock、WIP余额,方便后续扣减更新。 - 按物料分组迭代:对每个物料单独走抵扣流程:
- 初始化当前物料可用的Stock、WIP数值
- 遍历该物料的每一条待交付记录,拿到当前需要抵扣的数量
curr_qty - 优先扣Stock:如果Stock还有余额,取
min(curr_qty, Stock)作为本次Stock抵扣量,生成对应记录;更新剩余Stock和剩余待抵扣量,如果待抵扣量为0则跳过后续步骤,处理下一条记录 - 再扣WIP:如果WIP还有余额,取
min(剩余待抵扣量, WIP)作为本次WIP抵扣量,生成对应记录;更新剩余WIP和剩余待抵扣量,如果待抵扣量为0则处理下一条记录 - 剩余待抵扣量全部标记为Planned,生成对应记录
- 所有物料的抵扣记录合并后即为最终结果
可运行代码示例
import pandas as pd # 你的样例数据 data1 = [["Apple", "2021/1/1", 3], ["Apple", "2021/3/1", 2], ["Apple", "2021/7/1", 7], ["Banana", "2023/1/1", 10], ["Banana", "2023/12/1", 5], ["Banana", "2024/1/1", 3], ["Kiwi", "2021/2/1", 2], ["Kiwi", "2021/2/2", 1], ["Kiwi", "2021/2/3", 9], ["Kiwi", "2021/2/3", 20]] df1 = pd.DataFrame(data1, columns=['Item', 'Date', "Quantity Remaining"]) data2 = [["Apple", 2, 7], ["Banana", 10, 8], ["Kiwi", 10, 5]] df2 = pd.DataFrame(data2, columns=["Item", "Stock", "WIP"]) # 预处理1:df1按日期排序,保证先到期先抵扣 df1 = df1.sort_values(by=['Item', 'Date']).reset_index(drop=True) # 预处理2:df2转字典 stock_dict = df2.set_index('Item').to_dict('index') result = [] # 按物料分组处理 for item, group in df1.groupby('Item'): # 拿到当前物料的库存和WIP curr_stock = stock_dict[item]['Stock'] curr_wip = stock_dict[item]['WIP'] # 遍历该物料的每笔待交付 for _, row in group.iterrows(): date = row['Date'] remain = row['Quantity Remaining'] # 先扣库存 if curr_stock > 0 and remain > 0: deduct = min(remain, curr_stock) result.append([item, date, deduct, 'Stock']) curr_stock -= deduct remain -= deduct # 再扣WIP if curr_wip > 0 and remain > 0: deduct = min(remain, curr_wip) result.append([item, date, deduct, 'WIP']) curr_wip -= deduct remain -= deduct # 剩余标Planned if remain > 0: result.append([item, date, remain, 'Planned']) # 转成DataFrame outcome = pd.DataFrame(result, columns=["Item", "Delivery Date", "Quantity", "Note"]) print(outcome)
注:你提供的预期输出样例里
Banana的2023/9/1属于笔误,按照输入数据所有Banana的交付日期只有2023/1/1、2023/12/1、2024/1/1,代码运行结果会保留原始交付日期,符合需求要求。
内容的提问来源于stack exchange,提问作者Illusoma
相关产品推荐
相关产品推荐

