新闻详情

新闻详情

首页 / 资讯中心 / 详情

需求预测与库存优化:Python构建补货决策计算链

发布时间:2026/9/13 17:18:38来源:尧图网络
需求预测与库存优化:Python构建补货决策计算链
简介基于Python的库存优化与需求预测算法设计源码是一套面向企业供应链、库存管理从业者及算法开发者的完整实现方案重点解决库存积压与缺货风险并存的问题。通过历史数据分析与多种预测模型对比帮助使用者制定更合理的补货与库存配置策略适用于制造业、零售业、电子商务等多类场景。整个压缩包共21个文件大小约10.45MB其中6个Python源码文件负责数据清洗、特征转换、预测算法与数据库交互逻辑Excel工作表用于存储价格、库存等原始数据及预测结果CSV文件支持中间数据导入导出PNG图片直观展示库存变化趋势同时包含Markdown文档与XML配置便于了解项目结构和运行参数。目前已有411人学习参考适合具备一定Python基础、希望掌握库存预测建模或基于现有工程二次开发的学习者与研究团队。资源提供可运行的核心代码与配套数据涵盖时间序列分析、指数平滑、随机森林等算法实现既能快速理解需求预测的落地流程也可根据业务场景调整参数、接入外部系统显著缩短从算法设计到工程应用的时间。1. 需求预测与库存优化在 Python 里不是两个模型而是一条计算链仓库里几百个 SKU每周补货单怎么排运营基本靠 Excel 加直觉。用 Python 做需求预测与库存优化不是一上来就上强化学习而是先把安全库存、再订货点算对。最常见的方法是取近三个月销量算平均值再乘一个“安全系数”需求波动小、采购周期固定时勉强能跑一旦碰上促销、长假或新品放量均值和系数全都会失真。标题里的“库存优化与需求预测算法设计源码”就是把这两件事放进同一条计算链预测模型给出未来需求均值和波动幅度库存模型把它们换算成安全库存、再订货点和单次订货量。适合做电商仓储、进销存系统、供应链中台开发。实现不需要完整运筹学背景会用 pandas 和 statsmodels再理解几个参数就能从历史订单生成补货建议。2. 需求预测算法设计先用 ETS 拟合需求再用残差分位数给安全库存2.1 为什么先预测而不是直接平均历史销量平均数有一个隐含假设未来是历史均值附近的一条水平线。只要序列里有上升趋势或者每周有固定周期平均数就会系统性偏移。比如近 30 天日均销量 120但最后一周已经到 150按 120 算再订货点到提前期结束时大概率已经缺货。所以每个 SKU 应该先跑一遍时序预测输出未来 horizon 天的逐日期望再把它折算成日均值交给库存逻辑。还有一个容易漏掉的点库存优化不只是要一个预测均值还需要一个波动幅度。安全库存公式里的标准差应该来自预测残差而不是历史销量标准差。这也是为什么把“需求预测”放在“库存优化”前面——预测模型的输出质量直接决定安全库存是在补缺口还是在补噪声。2.2 用 statsmodels 实现 ETS 的完整函数import numpy as np import pandas as pd from statsmodels.tsa.holtwinters import ExponentialSmoothing def fit_ets(history: pd.Series, horizon: int 30): history history.sort_index().astype(float) if not isinstance(history.index, pd.DatetimeIndex): history.index pd.to_datetime(history.index) freq pd.infer_freq(history.index) if freq is None: history history.resample(D).sum().fillna(0) freq D else: history history.asfreq(freq).fillna(methodffill) season_periods 7 if freq in (D, W) and history.nunique() 60 else None model ExponentialSmoothing( history, trendadd, seasonal(add if season_periods else None), seasonal_periodsseason_periods, damped_trendTrue, ) fitted model.fit(optimizedTrue, use_boxcoxFalse) forecast fitted.forecast(horizon) resid (history - fitted.fittedvalues).dropna() resid_std float(resid.std(ddof1)) return forecast, resid_std这段代码先处理索引infer_freq识别日/周粒度识别不出来就按天重采样缺失日期补 0。很多订单表并不是连续日期直接传进 ETS 会因为索引频率不规律报错所以这步不能省。season_periods的规则是一个保守经验只有按天或按周的数据、且非重复值超过 60 个时才开 7 天周期。数据不足两个完整周期时开 seasonal拟合出来的季节项纯属噪声。damped_trendTrue会让趋势斜率随时间衰减30 天以上的 horizon 不会越推越离谱。resid是训练集上的残差序列这里取resid.std(ddof1)作为日需求波动如果想给缺货更多余量改成np.quantile(resid, 0.9)。这样预测模型的输出就同时覆盖了均值与波动。参数作用常见取值需要小心的点trend建模上升/下降趋势add / Nonemul 在销量接近 0 时会放大噪声seasonal建模周/月周期add / None数据不足两个周期就别开damped_trend阻尼趋势True / False短 horizon 时差距不大use_boxcoxBox-Cox 变换False零值序列需要先做偏移如果 statsmodels 的优化器对某些短序列收敛失败我会包一层 try-except退回简单指数平滑。异常处理要和正常路径分开避免整个批任务因为一个 SKU 断掉。2.3 滚动预测验证先看 MASE再看 Pinball Scoredef rolling_forecast_error(y: pd.Series, horizon7, step7): y y.sort_index() scores [] for split in range(len(y) - horizon, horizon - 1, -step): train y.iloc[:split] test y.iloc[split:split horizon] fc, _ fit_ets(train, horizon) scores.append(np.mean(np.abs(test.values - fc.values))) naive np.mean(np.abs(np.diff(y.values[-horizon-1:]))) return np.mean(scores) / naive if naive 0 else np.mean(scores)rolling_forecast_error从序列末尾往前多次采样每次只用 split 之前的数据训练避免未来信息泄漏。返回值是 MASE小于 1 说明比 Naive 预测好。注意如果序列全是 0naive 分母可能为 0要退回 MAE。MASE 衡量中心预测质量库存更关心高需求量一侧所以还要补一个 pinball lossmean(max(q * (y - yhat), (q - 1) * (y - yhat)))q 取 0.8 或 0.85。这个损失越小说明对高服务水平更友好。实际项目里我会按 SKU 排序看误差最大的前 20 个是趋势突变、间歇消耗还是促销脉冲再决定要不要单独换模型。3. 库存优化模型实现安全库存、再订货点与 EOQ 联动预测输出3.1 安全库存的输入是预测残差标准差不是历史销量标准差为什么不用历史销量标准差因为历史销量波动里既包含真正随机波动也包含趋势和季节变化。ETS 已经解释了趋势和季节剩下的残差才需要安全库存去兜底。反过来如果序列趋势很强历史标准差会偏大按它算安全库存等于多备了一截不存在的需求波动资金效率很差。提前期需求的标准差等于日需求标准差乘以提前期的平方根dlt_std daily_std * sqrt(lead_time_days)。这个公式假设每天误差独立实际补货场景里残差常有自相关所以我会在配置里留一个safety_factor乘数默认 1.0回测缺货率偏高时调到 1.1。另一个对齐问题是 horizon。如果下单后 15 天到货每隔 7 天审查一次那么一次补货要覆盖 15 7 22 天的需求lead_time_days要写 22。预测函数里的horizon也必须大于等于这个值。如果物流提前期本身不稳定比如有时 12 天有时 18 天只写 15 天会低估缺货更保守的做法是dlt_std sqrt(L * sigma_d^2 d^2 * sigma_L^2)其中sigma_L是提前期标准差。3.2 InventoryPolicy 类实现from math import sqrt class InventoryPolicy: def __init__(self, lead_time_days, holding_cost, order_cost, service_level0.95): self.lead_time_days lead_time_days self.holding_cost holding_cost self.order_cost order_cost self.z { 0.90: 1.28, 0.95: 1.65, 0.98: 2.05, 0.99: 2.33, }.get(service_level, 1.65) def recompute(self, daily_demand_mean, daily_demand_std): lt self.lead_time_days dlt_mean daily_demand_mean * lt dlt_std daily_demand_std * sqrt(lt) safety_stock self.z * dlt_std reorder_point dlt_mean safety_stock order_qty sqrt(2 * daily_demand_mean * self.order_cost / self.holding_cost) return { safety_stock: round(safety_stock, 2), reorder_point: round(reorder_point, 2), order_qty: round(order_qty, 2), }service_level被映射成正态分布分位数 z95% 服务水平对应 1.65。安全库存就是 z 乘以提前期需求标准差reorder_point是期望的提前期需求加上安全库存。order_qty来自经济订货批量 EOQ。这里 D 用的是日均需求daily_demand_mean所以隐含一年 365 天如果holding_cost是年度资金成本需要把 D 换成daily_demand_mean * 365否则order_qty会被低估。这是常见单位错误不少源码直接拿日需求塞进去。提示持有成本最好用“单件每天”建模和daily_demand_mean天然对齐没有财务数据时按库存资金占用的年化比例除以 365 估算。3.3 服务水平、提前期和成本的 3 个必调参数参数含义经验值调大后的影响service_level补货周期内不缺货概率0.90~0.98安全库存非线性上升lead_time_days到货天数 审查周期7~22再订货点近似线性上升holding_cost单件每日持有成本0.01~0.05订货批量变小order_cost单次采购固定成本50~200订货批量变大这三个参数不是互相独立。服务水平从 0.95 提到 0.98z 值从 1.65 变到 2.05安全库存增加约 24%如果客户对缺货率不敏感这个钱花得不值。更合理的做法是先设定服务水平再回测缺货率如果回测缺货率高于目标优先调safety_factor而不是直接改 z 值。没有缺货成本数据时我会用边际分析做判断最优服务水平在缺货成本与订购成本的平衡点上。缺货成本难以量化时取 0.95 作为起点跑两周回测再微调比拍脑袋设 0.99 要好。4. 基于 Python 的源码组织从订单表到补货建议单4.1 源码目录与模块职责inventory-opt/ ├── src/ │ ├── io.py # 数据加载、类型转换、日期聚合 │ ├── forecast.py # ETS 预测与残差统计 │ ├── inventory.py # 安全库存、ROP、EOQ 计算 │ └── report.py # 输出补货建议表 ├── config.py # 全局参数 ├── run_optimizer.py # 主流程 └── requirements.txtforecast.py与inventory.py之间只传递两个数日均需求预测值和残差标准差。不要为了省事把整个 DataFrame 塞进库存类。预测层内部改模型不会影响库存层这是源码设计里最重要的一条边界。4.2 清洗、分组与 Python 类型转换的公共函数import pandas as pd def load_sales(csv_path: str) - pd.DataFrame: df pd.read_csv(csv_path) df[date] pd.to_datetime(df[date]) df[qty] pd.to_numeric(df[qty], errorscoerce).fillna(0) df[sku] df[sku].astype(str).str.strip().str.upper() df df.groupby([sku, date], as_indexFalse)[qty].sum() return df def split_by_sku(df: pd.DataFrame): for sku, group in df.groupby(sku): yield sku, group.set_index(date)[qty].sort_index()pd.to_numeric(errorscoerce)会把1,200、7 件这类字符串转成 NaN再用fillna(0)补成 0。SKU 统一转大写并去空格否则ABC-1和abc-1 会变成两个组。groupby([sku, date])把同一天多行订单聚合不做这一步预测输入会带重复日期。如果销量列里有负数比如退货fillna(0)不会修负数。是否把负数当作退货抵减要看业务口径我会在io.py里单独配置allow_negative默认 False负数直接剔除并计数。4.3 主流程与配置注入from src.io import load_sales, split_by_sku from src.forecast import fit_ets from src.inventory import InventoryPolicy class Config: def __init__(self): self.horizon 30 self.lead_time_days 22 self.holding_cost 0.02 self.order_cost 50.0 self.service_level 0.95 self.min_daily_std 1e-6 def run(csv_path): cfg Config() df load_sales(csv_path) rows [] for sku, history in split_by_sku(df): if history.sum() 0: continue history history.tail(180) forecast, resid_std fit_ets(history, cfg.horizon) daily_mean float(forecast.mean()) / cfg.horizon daily_std max(resid_std, cfg.min_daily_std) policy InventoryPolicy( lead_time_dayscfg.lead_time_days, holding_costcfg.holding_cost, order_costcfg.order_cost, service_levelcfg.service_level, ) suggestion policy.recompute(daily_mean, daily_std) rows.append({ sku: sku, forecast_mean: round(daily_mean, 2), forecast_std: round(daily_std, 4), **suggestion, }) return pd.DataFrame(rows)history.tail(180)限制每个 SKU 最多保留 180 天避免老数据拖慢计算也让 ETS 只关注近期形态。daily_mean forecast.mean() / horizon是因为fit_ets返回未来 30 天逐日预测平均后得到日均需求。如果horizon30而lead_time_days22多出的 8 天是安全余量。min_daily_std是护栏。当 SKU 历史销量全是同一个数残差标准差会变成 0安全库存算出来为 0一旦出现异常需求就会缺货。只设1e-6防除零不够业务化更常见的做法是max(resid_std, daily_mean * 0.1)。配置项默认值为什么设置成这个horizon30覆盖 22 天提前期余量 8 天lead_time_days22到货 15 天 每周审查 7 天holding_cost0.02按资金成本年化估算order_cost50一次采购下单固定成本service_level0.95缺货率容忍 5%正式环境里这些配置应该从环境变量读取比如os.getenv(LEAD_TIME, 22)这样 CI 或调度系统不用改 Python 文件就能压测不同参数。5. 库存优化参数回测验证与三个容易被忽略的落地细节5.1 滚动回测骨架import numpy as np def simulate_stock(history, policy_cfg, horizon7, step7): y history.sort_index().reset_index(dropTrue) out_of_stock_rates [] for split in range(0, len(y) - horizon, step): train y.iloc[:split 1] test y.iloc[split 1: split 1 horizon] forecast, resid_std fit_ets(train, horizon) daily_mean float(forecast.mean()) / horizon suggestion policy_cfg.recompute(daily_mean, resid_std) stock suggestion[reorder_point] lost_days 0 for actual in test: stock - actual if stock 0: lost_days 1 stock suggestion[order_qty] out_of_stock_rates.append(lost_days / len(test)) return np.mean(out_of_stock_rates)这个模拟假设缺货当天立刻补货到order_qty是没有提前期的理想情况所以它是服务水平的上界。当回测缺货率高于目标服务水平时多半是resid_std太小调safety_factor比反复改预测模型更直接。5.2 三个容易忽略的落地细节第一冷启动。新 SKU 只有不到一个周期ETS 开 seasonal 必过拟合直接走trendadd就好如果连 7 天数据都没有就用同类 SKU 的日均销量中位数做daily_mean残差标准差取同类 75 分位数别让模型对 3 天历史做任何外推。第二间歇需求。大量天销量为 0 的序列ETS 会拟合出一堆接近 0 的小数残差标准差意义不大。我会把日序列按周聚合预测周需求再除以 7 还原成日需求也可以用 Croston 的思路把需求间隔时间和需求量拆开分别做指数平滑。注意回测指标不要被大量 0 值掩盖MASE 在这种序列上会失真。第三预测指标和库存指标要分开看。MAPE 误差小不代表安全库存偏得少。我会同时输出 MASE、pinball loss 0.8、模拟缺货率三个数。如果缺货率明显高于预期而 MASE 很好问题一定出在残差尾部没被捕捉把resid_std替换成 90 分位残差再跑一次。把这些指标输出成 CSV 扔进 CI每次调参后自动对比缺货率和服务水平比人工盯图表要快得多。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

