简介:这份资源是面向Python数据分析初学者、统计学课程学习者及毕业设计/期末大作业需求者的一套ARIMA时间序列销量预测完整方案,重点解决如何用statsmodels完成数据平稳化、模型定阶、参数估计与模型检验等建模流程。压缩包共13个文件,约254KB,包含4个py脚本(建模与预测主逻辑)、1个xls与1个xlsx数据表(原始销量与预测对比结果)、5个png图表(时序图、差分后自相关与偏相关图、上线效果等)、1个md说明及1个txt依赖清单,结构紧凑便于直接运行复现。资源采用每月上、中、下旬三次预测策略,将月上旬与中旬实际销量作为先验知识提升准确率,并配有requirements.txt与sales.py入口,方便快速跑通。目前已有2053人学习下载,适合作为课程设计参考、建模练手与预测结果对照的实战素材。
1. 拿到这份 ARIMA 销量预测包,先搞清楚它能替你干什么
电商运营月底被问「下个月能卖多少」,答不上来;课程设计要交一份带完整数据、能跑通、有图表的时间序列作业,翻遍 GitHub 全是半成品。这份salesPredict-master就是冲着这两个场景来的:它把一份真实销量表sales.xls、ARIMA 建模脚本arimaModel.py、入口sales.py、依赖清单requirements.txt和五张结果图(时序图、一阶差分后的自相关/偏相关、上线效果、预测对比)全打包在一起,解压就能跑。技术栈是 Python + statsmodels,核心是 ARIMA 的平稳化、定阶、参数估计与检验。适合做毕业设计、期末大作业、课程设计的人,也适合想给业务加个轻量预测模块的工程师。它不承诺预测多准,但把「一条时间序列怎么从原始销量走到预测值」这条链路完整摊开了。
2. 拆开目录看结构:每个文件在链路里干什么
2.1 目录清单与职责划分
先把包解开,别急着python sales.py。目录结构大致是这样:
salesPredict-master/ ├── data/ │ └── sales.xls # 原始销量数据,按时间排列 ├── model/ │ ├── __init__.py │ ├── arimaModel.py # ARIMA 建模核心:差分、定阶、拟合、预测 │ └── settings.py # 路径、参数、预测点配置 ├── test/ │ └── 某企通预测值与实际值对比_正式版.xlsx ├── pictures/ │ ├── 销量时序图.png │ ├── 一阶差分后,序列自相关情况.png │ ├── 一阶差分后,序列偏相关情况.png │ ├── 上线效果.png │ └── 销量预测测试情况.png ├── sales.py # 程序入口 ├── requirements.txt └── README.mddata/sales.xls是唯一的数据源,所有建模都从它读。model/arimaModel.py是真正干活的地方,差分、ADF 检验、ACF/PACF 定阶、拟合、预测都在这里。model/settings.py把路径和预测参数抽出来,改配置不用动主逻辑。sales.py只负责串流程。pictures/里那五张图不是装饰,是你验证模型有没有跑对的参照物——尤其「一阶差分后自相关/偏相关」两张,直接对应定阶环节。
2.2 数据长什么样,决定了后面怎么建模
sales.xls是月度销量序列,一行一个时间点。ARIMA 对数据的要求是:单变量、等间隔、按时间升序。如果你的表里有多列(比如多个 SKU),得先拆成单列再喂进去,否则 statsmodels 会报维度错误。常见做法是先用 pandas 读进来,确认索引是时间类型、没有缺失值、没有重复时间点:
import pandas as pd # 读原始销量表,指定时间列解析为 datetime df = pd.read_excel('data/sales.xls', parse_dates=['date']) df = df.sort_values('date').set_index('date') # 按时间升序并设为索引 series = df['sales'].astype(float) # 只取销量列,转 float print(series.head()) print('缺失值:', series.isna().sum()) print('时间间隔:', series.index.to_series().diff().value_counts().head())这段代码做三件事:解析时间列、按时间排序、检查缺失和间隔。parse_dates保证时间列不是字符串;set_index让后续 statsmodels 能识别时间轴;diff().value_counts()看间隔是否统一——如果出现多种间隔,说明数据有断月,得先补全或重采样。参数上,astype(float)不能省,Excel 读出来常是 object 或 int,ARIMA 拟合时对类型敏感。
2.3 依赖装不对,后面全是玄学报错
requirements.txt里主要是 statsmodels、pandas、numpy、matplotlib、xlrd(读 .xls 用)。装的时候注意版本,statsmodels 不同版本ARIMA和SARIMAX的接口有差异,老代码常用ARIMA,新版本推荐SARIMAX。稳妥做法是建虚拟环境再装:
python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install -r requirements.txt如果pip install卡在 statsmodels 编译,多半是缺 C 编译环境,Windows 上直接装预编译 wheel 即可。装完先python -c "import statsmodels; print(statsmodels.__version__)"确认能导入,再往下走。这一步翻车的概率比建模本身还高,别跳过。
3. ARIMA 建模全流程:从平稳化到定阶的每一步
3.1 平稳性检验与差分:d 参数怎么定
ARIMA 的 p、d、q 三个参数里,d 是差分阶数,作用是让序列平稳。原始销量序列通常有趋势或季节性,不平稳,直接建模会得到虚假回归。先做 ADF 检验:
from statsmodels.tsa.stattools import adfuller def adf_test(series, name='series'): result = adfuller(series.dropna(), autolag='AIC') print(f'{name} ADF统计量: {result[0]:.4f}') print(f'p值: {result[1]:.4f}') print(f'临界值: {result[4]}') return result[1] p_original = adf_test(series, '原始序列') if p_original > 0.05: series_diff1 = series.diff(1).dropna() # 一阶差分 p_diff1 = adf_test(series_diff1, '一阶差分')ADF 的原假设是「存在单位根,不平稳」。p 值大于 0.05 就不能拒绝原假设,说明不平稳,需要差分。autolag='AIC'让 statsmodels 自动选滞后阶数,比手动指定稳。一阶差分后如果 p 值仍大于 0.05,再差一次,但 d 一般不超过 2——差太多会把序列里的有效信息也差没。pictures/一阶差分后,序列自相关情况.png就是这一步的可视化结果,对照着看差分后 ACF 是否快速衰减到零附近。
3.2 ACF/PACF 定阶:p 和 q 怎么读图
差分把 d 定下来后,p(自回归阶数)和 q(移动平均阶数)靠 ACF 和 PACF 图判断。规则是:ACF 拖尾、PACF 截尾,用 AR(p),p 取 PACF 截尾处的滞后;ACF 截尾、PACF 拖尾,用 MA(q),q 取 ACF 截尾处的滞后;两个都拖尾,用 ARMA。代码:
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf import matplotlib.pyplot as plt fig, axes = plt.subplots(2, 1, figsize=(10, 8)) plot_acf(series_diff1, lags=20, ax=axes[0]) # 自相关 plot_pacf(series_diff1, lags=20, ax=axes[1]) # 偏自相关 plt.tight_layout() plt.savefig('pictures/acf_pacf.png')lags=20是看前 20 个滞后点,月度数据一般看 12 到 24 就够。读图时别死磕单点,看整体形态:ACF 在几个滞后后落进置信带(蓝色区域)内,就算截尾。pictures/里那两张差分后的自相关/偏相关图就是标准参照。如果图上看不准,用 AIC/BIC 网格搜索兜底:
import warnings from statsmodels.tsa.arima.model import ARIMA warnings.filterwarnings('ignore') best_aic = float('inf') best_order = None for p in range(0, 4): for q in range(0, 4): try: model = ARIMA(series, order=(p, 1, q)) result = model.fit() if result.aic < best_aic: best_aic = result.aic best_order = (p, 1, q) except Exception: continue print('最优阶数:', best_order, 'AIC:', best_aic)这段网格搜索把 p、q 各试 0 到 3,d 固定为 1,取 AIC 最小的组合。warnings.filterwarnings('ignore')是因为部分阶数会触发收敛警告,不影响搜索。AIC 越小越好,但别只看 AIC——阶数太高会过拟合,p、q 超过 3 就要警惕。
3.3 拟合、检验与预测:模型能不能用看残差
定好阶数后拟合,然后必须做残差检验。残差应该是白噪声,否则说明模型没把信息提取干净:
from statsmodels.stats.diagnostic import acorr_ljungbox model = ARIMA(series, order=best_order) result = model.fit() print(result.summary()) # Ljung-Box 检验残差是否为白噪声 lb = acorr_ljungbox(result.resid, lags=[10], return_df=True) print(lb)result.summary()里看系数显著性(P>|z| 小于 0.05)和 AIC。Ljung-Box 的 p 值大于 0.05 才说明残差没有自相关,模型合格。如果 p 值很小,回去调阶数或加差分。预测用result.forecast(steps=n)或result.get_forecast(steps=n),后者能拿置信区间。test/某企通预测值与实际值对比_正式版.xlsx就是预测值和真实值的对照表,pictures/上线效果.png是可视化,跑完自己的结果可以跟它对一下量级。
4. 每月分上中下旬三次预测:这个策略怎么落地
4.1 为什么拆成三个预测点
这是这份资源里最值得抄的设计。传统做法是月初一次性预测整月,误差全压在模型上。它改成每月上旬、中旬、下旬各预测一次当月销量:上旬预测时用上月末为止的历史;中旬预测时,上旬的实际销量已经拿到,作为已知值补进序列;下旬同理。越往后,已知信息越多,预测越准。本质是把「纯外推」变成「外推 + 当月已实现部分」,对月度销量这种受促销节奏影响大的序列特别有效。
4.2 在代码里怎么实现滚动预测
核心是把当月已发生的实际值拼到历史序列尾部,再预测剩余部分。伪代码逻辑:
def predict_by_period(series, order, period_day): """ series: 截至当前已知的历史销量 order: ARIMA 阶数 period_day: 当前是上旬(10)/中旬(20)/下旬(月底) """ model = ARIMA(series, order=order).fit() # 预测当月剩余天数对应的销量,这里按月粒度简化为预测当月总量 forecast = model.get_forecast(steps=1) pred = forecast.predicted_mean.iloc[0] return pred # 上旬:用上月末数据预测 pred_early = predict_by_period(series_until_last_month, best_order, 10) # 中旬:把上旬实际值拼进去再预测 series_mid = series_until_last_month.append(pd.Series([actual_early])) pred_mid = predict_by_period(series_mid, best_order, 20)关键在series_mid这一步:把上旬实际销量作为新数据点追加,序列变长,模型重新拟合。settings.py里通常会有预测点配置(比如哪几天触发),改周期不用动arimaModel.py。注意每次追加后要重新拟合,不能复用旧模型——参数会变。
4.3 预测结果怎么评估
评估指标用 MAE、RMSE、MAPE。MAPE 对销量这种正值序列最直观:
import numpy as np def mape(actual, pred): actual, pred = np.array(actual), np.array(pred) return np.mean(np.abs((actual - pred) / actual)) * 100 print('MAPE: %.2f%%' % mape(actual_list, pred_list))test/里的对比表可以直接拿来算。一般月度销量预测 MAPE 在 10% 到 20% 算可用,超过 30% 要回头看数据质量或阶数。pictures/销量预测测试情况.png是现成的评估图,对照自己的输出看趋势是否一致。
5. 避坑与排查:这几个地方最容易翻车
5.1 现象:ADF 检验 p 值一直大于 0.05,差分也不管用
原因通常是序列有季节性,普通差分消不掉。月度数据常见 12 期周期,一阶差分后仍有季节性自相关。解决:改用季节差分series.diff(12),或者直接上 SARIMA,在 order 里加季节性参数(p,d,q)(P,D,Q,12)。别硬差到 d=3,那样序列只剩噪声。
5.2 现象:模型拟合报「LinAlgError: Singular matrix」
原因多是数据里有重复时间点或常量段,导致矩阵奇异。解决:先series = series[~series.index.duplicated()]去重,再检查有没有连续相同的值——如果有,考虑加极小扰动或换数据段。另外 p、q 设太大也会触发,网格搜索时把上限压到 3 以内。
5.3 现象:预测值是一条直线,完全不波动
原因通常是 d 设太大,把序列差成了白噪声,模型只能预测均值。解决:回退差分阶数,重新做 ADF,确认 d 刚好让序列平稳即可,不要多差。另一个可能是 p、q 都是 0,等于没建模,检查网格搜索有没有真的跑起来。
5.4 现象:pip install -r requirements.txt报 xlrd 相关错误
原因:新版 xlrd 只支持 .xls,不支持 .xlsx,而 pandas 读 Excel 的引擎选择跟版本绑定。解决:确认sales.xls确实是 .xls 格式;如果报「Excel xlsx file; not supported」,说明文件其实是 xlsx 改了后缀,用openpyxl读或另存为真 xls。requirements 里 xlrd 版本建议锁 1.2.0 附近。
5.5 现象:每月三次预测的结果反而比一次预测差
原因:上旬实际值样本太少,拼进去后引入噪声,模型被带偏。解决:中旬、下旬再启用滚动,上旬仍用纯历史预测;或者给新拼入的值做平滑。这个策略不是无脑三次都滚,得看当月已实现数据量够不够。
6. 进阶:把 ARIMA 当基线,用残差和滚动窗口再榨一点精度
跑通基础流程后,真正拉开差距的是两件事:滚动窗口重拟合和残差利用。固定用全历史拟合,模型对近期变化反应迟钝;改成滑动窗口(比如只用最近 24 个月)能让参数跟着最新趋势走:
def rolling_forecast(series, order, window=24, horizon=1): preds, actuals = [], [] for i in range(window, len(series) - horizon + 1): train = series.iloc[i-window:i] # 滑动窗口训练集 test = series.iloc[i:i+horizon] # 待预测点 model = ARIMA(train, order=order).fit() pred = model.get_forecast(steps=horizon).predicted_mean preds.extend(pred.values) actuals.extend(test.values) return actuals, predswindow=24是经验值,月度数据两年窗口通常够覆盖一个完整业务周期。窗口太小(比如 6)参数抖动大,太大(比如全历史)又失去适应性。跑完用 MAPE 对比固定窗口和滚动窗口,选误差小的那个。
另一个技巧是把 ARIMA 的残差喂给一个简单模型做二次修正。ARIMA 假设残差是白噪声,但真实销量里常有促销脉冲,残差里会残留结构。常见做法是对残差再跑一次 ARIMA 或阈值判断,把大残差对应的月份标记为异常点,预测时单独处理。这不是标准 ARIMA 流程,但业务上往往有效。
验证方法上,别只看一张上线效果.png。自己切分训练集和测试集,用滚动预测跑一遍,把 MAPE、RMSE 和test/里的对比表对齐。如果差异大,先查数据时间范围是否一致,再查阶数是否被settings.py里的默认值覆盖了。
血泪经验是:每次改完settings.py里的预测点或窗口参数,我都会强制重跑一遍 ADF 和残差检验,确认模型没被配置改动带偏。ARIMA 这东西,参数之间是联动的,动一个地方,d、p、q 可能都得重看。希望这份拆解帮到你,少走几个我踩过的坑。
本文还有配套的精品资源,点击获取