Python SARIMA 实战:从定阶到滚动预测的完整流程

发布时间:2026/10/12 23:12:43
Python SARIMA 实战:从定阶到滚动预测的完整流程 简介本资源面向计算机、电子信息工程、数学等专业的大学生及时间序列预测初学者提供一套完整的SARIMA模型Python实现方案可用于课程设计、期末大作业或毕业设计。压缩包共5个文件包含3个csv数据文件、1个xlsx表格和1个py源码脚本整体约61KB数据与代码配套齐全便于直接运行与验证。代码采用参数化编程参数可灵活调整且配有保姆级注释几乎一行一注释极大降低小白入门门槛。读者可从中掌握SARIMA建模的完整流程包括数据读取、平稳性检验、差分处理、模型定阶、训练预测及结果可视化同时理解时间序列预测的核心思路与调参方法。目前已有448人学习下载适合希望快速上手SARIMA并完成实战项目的学习者参考。1. 从一份带季节波动的销量表说起SARIMA 到底解决什么问题手里有一份按天或按月记录的销量、流量、能耗数据画出来是一条明显带周期起伏的曲线——每年夏天高、冬天低或者每周五冲顶、周末回落。这种数据用普通 ARIMA 去拟合残差里会残留一大块规律性波动预测值总是慢半拍误差在周期拐点上集中爆发。SARIMA季节性差分自回归移动平均就是为这种「趋势 周期」叠加的场景准备的它在 ARIMA 的自回归、差分、移动平均三件套之外额外加了一组季节性参数专门吃掉周期成分。这篇笔记讲的是用 Python 的 statsmodels 把 SARIMA 从数据到预测完整跑通包含可复现的源码结构和一份能直接替换的数据模板。适合已经会 pandas 基础操作、想给业务做短期预测的工程师也适合被「预测总在周期点翻车」折磨过的人。核心结论先放这SARIMA 不难难的是定阶和季节性差分这两步玄学感主要来自这里。2. 先把数据和时间轴对齐SARIMA 的输入长什么样2.1 时间序列的硬性格式要求statsmodels 的 SARIMA 对输入挑剔得有点不讲道理。它要的是一个带 DatetimeIndex 的一维 Series索引必须是规整的、无缺失的时间点值必须是数值型。很多人翻车就翻在第一步从 Excel 或数据库读出来的日期是字符串或者中间缺了几天模型不会报错但拟合出来的结果完全没法解释。我一般会先做三件事把日期列转成 datetime、设为索引、用 asfreq 把频率固定下来。频率这个参数很关键月度数据是 MS月初或 M日度是 D小时是 H。频率定错后面的季节性周期 s 就全错了。import pandas as pd import numpy as np # 读取原始数据假设两列date, value df pd.read_csv(sales.csv) # 1. 日期转 datetime并设为索引 df[date] pd.to_datetime(df[date]) df df.set_index(date).sort_index() # 2. 固定频率月度数据用 MS月初缺失点补 NaN series df[value].asfreq(MS) # 3. 检查缺失和类型 print(series.dtype) # 必须是 float64 或 int64 print(series.isna().sum()) # 缺失数量后面要处理 print(series.index.freq) # 确认频率已固定这段代码的逻辑是先把时间轴变成模型能识别的对象再强制频率对齐。asfreq 的作用是把不规整的时间点补齐成等间隔缺的地方填 NaN这样模型才知道「每个时间步代表多久」。参数上MS 表示每月第一天如果你的数据是每月最后一天记录用 M。日度数据用 D别用 B工作日除非你的业务确实只在工作日发生。2.2 缺失值处理与平稳性初判补齐频率后大概率会出现 NaN。SARIMA 本身不处理缺失得先填。常见做法是线性插值或前向填充但要注意如果缺失是连续一大段插值会引入虚假规律这时候宁可截断数据。我一般对零星缺失用 interpolate对连续超过 3 个点的缺失直接标记出来人工确认。填完之后做平稳性初判。SARIMA 的 d 和 D 两个差分参数就是为平稳性服务的。用 ADF 检验看是否需要差分用季节差分看周期是否被消掉。from statsmodels.tsa.stattools import adfuller # 线性插值填补零星缺失 series series.interpolate(methodlinear) # 原始序列 ADF 检验 adf_result adfuller(series.dropna()) print(ADF统计量:, adf_result[0]) print(p值:, adf_result[1]) # 一阶季节差分假设周期 s12 series_seasonal_diff series.diff(12).dropna() adf_result2 adfuller(series_seasonal_diff) print(季节差分后p值:, adf_result2[1])ADF 的原假设是「存在单位根不平稳」p 值小于 0.05 才能拒绝原假设、认为平稳。如果原始序列 p 值很大先做一阶普通差分 d1如果差分后仍有明显周期再做季节差分 D1。这里 s12 是月度数据的常见周期日度数据如果按周波动就是 s7。注意差分次数不是越多越好每差一次就丢一批数据d 和 D 通常不超过 1。3. 定阶才是 SARIMA 的真正门槛p、d、q 与 P、D、Q 怎么选3.1 两组参数的分工与取值边界SARIMA 的参数写成 (p, d, q)(P, D, Q)s前一组管非季节部分后一组管季节部分s 是周期长度。p 是自回归阶数看的是「当前值和前 p 个值相关」q 是移动平均阶数看的是「当前值和前 q 个预测误差相关」d 是普通差分次数。带大写的 P、D、Q 是同样的逻辑但作用在「相隔 s 个时间步」的滞后上。取值边界上p 和 q 一般不超过 3P 和 Q 不超过 2d 和 D 取 0 或 1。这不是硬规定是经验阶数越高参数越多过拟合风险越大而且计算量涨得很快。我见过有人把 p 设到 10结果模型把噪声也学进去了样本外预测一塌糊涂。定阶有两条路看 ACF/PACF 图或者网格搜索 AIC/BIC。前者快但主观后者慢但客观。实际项目里我一般先用图定个大致范围再用网格搜索在范围内挑最优。3.2 用 ACF 和 PACF 图缩小范围ACF 是自相关函数PACF 是偏自相关函数。经验规则AR 模型的 ACF 拖尾、PACF 截尾MA 模型的 ACF 截尾、PACF 拖尾。截尾指的是某个滞后之后突然掉到置信区间内拖尾是缓慢衰减。import matplotlib.pyplot as plt from statsmodels.graphics.tsaplots import plot_acf, plot_pacf fig, axes plt.subplots(2, 2, figsize(12, 8)) # 非季节部分看普通差分后的序列 plot_acf(series.diff(1).dropna(), axaxes[0, 0], lags20) plot_pacf(series.diff(1).dropna(), axaxes[0, 1], lags20) # 季节部分看季节差分后的序列滞后按 s 的倍数看 plot_acf(series.diff(12).dropna(), axaxes[1, 0], lags36) plot_pacf(series.diff(12).dropna(), axaxes[1, 1], lags36) plt.tight_layout() plt.show()看图时重点找「第几个滞后开始落进蓝色置信带」。比如 PACF 在第 2 个滞后后截尾p 就试 2ACF 在第 1 个滞后后截尾q 就试 1。季节部分看滞后 12、24、36 的位置如果 PACF 在 12 处有个明显尖峰然后截尾P 试 1。图只是给范围别指望它给出唯一答案。3.3 网格搜索 AIC 选最优组合把图给出的范围做成候选集用循环跑一遍比 AIC 或 BIC。AIC 越小越好它同时惩罚拟合误差和参数数量。这一步计算量大建议先把范围压小。import warnings from statsmodels.tsa.statespace.sarimax import SARIMAX warnings.filterwarnings(ignore) # 抑制收敛警告正式项目要记录 best_aic np.inf best_order None best_seasonal None # 候选范围按 ACF/PACF 图缩小后设定 p_range range(0, 3) d_range [1] q_range range(0, 3) P_range range(0, 2) D_range [1] Q_range range(0, 2) s 12 for p in p_range: for q in q_range: for P in P_range: for Q in Q_range: try: model SARIMAX( series, order(p, 1, q), seasonal_order(P, 1, Q, s), enforce_stationarityFalse, enforce_invertibilityFalse, ) result model.fit(dispFalse) if result.aic best_aic: best_aic result.aic best_order (p, 1, q) best_seasonal (P, 1, Q, s) except Exception: continue print(最优AIC:, best_aic) print(非季节阶数:, best_order) print(季节阶数:, best_seasonal)这段是定阶的核心。enforce_stationarity 和 enforce_invertibility 设成 False 是为了让搜索过程不因为参数越界而中断但最终选出的模型要回头检查这两个性质。d 和 D 固定为 1 是因为前面已经验证过差分后平稳如果数据本身平稳这两个可以设 0。循环里用 try 包住是因为某些参数组合会导致不收敛直接跳过。跑完拿到最优组合别急着用下一步要做残差诊断。4. 拟合、诊断、预测把模型跑成能交付的结果4.1 拟合与残差白噪声检验拿到最优阶数后正式拟合然后检查残差。残差应该是白噪声——没有自相关、均值接近零、方差稳定。如果残差里还有规律说明模型没吃干净信息得回去调阶数。from statsmodels.stats.diagnostic import acorr_ljungbox # 用最优阶数正式拟合 final_model SARIMAX( series, orderbest_order, seasonal_orderbest_seasonal, enforce_stationarityFalse, enforce_invertibilityFalse, ) final_result final_model.fit(dispFalse) # 残差 Ljung-Box 检验滞后取 10 和 20 lb_test acorr_ljungbox(final_result.resid, lags[10, 20], return_dfTrue) print(lb_test) # 残差描述统计 print(final_result.resid.describe())Ljung-Box 检验的原假设是「残差是白噪声」p 值大于 0.05 才说明残差没有剩余自相关模型合格。如果 p 值很小说明还有信息没提取常见原因是阶数不够或者季节性周期设错。残差均值应该接近 0标准差要稳定如果残差图里出现明显的喇叭口方差随时间变大考虑对数据取对数再建模。4.2 预测与置信区间输出预测分样本内和样本外。样本内看拟合效果样本外才是真正要交付的。get_forecast 可以同时给出预测值和置信区间。# 样本外预测未来 12 期 forecast_steps 12 forecast final_result.get_forecast(stepsforecast_steps) # 预测均值 pred_mean forecast.predicted_mean # 95% 置信区间 pred_ci forecast.conf_int(alpha0.05) # 拼成结果表 result_df pd.DataFrame({ forecast: pred_mean, lower: pred_ci.iloc[:, 0], upper: pred_ci.iloc[:, 1], }) print(result_df) # 保存 result_df.to_csv(sarima_forecast.csv, encodingutf-8-sig)get_forecast 返回的对象里predicted_mean 是点预测conf_int 是区间。alpha0.05 对应 95% 置信区间想宽一点用 0.1窄一点用 0.01。预测步长别设太大SARIMA 的误差会随步长累积月度数据预测 12 期还算靠谱超过 24 期基本只能看趋势方向。保存时用 utf-8-sig 是为了 Excel 打开不乱码这是血泪经验。4.3 滚动预测评估真实误差一次性预测 12 期看着漂亮但真实业务里你每个月都会拿到新数据。滚动预测才是贴近实战的评估方式每次只用历史数据预测下一期然后纳入真实值再预测下一期。from sklearn.metrics import mean_absolute_error, mean_squared_error # 留出最后 12 期做测试 train series[:-12] test series[-12:] history list(train) predictions [] for t in range(len(test)): model SARIMAX( history, orderbest_order, seasonal_orderbest_seasonal, enforce_stationarityFalse, enforce_invertibilityFalse, ) result model.fit(dispFalse) yhat result.forecast(steps1).iloc[0] predictions.append(yhat) history.append(test[t]) # 纳入真实值 mae mean_absolute_error(test, predictions) rmse np.sqrt(mean_squared_error(test, predictions)) print(MAE:, mae) print(RMSE:, rmse)滚动预测的逻辑是「走一步、看一步、再预测下一步」每一步都重新拟合。这样评估出来的误差才是模型上线后的真实水平。MAE 和 RMSE 结合看MAE 反映平均偏差RMSE 对大误差更敏感。如果 RMSE 远大于 MAE说明存在个别预测严重偏离的点要回去看那几个时间点是不是有异常事件。这段代码慢因为每步都重新拟合数据量大时可以把重新拟合改成用已有模型追加观测值。5. 避坑与排查SARIMA 落地时最容易翻车的 5 个点5.1 现象模型不收敛报一堆警告原因通常是阶数太高、数据里有极端异常值或者差分后序列方差过大。解决先把 p、q、P、Q 降到 1 以内试确认能收敛再逐步加对异常值做 winsorize 或剔除对数据取对数压缩方差。收敛警告不要用 warnings.filterwarnings 一盖了之正式项目里要记录哪些参数组合失败了。5.2 现象预测值是一条直线原因多半是 d 或 D 设多了差分把趋势和周期都消掉了模型只剩均值可预测。解决回头做 ADF 检验确认到底需要几次差分。很多平稳序列根本不需要差分d0、D0 反而预测更准。另一个可能是 p、q 全设成 0模型退化成纯均值模型。5.3 现象样本内拟合完美样本外一塌糊涂典型的过拟合。原因阶数过高、参数过多模型把噪声当规律学了。解决用 AIC/BIC 而不是只看拟合优度做滚动预测评估把数据分成训练、验证、测试三段验证集上调参测试集上只评估一次。我一般会强制要求样本外误差不能比样本内差 3 倍以上。5.4 现象季节性完全没被捕捉原因s 设错了。月度数据 s12季度数据 s4周数据 s7日数据如果按年周期是 s365。很多人把月度数据的 s 设成 30模型完全找不到周期。解决先画图确认周期长度用 pandas 的 seasonal_decompose 分解出季节项看它的周期是多少再设 s。5.5 现象预测置信区间宽得没法用原因数据本身噪声大或者预测步长太长。解决缩短预测步长对数据做平滑或聚合日度聚成周度检查是否有结构性断点比如政策变化、促销活动断点前后的数据要分开建模。置信区间宽不一定是模型问题有时候是数据本身就不支持精确预测这时候要如实告诉业务方。6. 把 SARIMA 用稳的几个进阶习惯第一个习惯是给模型加外生变量。SARIMA 可以扩展成 SARIMAX把促销、节假日、天气这些已知影响因素作为 exog 传进去预测精度往往能提升一截。用法是在 fit 和 forecast 时都传入同名的外生变量矩阵注意预测期的外生变量要提前准备好。# exog 是形状为 (n_samples, n_features) 的数组 model SARIMAX( series, exogexog_train, orderbest_order, seasonal_orderbest_seasonal, ) result model.fit(dispFalse) forecast result.get_forecast(steps12, exogexog_future)第二个习惯是定期重估参数。业务数据会漂移半年前的最优阶数现在可能不是最优了。我一般设一个季度重跑一次网格搜索把新数据纳入看最优阶数有没有变。变了就更新没变就继续用。第三个习惯是留后悔药。每次定阶、每次调参、每次预测都把参数、AIC、样本外误差记进一张表。下面是我常用的记录格式日期orderseasonal_orderAIC样本外MAE备注2024-01(1,1,1)(1,1,1,12)452.318.7基线2024-04(2,1,1)(1,1,0,12)448.116.2去掉Q后更稳2024-07(1,1,2)(1,1,1,12)450.517.9数据漂移回退这张表的价值在于当业务方问「为什么这次预测不准」时你能拿出历史对比说清楚是数据变了还是模型退化了。没有这张表调参就是黑匣子出了问题只能重来。最后一个习惯是别迷信单一模型。SARIMA 擅长线性周期遇到复杂非线性模式会力不从心。我通常会把 SARIMA 和一个简单的基线比如季节朴素法直接用去年同期的值对比如果 SARIMA 打不过基线说明数据里没有 SARIMA 能利用的额外信息这时候硬上模型就是自欺欺人。基线跑起来只要几行代码但能帮你省下大量无效调参时间。这套流程我在几个销量预测项目里反复用过最深的教训是定阶花的时间应该占整个项目的六成剩下四成才是拟合和预测。很多人反过来急着跑模型结果在定阶上偷的懒全在样本外误差上还回来了。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询