简介这是一份面向Python数据分析与机器学习学习者的“ARIMA时间序列销量预测”完整项目资料适合毕业设计、期末大作业或课程设计场景。资源以statsmodels为核心覆盖序列平稳化、AR/MA过程、自动定阶与参数估计、模型检验等完整流程并采用每月分上中下旬三个节点预测当月销量的策略充分利用上旬和中旬的实际销量作为先验知识提升预测准确率。 压缩包共13个文件254KB包含4个Python源码、5张过程图表含销量时序图、差分自相关/偏相关图、预测对比图等、Excel数据文件、依赖清单与说明文档目录结构清晰便于直接运行与二次修改。目前已有2053人学习下载适合需要从零搭建销量预测模型、理解ARIMA实操细节的读者参考。1. 拿到这份ARIMA销量预测数据包先别急着跑模型先回答三个问题搞销量预测的人电脑里多半会躺着一个叫 python基于ARIMA时间序列的销量预测模型全部数据.zip 的压缩包。从同事那儿拷的、从资料站下的、项目沉淀的都有解压后基本是一套 Python 脚本、一张历史销售表和一些说明文件。直接跑通常能出结果但跑出来的预测值和业务上的真实销量对不上才是常态。这个数据包解决的是最基础也最常用的单变量时间序列预测问题只凭历史销量这单独一列数用 ARIMA 模型预测未来若干天的数值。它适合手里有几十到几百个历史样本、不想堆太多业务假设、又需要一个可解释结果的人。我拿到任何一个这样命名的包从不在第一时间打开模型脚本而是先回答三个问题历史数据的时间频率是天的还是周的数据里有没有缺失日期和 0 值这段序列是平稳的还是带趋势带季节性的这三个问题的答案决定了你该选多长的预测窗口、差分阶数 d 怎么取、要不要升级成带季节项的 SARIMA。把这三点理清楚之前所有调参都是自我安慰。接下来就按一条完整的 ARIMA 销量预测流程往下走每一步都有可以直接抄的代码和参数说明。2. 用ARIMA做单变量销量预测的完整流程从历史销售流水到未来7天预测值2.1 为什么单变量销量预测优先考虑ARIMA而不是LSTM先聊一个经常被问到的选型问题。很多人一听到时间序列预测就想到 LSTM实际上对于销量预测这件事LSTM 并不是第一选择。原因很简单门店或者 SKU 级别的销量历史往往只有几十条到两三百条日度数据LSTM 在这种样本量下很难收敛随便调参就是过拟合。ARIMA 只要小几十条数据就能建模statsmodels 的接口稳定跑一遍全流程不过十几秒迭代成本低很多。从可解释性上看ARIMA 输出的是经典的模型摘要每一项系数、显著性、AIC、残差白噪声检验结果全部可以量化地摆出来。业务方问「你凭什么叫这个数」你至少能指着检验结果解释。而且 ARIMA 默认给出置信区间这对库存决策特别重要后面第六章专门讲这一点。注意前提是单变量且没有突变型周期一旦数据里混入促销日、断货日、节假日这种外部冲击ARIMA 就会翻车这个到第四章细说。2.2 第一步把销售流水加载进来先回答“这序列能不能用来预测”我这里默认数据包里是一张销售表两列日期、销量。读取的时候务必用 pandas 处理时间索引不要图省事直接 plai 序列读进来。import pandas as pd import matplotlib.pyplot as plt df pd.read_excel(sales.xlsx, parse_dates[date]) df df.set_index(date)[sales].astype(float) df df.sort_index() print(df.head()) print(样本数:, len(df)) print(时间跨度:, df.index.min(), -, df.index.max()) fig, ax plt.subplots(figsize(12, 4)) ax.plot(df, marker., markersize3) ax.set_title(daily sales) ax.set_ylabel(sales amount) plt.tight_layout() plt.show()这段代码做三件事把日期列解析成时间索引、把销量列转成浮点数、按时间排序。顺手打印样本数和时间跨度。画图这一步别省序列长什么样直接决定后面差分阶数和季节性判断肉眼看一眼比任何检验统计量都直观。接着要处理时间频率。销售数据最经常碰到的坑是时间索引不连续周日照常营业但系统没开单日期就跳过去了。我会用 asfreq 规整到日频率缺的日期先填 NaN再由你决定是填 0 还是插值。df df.asfreq(D) print(缺失值数量(转为NaN后):, df.isna().sum())这一步的关键是先把缺口暴露出来。如果不做statsmodels 内部会用错误的时间间隔去估计自相关后续 ACF、PACF 全都会被带偏。2.3 第二步ADF平稳性检验与差分d值不是拍脑袋定的ARIMA 里的 I 是差分差分的前提是序列不平稳。判断平稳性最常用的工具是 ADF 检验原假设是序列存在单位根也就是不平稳。p 值小于 0.05 就拒绝原假设认为序列平稳。from statsmodels.tsa.stattools import adfuller def check_adf(series): res adfuller(series.dropna()) print(ADF 统计量:, round(res[0], 4)) print(p-value:, round(res[1], 4)) print(临界值:, {k: round(v, 4) for k, v in res[4].items()}) check_adf(df)如果 p 值大于 0.05对序列做一阶差分后再检验。df_diff df.diff().dropna() check_adf(df_diff)一阶差分不够就再差分一次但 d 一般不超过 2。销量数据通常一阶差分后就能过检验因为日销量天然有均值回归倾向。要注意的是ADF 对异常值敏感如果数据里有促销日那种十倍于平时的脉冲检验结果可能误导你多差一阶结果方差爆炸。遇到这种数据先做异常点处理再来检验第四章第一个坑就是它。2.4 第三步用ACF/PACF读图定阶p和q的初步判断差分平稳后画 ACF 看滑动平均的阶数 q画 PACF 看自回归的阶数 p。这步很多人做得随意我建议把它当参考而不是结论真实销量数据很难出现教科书那种干净的截尾拖尾图形。from statsmodels.graphics.tsaplots import plot_acf, plot_pacf plot_acf(df_diff, lags20) plot_pacf(df_diff, lags20)读图经验是PACF 在 k 阶之后突然截尾p 取 kACF 在 k 阶之后截尾q 取 k两边都缓慢衰减则说明序列还可能有趋势或季节性没提干净。实际业务序列经常两边都是拖尾那不要硬读直接进网格搜索。读取的结果只是给搜索范围一个初始约束比如图里判断 p、q 在 0 到 3 之间搜索空间就设 0 到 3。2.5 第四步拟合模型summary和Ljung-Box检验必须一起看以初步判断 order 为 (2, 1, 2) 为例拟合代码非常短from statsmodels.tsa.arima.model import ARIMA model ARIMA(df, order(2, 1, 2)) fit model.fit() print(fit.summary())注意这里直接把原始序列 df 传给 ARIMA差分由参数 d1 在模型内部完成预测结果也自动回到原始量纲。这是 statsmodels 较新版本推荐的做法不要手动先 diff 再建模否则预测值全在差分尺度上还原时极其容易算错。summary 表里有几项必须看一是所有系数的 P|z|有系数不显著就适当降阶二看 AIC 和 BIC用于模型间比较三是看拟合是否收敛有时会提示达到迭代上限需要把 maxiter 调大。然后做残差白噪声检验用 Ljung-Boxfrom statsmodels.stats.diagnostic import acorr_ljungbox resid fit.resid lb acorr_ljungbox(resid, lags[10], return_dfTrue) print(lb)p 值大于 0.05 说明残差没有自相关信息已经提干净。如果这一项不通过后面无论预测值多好看都不可信。2.6 第五步预测、置信区间与量纲还原拟合通过后预测未来 N 步直接调 get_forecaststeps 7 forecast fit.get_forecast(stepssteps) mean forecast.predicted_mean ci forecast.conf_int(alpha0.05) result pd.DataFrame({ forecast: mean, lower: ci.iloc[:, 0], upper: ci.iloc[:, 1] }) print(result)alpha0.05 得到的是 95% 置信区间意思是在模型假设下未来一周每天的真实销量有 95% 概率落在这个区间内。区间宽度过宽比如超过均值的两倍说明模型对长期预测没有把握这时候缩减预测窗口比强行优化模型更有效。如果你确实用了老版本流程手动对 df_diff 建模那还原公式是差分值累加后加上最后一个真实值。写成代码就是 forecast_cumsum forecast_diff.cumsum() df.iloc[-1]。这个操作容易在索引对齐上出问题所以我才反复强调直接用带 d 参数的 ARIMA 建模让库帮你做整合。3. ARIMA参数调优pdq网格搜索、AIC/BIC取舍与季节性判断3.1 用网格搜索替代肉眼读图一个不依赖pmdarima的pdq寻优函数ACF/PACF 读图只能给一个大概范围真正落地我会写一个网格搜索把所有候选 pdq 组合跑一遍按 AIC 排序。有人喜欢用 pmdarima 的 auto_arima内网环境经常装不上而且它自动选出来的阶数偶尔偏大。自己写一个不依赖第三方包的搜索函数更可控要加约束也方便。import itertools import warnings import numpy as np warnings.filterwarnings(ignore) def search_pdq(series, p_rangerange(0, 4), d_rangerange(0, 3), q_rangerange(0, 4)): best_aic np.inf best_order None results [] for order in itertools.product(p_range, d_range, q_range): if order (0, 0, 0): continue try: model ARIMA(series, orderorder).fit() except (ValueError, np.linalg.LinAlgError, TypeError): continue results.append((model.aic, order)) if model.aic best_aic: best_aic model.aic best_order order results.sort(keylambda x: x[0]) print(Top 5 (AIC, order):) for aic, ord_ in results[:5]: print(ord_, AIC:, round(aic, 2)) return best_order best_order search_pdq(df) print(最优阶数:, best_order)参数范围限制在 p、q 各 0 到 3d 0 到 2。这个范围覆盖了绝大多数日度销量序列盲目把 p、q 扩到 6 以上只会选出过度复杂的模型。代码里 try 块是为了接住某些组合导致的矩阵求逆失败这在 d 较大时经常发生直接跳过就行。运行后不要只看排名第一看 Top 5 的 AIC 差距。如果前三名 AIC 只差零点几说明这几个模型统计上几乎没有差别此时选阶数最低的那个理由写在下一节。3.2 AIC/BIC的取舍规则数值接近时选低阶不是选最低AIC 和 BIC 都是越低越好但两者对复杂度惩罚力度不同。AIC 惩罚较轻BIC 惩罚较重所以数据量大时BIC 选出来的模型通常比 AIC 更简洁。业务上销量预测没有严格的统计推断需求核心诉求是稳定所以我的经验是当 AIC 相差小于 5 时直接选阶数小的如果你希望模型参数更保守用 BIC 做二次排序。实现上很简单把上一节代码里 model.aic 换成 model.bic 再跑一遍就行。有一点要特别提醒网格搜索是在整个数据集上拟合它选出的阶数不代表样本外预测效果一定最好。真正决定模型能不能用的是滚动验证第五章写。3.3 旺季与周期数据什么时候必须从ARIMA升级成SARIMA很多销量序列有明显的以周为单位的周期周末高、工作日低。如果 ACF 图在 lag7、14 这种位置出现显著峰值或者分解后能看出周期性说明纯 ARIMA 不够要加季节性项模型升级为 SARIMAstatsmodels 里写作 SARIMAX。from statsmodels.tsa.statespace.sarimax import SARIMAX model_s SARIMAX(df, order(1, 1, 1), seasonal_order(1, 1, 1, 7)) fit_s model_s.fit() print(fit_s.summary())seasonal_order 的四元组是 (P, D, Q, S)S7 表示以 7 天为周期。判断要不要加季节项最直接的做法是跑一次季节分解from statsmodels.tsa.seasonal import seasonal_decompose dec seasonal_decompose(df, modeladditive, period7) dec.plot()分解图里如果 seasonal 分量有明显的起伏强度且不是噪声形状就该上季节性模型。加了季节项后模型参数会变多对数据量的要求也更高如果只有三十天数据却要求你预测一个月那无论用什么模型都不会太可靠这种情况我会直接降低预期跟业务方约定只预测 7 天。3.4 乘法模型还是加法模型从分解参数说到判断依据很多人在网上的热词是「时间序列法乘法模型和加法模型在哪找」其实这个问题的答案就在分解函数里。上面代码的 seasonal_decompose 的 model 参数默认是 additive改成 multiplicative 就是乘法分解。二者的数学形式是加法模型 y 趋势 季节 残差乘法模型 y 趋势 × 季节 × 残差。怎么选看季节波动的幅度是否随销量水平变化。快消品和电商数据通常更适合乘法模型——旺季销量十万波动上下几万很正常淡季销量一万波动也就几千。如果季节波动的绝对值始终稳定比如每周六固定比工作日多卖五千件和总量无关那就是加法。拿不准年代替猜把两种分解都跑一遍看哪一种的残差更平稳、方差更小。另外提一句statsmodels 的 STL 分解默认只支持加法但你可以先对序列取对数再用加法分解效果等价于乘法分解这是处理高波动销量数据的常用招数。4. 销量预测的5个翻车现场现象、原因与排查方法4.1 坑1促销日和大促脉冲模型把异常当成了规律现象预测曲线在大促日期前后出现诡异的尖峰或深谷明明促销只做一天模型却预测出了一个持续数周的波动带。原因促销日的销量是正常水平的几倍甚至十几倍对 ARIMA 这种线性模型来说它是一个会拉升自回归系数估计的强异常点。模型为了拟合这个脉冲把 MA 阶数拉高、参数调得极敏感最终正常日子的预测也被带偏。核心在于 ARIMA 不具备区分「结构性冲击」和「随机噪声」的能力。解决把促销日当成外部冲击处理。最简单的方法是先把促销日的销量替换成相邻非促销日的均值建模后预测再手动叠加大促增量稍复杂一点的做法是用 SARIMAX 加外生变量促销日取值 1 否则为 0让模型把促销效果单独估计出来。我的习惯是先做一次异常点标记用前后七天的中位数把极端值压回合理范围再进入 ADF 检验流程。4.2 坑2预测值小得离谱量纲还原写错了现象模型拟合的 summary 里一切都正常AIC 也低但 predict 出来的未来销量全是零点几或者负数和真实量级差了几个数量级。原因这是典型的「先 diff 再建模」导致的问题。很多人对原始序列做了 df.diff()把差分序列丢给 ARIMA预测结果自然停留在差分尺度上。差分值的数量级往往只有原始值的几十分之一不还原当然不对。更隐蔽的是索引错位差分之后首行变成 NaNdropna 后索引漂移后续 cumsum 加上的「最后一个真实值」对不上日期。解决规范做法是把 d 交给 ARIMA 的参数直接 ARIMA(df, order(p, d, q))预测结果自动回到原始量纲。如果你不得不手工还原公式是预测总量等于原始序列最后一个值加上差分预测的累计和df.iloc[-1] forecast_diff.cumsum()并检查预测索引是否从原始序列的下一日开始。4.3 坑3网格搜索挑出一个参数多到离谱的模型现象auto_arima 选出来 order 是 (5, 2, 4)模型参数一大堆拟合速度慢预测曲线还在小幅高频抖动业务方完全无法接受。原因自动搜索算法按 AIC 最低选模型当数据有噪声时复杂模型能把噪声的细节也拟合进去AIC 表面好看样本外一测就露馅。这在销量数据里尤其常见因为销量序列噪声大、信号弱过拟合的门槛很低。解决限制搜索范围是第一步p、q 最大给到 4d 不超过 2。第二步是在 AIC 接近的模型里惩罚复杂度具体规则就是 3.2 节说的AIC 相差小于 5 取低阶或者直接用 BIC 排序。最后一招是看预测曲线的形态预测值应该平滑地趋近于历史均值或者趋势如果呈现锯齿状抖动基本上就是过拟合的模型降阶重跑。4.4 坑4预测天数一长曲线一路掉头向下现象预测未来 60 天前 10 天还算正常后面 30 天曲线越走越低最后趋近于一个很低的常数。原因ARIMA 本质是均值回归模型它会把长期预测拉向序列的均值。如果历史销量有整体向上的趋势被差分掉了而差分项本身没有足够强的持续性长期预测就会慢慢掉头。另外滚动窗口外的预测没有真实值反馈误差会逐步累积预测曲线最终收敛到一个稳定水平这在统计上是正常的但业务接受度很差。解决不要强求一次预测 60 天。我把预测窗口压缩到 7 到 14 天然后每周滚动重拟合一次模型用最新数据修正参数。库存类业务只需要一个提前期的预测14 天以内足够。如果业务方坚持要月度预测就用 ARIMA 预测基础销量再单独叠加已知的活动计划、新店开业等增量而不是让模型硬学。4.5 坑50值过多模型把稀疏销量当成白噪声现象销售序列里大量 0 值比如新店刚开业、断货、系统漏单。ADF 检验时 p 值一会显著一会不显著残差方差大预测值频繁出现负数。原因真实销量本质上是个离散非负序列0 值过多时残差不满足正态假设方差被 0 值的聚集拉大。更麻烦的是区分不了「真 0」和「缺数」不开门的 0 和口径错了的 0 处理方式完全不同。解决先做数据审计。缺数用 asfreq(D) 转 NaN再按业务规则填充比如用前后七天的同星期均值插值如果某些日期门店本来就不营业把这些日期直接从序列里删掉只保留实际营业日别硬凑连续时间索引。处理完再看序列如果 0 值占比仍然很高说明这个粒度本身不适合 ARIMA 单变量建模我一般会提高到周维度聚合或者换用 counts 模型比如负二项回归那是另一套方案了。5. 让ARIMA预测真正能落地滚动验证、误差指标与基线对照5.1 滚动预测每周更新一次模型比一次性预测全月靠谱模型好不好的评判标准不是训练集上的 AIC而是在未知数据上的预测误差。最可靠的做法是滚动验证每次取前 N 条数据拟合预测接下来的 h 条然后窗口往后滑动重复多轮把所有预测值和真实值放在一起算误差。from statsmodels.tsa.arima.model import ARIMA def rolling_forecast(series, order, window60, horizon7): preds [] actuals [] end len(series) - horizon 1 for start in range(0, end, horizon): train series.iloc[start:start window] test series.iloc[start window:start window horizon] if len(test) horizon: break model ARIMA(train, orderorder).fit() pred model.get_forecast(stepslen(test)).predicted_mean preds.extend(pred.values) actuals.extend(test.values) return actuals, preds actuals, preds rolling_forecast(df, best_order)代码里 window60 表示用最近 60 天训练horizon7 表示预测未来 7 天然后窗口后移 7 天循环进行。这个做法的好处是模拟了真实业务节奏你每周一用截至上周日的数据重训模型产出下一周预测。相比一次性拟合无数个时间段滚动验证的误差才是模型真实的样本外表现。5.2 误差指标MAPE和RMSE在销量场景下的坑误差指标选不对模型评估也会翻车。MAPE 是销量预测里最常用的指标但它有个致命问题实际销量为 0 时MAPE 直接趋近无穷大。前面 4.5 节说过销量序列里 0 值并不少见所以很多人跑出来的 MAPE 大得不敢看。我的做法是给 MAPE 加一个下限过滤只统计真实值大于某个阈值比如 1 件的样本点。RMSE 对大的预测误差特别敏感适合库存场景因为一次预测偏差很大就可能导致断货或积压。MAE 最直观单位就是件数/金额。日报我一般同时给 MAE 和带阈值的 MAPEMAE 让业务方理解平均差多少件MAPE 用来做相对比较。不建议只报一个指标单看 MAE 无法体现相对水平单看 MAPE 又会被除零问题污染。5.3 基线对照先跑一个移动平均再谈ARIMA的提升很多项目直接拿着 ARIMA 的预测结果去见业务被挑战「这比我拍脑袋准在哪」时拿不出对照。正确做法是先建立基线最简单的基线是历史均值其次是移动平均再往上是指数平滑。ARIMA 的价值必须通过击败这些基线才能证明。from sklearn.metrics import mean_absolute_error import numpy as np # 基线: 用最近7天移动平均作为未来7天的预测 baseline_preds [] for start in range(0, len(actuals), 7): end start 7 base np.mean(actuals[max(0, start - 7):start]) baseline_preds.extend([base] * min(7, len(actuals) - start)) baseline_mae mean_absolute_error(actuals[:len(baseline_preds)], baseline_preds) arima_mae mean_absolute_error(actuals, preds) print(移动平均基线 MAE:, round(baseline_mae, 2)) print(ARIMA MAE:, round(arima_mae, 2))如果 ARIMA 跑下来只比移动平均低一点点而且误差波动更大我直接建议业务用移动平均省下来的复杂度就是省下来的维护成本。ARIMA 的真正优势是在序列有明显自相关结构时体现的没有这种结构时它只是把简单事情搞复杂的典型代表。5.4 边界意识什么时候放弃ARIMA换LSTM或ProphetARIMA 不是万能的它处理不了的问题有三类。第一是外部因素主导的场景比如店铺促销活动决定了 30% 以上的销量变化这时候该加外生变量用 SARIMAX 或者干脆上梯度提升树做特征回归。第二是样本量充沛且模式复杂的场景上千个 SKU 的日度数据ARIMA 逐个调参不现实这时候用 LSTM 做多步预测或者用 LightGBM 加时序特征反而更务实。第三是节假日效应明显的场景Prophet 这种把节假日显式建模的工具要比 ARIMA 省心得多。判断标准很简单ARIMA 的残差检验通不过且滚动验证误差和基线拉不开差距就说明模型结构已经装不下这份数据的复杂度了。不要恋战换个模型比硬调参更省时间。6. 一个提升预测稳定性的技巧用置信区间替代点预测做备货最后一个价值点也是我踩过坑之后的固定习惯永远不要拿着预测均值去定备货量。点预测看着是唯一答案实际业务里成本最怕的是预测方差。ARIMA 的好处是天生带置信区间把它转换成库存决策的上下限比单纯优化点预测精度有效得多。fit_final ARIMA(df, orderbest_order).fit() forecast_final fit_final.get_forecast(steps14) mean forecast_final.predicted_mean ci forecast_final.conf_int(alpha0.2) # 80%置信区间 stock_plan pd.DataFrame({ point_forecast: np.floor(mean).astype(int), low_stock: np.floor(ci.iloc[:, 0]).clip(lower0).astype(int), high_stock: np.ceil(ci.iloc[:, 1]).astype(int) }) print(stock_plan)alpha0.2 得到 80% 置信区间意思是未来两周销量有八成概率落在上下限之间。我一般会保守一点取 90% 或 95%区间越宽安全库存越高但也占用资金。实际操作里低于下限就补货高于上限就观望点预测只用来定中间目标。有一个教训印象很深某次只看均值备货结果旺季销量冲到了 95% 置信区间的上沿库存直接断掉从那以后任何预测结果我都会带着区间一起看这个习惯让备货准确率高了不少。希望帮到你。本文还有配套的精品资源点击获取