ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

SARIMA模型实战:从原理到Python代码,搞定时间序列预测

SARIMA模型实战:从原理到Python代码,搞定时间序列预测 1. 项目概述为什么SARIMA是时序预测的“瑞士军刀”如果你正在准备数学建模竞赛或者在工作中需要处理带有明显周期性波动的数据——比如月度销售额、每日用电量、季节性流感病例数——那么SARIMA模型绝对是你工具箱里不可或缺的一把“瑞士军刀”。它不像一些复杂的深度学习模型那样是个“黑箱”也不像简单移动平均那样对趋势和季节束手无策。SARIMA的全称是季节性差分自回归滑动平均模型这个名字听起来复杂但拆开来看它就是经典ARIMA模型的“威力加强版”专门用来对付那些既有长期趋势、又有固定周期比如一年四季、一周七天的时间序列数据。我见过很多新手一上来就套用ARIMA结果对季节性因素视而不见预测结果在波峰波谷上完全错位模型效果惨不忍睹。SARIMA的核心价值就在于它通过引入“季节性差分”和“季节性自回归/滑动平均”项把时间序列中嵌套的周期性规律给“抽”了出来分别建模。这就像处理一个既有年度大趋势比如公司业务逐年增长又有季度内小波动比如每个季度末业绩冲刺的数据时SARIMA能帮你把这两层规律都看清楚、说明白。用Python来实现SARIMA现在最主流、最成熟的库就是statsmodels。它封装了完整的模型识别、参数估计、诊断检验和预测流程你不需要从零开始推导复杂的数学公式而是可以把精力集中在理解数据、调优模型和解释结果上。这对于数模竞赛中时间紧迫的场景或者业务中需要快速验证想法的场景是极大的效率提升。接下来我会带你从原理到代码手把手拆解SARIMA让你不仅能跑通代码更能理解每一个参数背后的意义避开我当年踩过的那些坑。2. 核心原理拆解SARIMA模型的“三层密码”要玩转SARIMA不能只停留在调包层面。你得理解它的“三层密码”非季节性部分、季节性部分以及两者如何协同工作。这决定了你如何解读模型输出以及如何诊断模型的好坏。2.1 基石ARIMA模型的核心思想SARIMA建立在ARIMA之上所以我们先快速回顾一下ARIMA。ARIMA模型可以看作由三个部分组成AR(p) - 自回归当前值用过去p个时刻的历史值来解释。公式可以简化为Y_t c φ1*Y_{t-1} φ2*Y_{t-2} ... φp*Y_{t-p} ε_t。这里的φ是自回归系数衡量过去值对现在的影响。p就是自回归的阶数你可以理解为“回头看几步”。I(d) - 差分如果序列不平稳有趋势通过差分使其平稳。一阶差分就是Y_t Y_t - Y_{t-1}二阶差分就是在一阶差分的基础上再做一次。d是差分的阶数。平稳性是很多时间序列模型的基本要求意味着序列的统计特性如均值、方差不随时间变化。MA(q) - 移动平均当前值用过去q个时刻的预测误差白噪声来解释。公式为Y_t μ ε_t θ1*ε_{t-1} θ2*ε_{t-2} ... θq*ε_{t-q}。这里的θ是移动平均系数ε是误差项。q是移动平均的阶数可以理解为模型“记忆”过去预测失误的能力。ARIMA(p,d,q)就是把这三部分组合起来对差分后的平稳序列进行建模。它的目标是找到一组参数(p,d,q)使得模型能最好地捕捉数据中的规律同时剩余的误差ε_t是白噪声随机且不可预测。2.2 升级SARIMA如何引入季节性SARIMA(P,D,Q,s)在ARIMA的基础上增加了一套完全平行的“季节性组件”专门用来刻画周期为s的季节性模式。季节性差分 (D)这是对付季节性不平稳的关键。如果序列在每个周期内的波动模式不稳定比如每年的波峰越来越高就需要做季节性差分。公式是Y_t Y_t - Y_{t-s}。例如对于月度数据(s12)季节性差分就是用本月的数据减去去年同月的数据。D是季节性差分的阶数通常为0或1。季节性自回归 (P)表示当前值与其过去一个或多个完整周期前的值之间的关系。例如P1意味着本月的值与去年同月的值有线性关系。季节性移动平均 (Q)表示当前值与过去一个或多个完整周期前的预测误差之间的关系。周期长度 (s)这是最重要的超参数必须根据你的数据先验知识来定。月度数据s12季度数据s4周数据s7小时数据日周期s24。所以一个完整的SARIMA模型写作SARIMA(p,d,q)(P,D,Q)[s]。模型实际上是在同时对“差分后的序列”和“季节性差分后的序列”进行ARMA建模。你可以把它想象成两个齿轮在咬合转动一个齿轮处理短期相邻点的依赖和误差另一个大齿轮处理长期跨周期的依赖和误差。2.3 模型诊断你的SARIMA“健康”吗拟合完模型不是终点诊断检验至关重要。如果模型没学好预测就是“垃圾进垃圾出”。主要看三点残差分析理想的残差应该是一个白噪声序列没有自相关性。我们通常用“Ljung-Box检验”来定量判断。如果检验的p值大于0.05例如说明残差是白噪声模型提取了数据中所有可预测的信息。参数显著性模型估计出的每个系数φ, θ, Φ, Θ都应该是统计显著的通常看p值0.05。如果某个系数不显著说明对应的项可能不重要可以考虑简化模型。信息准则AIC赤池信息准则和BIC贝叶斯信息准则是模型选择的量化指标。在比较多个候选模型时AIC/BIC值越小通常意味着模型在拟合优度和复杂度之间取得了更好的平衡。注意AIC/BIC主要用于模型比较而非绝对判断。一个AIC更低的模型如果残差检验不通过依然不是好模型。实操心得模型诊断一定要做我早期曾只追求AIC最小结果预测外推时偏差极大。后来发现那个“最优”模型的残差里还有明显的周期性说明它连基本的季节性都没抓全。一个通过残差检验的简单模型往往比一个AIC低但残差有问题的复杂模型更可靠。3. 完整实战用Python预测月度航空客流量理论说得再多不如动手跑一遍。我们使用经典的“航空乘客”数据集AirPassengers它记录了1949-1960年每月的国际航线乘客总数具有明显的增长趋势和年度季节性是演示SARIMA的绝佳例子。3.1 环境准备与数据探索首先确保你的Python环境安装了必要的库statsmodels,pandas,numpy,matplotlib。如果没有通过pip install statsmodels pandas numpy matplotlib安装。import numpy as np import pandas as pd import matplotlib.pyplot as plt from statsmodels.tsa.statespace.sarimax import SARIMAX from statsmodels.tsa.stattools import adfuller from statsmodels.graphics.tsaplots import plot_acf, plot_pacf import warnings warnings.filterwarnings(ignore) # 忽略一些不影响运行的警告 # 加载数据 url https://raw.githubusercontent.com/jbrownlee/Datasets/master/airline-passengers.csv df pd.read_csv(url, parse_dates[Month], index_colMonth) df.columns [Passengers] print(df.head()) print(f\n数据形状: {df.shape})数据加载后我们先画图直观感受一下plt.figure(figsize(12, 6)) plt.plot(df.index, df[Passengers], markero, linestyle-, linewidth1, markersize3) plt.title(Monthly Airline Passengers (1949-1960)) plt.xlabel(Year-Month) plt.ylabel(Passengers (Thousands)) plt.grid(True, alpha0.3) plt.show()从图上你能清晰地看到两点1) 一个强劲的向上增长趋势2) 每年夏季年中出现波峰冬季出现波谷的稳定12个月周期。这就是一个典型的非平稳强季节性的序列。3.2 平稳性检验与差分确定SARIMA要求序列在差分为后是平稳的。我们使用Augmented Dickey-Fuller (ADF)检验来判断。# 原始序列的ADF检验 result adfuller(df[Passengers]) print(原始序列ADF检验结果:) print(fADF Statistic: {result[0]:.4f}) print(fp-value: {result[1]:.4f}) print(Critical Values:) for key, value in result[4].items(): print(f\t{key}: {value:.4f}) # 通常p-value 0.05 认为非平稳输出结果大概率显示p值远大于0.05无法拒绝“序列非平稳”的原假设。所以我们需要差分。如何确定d和D确定d常规差分阶数通常先做一阶常规差分消除趋势。对差分后的序列再做ADF检验如果平稳了则d1如果不平稳可能需要进行二阶差分(d2)但实践中d很少大于2。# 一阶差分 df[Passengers_diff1] df[Passengers].diff().dropna() result_diff1 adfuller(df[Passengers_diff1].dropna()) print(f一阶差分后p-value: {result_diff1[1]:.4f})确定D季节性差分阶数对于有明显年度周期的数据我们尝试做一阶季节性差分s12。将季节性差分后的序列或先常规差分再季节性差分的序列进行ADF检验。# 季节性差分 (周期s12) df[Passengers_seasonal_diff] df[Passengers].diff(periods12).dropna() result_seasonal adfuller(df[Passengers_seasonal_diff].dropna()) print(f季节性差分(12)后p-value: {result_seasonal[1]:.4f})更常见的做法是进行“一阶常规差分一阶季节性差分”这能同时消除趋势和季节性。我们可以用statsmodels的seasonal_decompose函数做季节性分解更直观地观察。from statsmodels.tsa.seasonal import seasonal_decompose decomposition seasonal_decompose(df[Passengers], modelmultiplicative) # 由于波动幅度随趋势增大这里用乘法模型 fig decomposition.plot() fig.set_size_inches(12, 8) plt.show()从分解图可以清晰看到趋势项、季节项和残差项。这坚定了我们同时使用常规差分和季节性差分的决心。对于本例我们初步设定d1, D1, s12。3.3 识别p, q, P, QACF与PACF图解读确定了d, D, s后我们需要识别AR和MA的阶数(p,q)以及季节性阶数(P,Q)。这里主要依靠自相关函数(ACF)图和偏自相关函数(PACF)图来分析差分后的平稳序列。ACF图描述当前序列与过去各期序列之间的相关性。ACF拖尾逐渐衰减为0提示可能是AR过程ACF截尾在滞后q阶后突然降至接近0提示可能是MA(q)过程。PACF图描述在排除中间各期影响后当前序列与过去某期序列之间的纯粹相关性。PACF截尾在滞后p阶后突然降至接近0提示可能是AR(p)过程PACF拖尾提示可能是MA过程。# 对“一阶常规差分一阶季节性差分”后的序列绘制ACF/PACF df[diff1_seasonal1] df[Passengers].diff().diff(periods12).dropna() fig, axes plt.subplots(1, 2, figsize(14, 4)) plot_acf(df[diff1_seasonal1].dropna(), lags40, axaxes[0]) plot_pacf(df[diff1_seasonal1].dropna(), lags40, axaxes[1], methodywm) # 使用ywm方法避免警告 plt.show()解读ACF/PACF图需要一些经验看非季节性部分(p,q)观察滞后1,2,3...附近的特征。在滞后1阶后PACF可能呈现截尾提示p1。ACF呈拖尾状。看季节性部分(P,Q)观察滞后s, 2s, 3s...即12, 24, 36...附近的特征。在滞后12阶处PACF有一个显著的尖峰后截尾提示季节性自回归阶数P1。在滞后12阶处ACF也有显著尖峰但随后衰减结合季节性MA考虑。基于以上观察和常见经验对于月度数据季节性部分常取1阶我们可以设定一个初始的候选模型SARIMA(1,1,1)(1,1,1)[12]。这是一个不错的起点。3.4 模型拟合、诊断与参数调优现在我们用SARIMAX函数来拟合模型。SARIMAX比早期的ARIMA函数更强大支持外生变量和更灵活的配置。# 定义模型 model SARIMAX(df[Passengers], order(1, 1, 1), # (p, d, q) seasonal_order(1, 1, 1, 12), # (P, D, Q, s) enforce_stationarityFalse, enforce_invertibilityFalse) # 拟合模型 results model.fit(dispFalse) # dispFalse不显示迭代信息 print(results.summary())summary()会输出海量信息重点关注这几部分系数表 (Coefficients)查看ar.L1,ma.L1,ar.S.L12,ma.S.L12对应的P|z|列。如果p值小于0.05说明该系数显著不为零。如果某个系数不显著比如ma.S.L12的p值很大可以考虑在后续模型中移除该项。信息准则记录下AIC和BIC的值用于模型比较。残差诊断图statsmodels提供了便捷的绘图方法。results.plot_diagnostics(figsize(12, 8)) plt.show()诊断图包含四个子图标准化残差图残差应该围绕0随机波动无明显趋势或周期性。残差直方图核密度估计理想情况下应与正态分布曲线红色大致吻合检验残差是否接近正态分布。正态Q-Q图点应大致分布在红色直线上检验正态性。残差自相关图(ACF)所有滞后阶数的自相关系数应落在蓝色置信区间内即接近0表明残差是白噪声。如果残差ACF图中有显著超出置信区间的柱状条特别是在滞后12,24等季节周期处说明模型未能完全捕捉季节性需要考虑增加P或Q的阶数。如果直方图或Q-Q图严重偏离正态可能需要考虑对数据做变换如对数变换。模型调优策略 我们很少能一次就找到最优参数。通常的做法是构建一个参数网格遍历多种(p,d,q)(P,D,Q)的组合选择AIC最低且通过残差检验的模型。import itertools # 定义参数搜索范围范围不宜过大否则组合爆炸 p d q range(0, 2) # 非季节性参数尝试0和1 P D Q range(0, 2) # 季节性参数尝试0和1 s 12 # 固定周期 pdq list(itertools.product(p, d, q)) seasonal_pdq list(itertools.product(P, D, Q, [s])) best_aic np.inf best_order None best_seasonal_order None warnings.filterwarnings(ignore) # 忽略拟合过程中的警告 for param in pdq: for param_seasonal in seasonal_pdq: try: mod SARIMAX(df[Passengers], orderparam, seasonal_orderparam_seasonal, enforce_stationarityFalse, enforce_invertibilityFalse) res mod.fit(dispFalse) if res.aic best_aic: best_aic res.aic best_order param best_seasonal_order param_seasonal # print(fSARIMA{param}{param_seasonal} - AIC:{res.aic:.2f}) except Exception as e: # print(fError with {param}{param_seasonal}: {e}) continue print(f\n最优模型: SARIMA{best_order}{best_seasonal_order}) print(f最低AIC: {best_aic:.2f})运行这段代码需要一些时间。对于这个数据集最优模型很可能仍然是(1,1,1)(1,1,1,12)或类似组合。记住AIC最低是重要参考但最终模型一定要用诊断图验证。3.5 模型预测与效果评估选定最终模型并拟合后就可以进行预测了。我们预测未来24个月两年的数据。# 使用最优参数重新拟合最终模型假设best_order, best_seasonal_order已从上一步获得 final_model SARIMAX(df[Passengers], orderbest_order, seasonal_orderbest_seasonal_order, enforce_stationarityFalse, enforce_invertibilityFalse) final_results final_model.fit(dispFalse) # 进行样本外预测预测未来24个月 forecast_steps 24 forecast_obj final_results.get_forecast(stepsforecast_steps) forecast_mean forecast_obj.predicted_mean forecast_ci forecast_obj.conf_int() # 置信区间 # 创建未来日期索引 last_date df.index[-1] forecast_index pd.date_range(startlast_date pd.DateOffset(months1), periodsforecast_steps, freqMS) # 可视化历史数据、拟合值和预测值 plt.figure(figsize(14, 7)) plt.plot(df.index, df[Passengers], labelObserved (History), colorblue) # 也可以绘制样本内的拟合值 plt.plot(final_results.fittedvalues.index, final_results.fittedvalues, labelFitted, colorgreen, alpha0.7) # 绘制预测值及置信区间 plt.plot(forecast_index, forecast_mean, labelForecast, colorred, linestyle--) plt.fill_between(forecast_index, forecast_ci.iloc[:, 0], forecast_ci.iloc[:, 1], colorred, alpha0.15, label95% Confidence Interval) plt.title(SARIMA Model Forecast for Airline Passengers) plt.xlabel(Year-Month) plt.ylabel(Passengers) plt.legend(locbest) plt.grid(True, alpha0.3) plt.show() # 打印预测值 forecast_df pd.DataFrame({ Month: forecast_index, Forecast_Passengers: forecast_mean.values, CI_lower: forecast_ci.iloc[:, 0].values, CI_upper: forecast_ci.iloc[:, 1].values }) print(forecast_df.head(12))从预测图中你可以看到模型成功延续了数据的增长趋势和年度季节性波动。置信区间随着预测时间的拉远而逐渐变宽这符合预测不确定性增大的常识。注意事项SARIMA是一种线性模型它假设未来的模式是历史模式的线性延续。对于存在结构性突变如政策变化、突发事件的时间序列SARIMA的长期预测效果会下降。它更擅长中短期预测。4. 避坑指南与高阶技巧在实际应用和数模竞赛中仅仅跑通流程是不够的。下面这些我踩过的坑和总结的技巧能帮你把模型用得更好。4.1 常见问题与解决方案速查表问题现象可能原因排查与解决思路模型拟合失败或报错1. 参数组合不合理导致模型不可逆或不平稳。2. 数据存在NaN或Inf值。3. 季节性周期s设置错误。1. 检查enforce_stationarity和enforce_invertibility参数或尝试更简单的参数组合。2. 确保输入数据是干净的数值型序列处理缺失值。3. 通过绘制时序图、计算自相关确认周期s。预测结果是一条直线或常数1. 差分阶数d或D过高导致序列过度差分失去了原有信息。2. 模型阶数(p,q,P,Q)全部为0变成了随机游走模型。1. 重新检查平稳性检验确保差分阶数恰当。通常d和D为0或1即可。2. 检查ACF/PACF图确保至少包含AR或MA项。预测值的置信区间异常宽1. 模型拟合不佳残差方差大。2. 数据本身波动性极大。3. 预测步长steps设置得太远。1. 重新进行模型诊断优化参数或考虑对数据做变换如对数变换以稳定方差。2. 这是模型对不确定性的诚实反映需谨慎解读长期预测。3. 专注于中短期预测。季节性预测不准确波峰波谷错位1. 季节性周期s设置错误。2. 季节性差分阶数D不足季节性未平稳或过高。3. 季节性AR或MA项(P,Q)阶数不足未能捕捉复杂的季节模式。1. 这是最常见原因务必根据业务知识月/季/周和ACF图在季节滞后处的峰值确认s。2. 观察季节性分解图确认是否需要季节性差分。3. 尝试增加P或Q的值特别是当ACF/PACF在季节滞后处有多个显著峰值时。AIC很低但预测效果差1. 过拟合。模型过于复杂捕捉了噪声而非规律。2. 样本外预测能力与样本内拟合优度是两回事。1. 坚持使用残差诊断图检验。一个过拟合的模型其残差可能看似白噪声但参数过多不稳定。2.务必使用样本外测试将数据分为训练集和测试集在训练集上拟合在测试集上评估预测精度如MAE, RMSE。4.2 数据预处理容易被忽略的关键步骤缺失值处理SARIMA模型通常要求连续的时间序列。对于少量缺失值可以采用前向填充、线性插值或季节性插值。对于大量缺失可能需要考虑其他模型或数据来源。SARIMAX本身可以处理部分缺失但结果可能不稳定。方差稳定化如果序列的波动幅度随着趋势增大而增大就像我们的航空乘客数据这称为“异方差”。直接建模可能效果不佳。一个经典的处理方法是对数变换df[‘Log_Passengers’] np.log(df[‘Passengers’])。在对数尺度上建模预测后再通过指数变换np.exp()还原。这常常能显著提升模型表现。异常值处理时间序列中的异常值如某个月的极端天气导致销量暴跌会干扰模型对整体模式的识别。可以结合业务判断或使用统计方法如3σ原则检测并处理平滑或视为缺失值。4.3 模型选择与评估不止看AIC在数模竞赛或严谨的项目中模型评估必须基于样本外预测。滚动预测法更接近真实预测场景。例如用前48个月数据预测第49个月然后用前49个月数据预测第50个月以此类推。计算整个测试集上预测误差的平均值。常用评估指标MAE (平均绝对误差)mean(abs(实际值 - 预测值))。对异常值不敏感解释直观。RMSE (均方根误差)sqrt(mean((实际值 - 预测值)^2))。惩罚大误差更常用。MAPE (平均绝对百分比误差)mean(abs((实际值 - 预测值)/实际值)) * 100%。适用于不同量级序列的比较但当实际值接近0时MAPE会失真。# 示例简单的样本外评估将最后24个月作为测试集 train df.iloc[:-24] test df.iloc[-24:] model_train SARIMAX(train[Passengers], orderbest_order, seasonal_orderbest_seasonal_order) results_train model_train.fit(dispFalse) # 预测测试集长度 forecast_test results_train.get_forecast(stepslen(test)) predicted_values forecast_test.predicted_mean # 计算RMSE from sklearn.metrics import mean_squared_error rmse np.sqrt(mean_squared_error(test[Passengers], predicted_values)) print(f测试集RMSE: {rmse:.2f})4.4 SARIMA的局限性与替代方案认识到模型的边界和天花板同样重要。局限性线性假设SARIMA是线性模型无法捕捉复杂的非线性关系。单变量只能利用序列自身的历史信息无法融入其他影响因素如价格、促销活动。固定周期要求季节性周期s是固定且已知的。对于多周期如同时存在周周期和年周期或非整数周期数据处理起来麻烦。长期预测能力弱预测步长越长不确定性呈指数增长预测值会收敛到序列的均值或趋势线。进阶/替代方案SARIMAX你正在使用的statsmodels.tsa.statespace.sarimax.SARIMAX本身就支持外生变量exog参数可以引入其他预测因子。Prophet由Facebook开源对趋势、季节性和节假日效应有更灵活的处理特别适合商业时间序列对缺失值和异常值更稳健。深度学习模型如LSTM、GRU等循环神经网络能捕捉非线性依赖和长期依赖适合海量数据但需要更多的数据、计算资源和调参经验可解释性较差。选择模型时永远要遵循“奥卡姆剃刀”原则在效果相当的情况下选择更简单、更可解释的模型。SARIMA在大多数具有明显趋势和季节性的业务预测场景中依然是首选。
返回列表