notebooklm-py 架构决策实录:Capability Protocol 模式从「胖联合体」到「可组合能力」的演进 2026/9/13 17:57:41

notebooklm-py 架构决策实录:Capability Protocol 模式从「胖联合体」到「可组合能力」的演进

notebooklm-py 架构决策实录:Capability Protocol 模式从「胖联合体」到「可组合能力」的演进 【免费下载链接】notebooklm-py Unofficial Python API and agentic skill for Google Gemini Notebook. Full programmatic access to NotebookLMs features—including…

阅读更多 →
如何用 screenpipe CLI 启动本地录制并验证 localhost:3030 API 可用 2026/9/13 17:57:41

如何用 screenpipe CLI 启动本地录制并验证 localhost:3030 API 可用

如何用 screenpipe CLI 启动本地录制并验证 localhost:3030 API 可用 【免费下载链接】screenpipe YC (S26) | Open Computer History | Record your screen continuously locally and provide context to your agents (Claude, Codex, Openclaw, Hermes, Runner...) 项目地址…

阅读更多 →
三步跑通首次运行:ViMax 配置管理实践指南 2026/9/13 17:57:41

三步跑通首次运行:ViMax 配置管理实践指南

三步跑通首次运行:ViMax 配置管理实践指南 【免费下载链接】ViMax "ViMax: Agentic Video Generation (Director, Screenwriter, Producer, and Video Generator All-in-One)" 项目地址: https://gitcode.com/GitHub_Trending/ai/ViMax ViMax 配置…

