Pandas大数据量下库存预测DataFrame操作性能优化求助
优化Pandas库存预测代码的性能方案
嘿,作为Pandas新手能写出实现功能的代码已经很棒了!嵌套循环在数据量小的时候没问题,但面对10万级别的数据时,逐行操作的时间复杂度会让速度骤降。咱们用向量化操作和高效数据合并来重构逻辑,性能会提升好几个数量级,代码也更简洁易维护。
原代码的核心问题
你的嵌套循环+每次循环内的loc查询,本质是Python层面的逐行处理,而Pandas的优势在于底层C实现的向量化操作,能批量处理数据,避免循环的开销。另外原代码还有个小bug:if len(df_purchase_orders.index) > 0应该判断的是匹配到的订单数量len(order_qty) > 0,否则不管有没有对应订单都会执行加量操作。
优化后的实现步骤
下面是一步步的优化方案,完全对齐你原来的业务逻辑:
1. 预处理数据和日期
先把所有日期转成datetime类型,方便后续计算,同时聚合采购订单(避免同一物料同一天多个订单的情况):
import pandas as pd # 假设today和endofmonth已经定义,先转成datetime today = pd.to_datetime(today) endofmonth = pd.to_datetime(endofmonth) df_dates = pd.date_range(start=today, end=endofmonth, freq='D') # 预处理采购订单:转日期+聚合同物料同日的到货量 df_purchase_orders['DATE'] = pd.to_datetime(df_purchase_orders['DATE']) df_po_agg = df_purchase_orders.groupby(['ITEM', 'DATE'])['RECEIVING_AMOUNT'].sum().reset_index()
2. 生成物料-日期的笛卡尔积基础表
我们需要每个物料在每一天都有预测数据,用MultiIndex.from_product快速生成所有组合:
# 获取所有唯一物料 items = df_stock['ITEM'].unique() # 生成物料和日期的笛卡尔积 index = pd.MultiIndex.from_product([items, df_dates], names=['ITEM', 'DATE']) df_base = pd.DataFrame(index=index).reset_index()
3. 合并初始库存和日消耗数据
把库存表的初始库存、日消耗关联到基础表:
df_base = df_base.merge(df_stock[['ITEM', 'STOCK', 'DAILY_DEDUCTION']], on='ITEM', how='left')
4. 计算累计消耗量
根据日期与today的天数差,计算到当日为止的累计消耗(完全对齐原代码的每日扣减逻辑):
# 计算每个日期距离today的天数差(today为0,次日为1...) df_base['days_since_today'] = (df_base['DATE'] - today).dt.days # 累计消耗:每日扣减 * 已过天数+1(因为today当天就会扣一次) df_base['cumulative_deduction'] = df_base['DAILY_DEDUCTION'] * (df_base['days_since_today'] + 1)
5. 合并到货量并计算累计到货
把采购订单的到货量匹配到对应日期和物料,然后计算到当日为止的累计到货:
# 合并到货量,无到货的填0 df_base = df_base.merge(df_po_agg, on=['ITEM', 'DATE'], how='left') df_base['RECEIVING_AMOUNT'] = df_base['RECEIVING_AMOUNT'].fillna(0) # 按物料分组,计算累计到货量 df_base['cumulative_receiving'] = df_base.groupby('ITEM')['RECEIVING_AMOUNT'].cumsum()
6. 计算最终预测库存
用向量化计算直接生成所有日期的预测库存:
# 计算预测库存:初始库存 - 累计消耗 + 累计到货 df_base['predicted_stock'] = df_base['STOCK'] - df_base['cumulative_deduction'] + df_base['cumulative_receiving'] # 转成int类型,和原代码输出一致 df_base['predicted_stock'] = df_base['predicted_stock'].astype(int)
7. 整理成原代码的输出格式
如果需要和原代码一样的列表格式,直接转换即可:
# 格式化日期并提取所需列 result = df_base.rename(columns={'DATE': 'date_format'}) result['date_format'] = result['date_format'].dt.strftime('%Y-%m-%d') # 转成和原temptable一样的列表结构 temptable = result[['date_format', 'ITEM', 'predicted_stock']].values.tolist()
为什么这个方案更快?
- 所有操作都是向量化批量处理,底层由Pandas的C扩展实现,比Python循环快几十到几百倍;
- 用
merge和groupby替代了循环内的loc查询,避免了重复的数据检索开销; - 代码逻辑更清晰,也更容易维护和调试。
内容的提问来源于stack exchange,提问作者akelopes
相关产品推荐
相关产品推荐