阅读更多 →
PDF免费去限制:5 步用 PDFPatcher 去除 PDF 复制与打印限制 2026/9/13 17:57:41

PDF免费去限制:5 步用 PDFPatcher 去除 PDF 复制与打印限制

PDF免费去限制:5 步用 PDFPatcher 去除 PDF 复制与打印限制 【免费下载链接】PDFPatcher PDF补丁丁——PDF工具箱,可以编辑书签、剪裁旋转页面、解除限制、提取或合并文档,探查文档结构,提取图片、转成图片等等 项目地址: https…

阅读更多 →
OI-wiki 十五拼图(N-Puzzle)全解:问题定义、可解性判定与 A* 启发式搜索 2026/9/13 17:57:41

OI-wiki 十五拼图(N-Puzzle)全解:问题定义、可解性判定与 A* 启发式搜索

OI-wiki 十五拼图(N-Puzzle)全解:问题定义、可解性判定与 A* 启发式搜索 【免费下载链接】OI-wiki :star2: Wiki of OI / ICPC for everyone. (某大型游戏线上攻略,内含炫酷算术魔法) 项目地址: https://…

阅读更多 →
FOC电机控制核心:SVPWM扇区判断与X、Y、Z推导实战 2026/9/13 17:54:41

FOC电机控制核心:SVPWM扇区判断与X、Y、Z推导实战

做FOC电机控制的人,最后都得过SVPWM这一关。不管你是用ST的MCSDK、TI的InstaSPIN,还是拿着论文从零写驱动,电流环输出的Vd、Vq经过反Park变换变成Vα、Vβ之后,最终都要靠SVPWM把这些“数学世界的电压”翻译成6个MOS管的开关动作。…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