资讯动态

Python时间序列预测:ARIMA模型从定阶到预测实战全解析

发布时间:2026/9/11 12:31:45 来源:尧图企业网站定制
Python做时间序列预测绕不开ARIMA模型说到ARIMA最经典的入门案例就是洗发水销售预测。今天咱们不聊虚的把原理、定阶、建模、预测、排查一路走下来代码直接贴大家复制就能跑起来。这篇东西更适合两类人看一类是做数据分析或运营手头有销售、流量这类按月或按天统计的指标想用Python把预测做出来另一类是刚接触机器学习但只做过回归、分类对时间序列有点懵想找一个最小闭环练手项目的人。用洗发水预测当例子有个好处数据量小、结构简单、趋势明显能让你把注意力放在ARIMA的建模逻辑上而不是被几十个字段和复杂预处理劝退。1. 先搞清楚ARIMA在解决什么问题1.1 时间序列预测和普通预测的核心区别很多初学者会问时间序列预测不也是训练一个模型、然后预测一个数吗和线性回归有什么区别最大的区别在于样本的独立性假设。普通回归默认样本之间是独立的比如预测房价每个房子的特征和价格之间是独立关系打乱顺序不影响结果。时间序列数据则完全不是这样——今天卖了多少瓶洗发水和昨天、前天、上个月都有关系甚至和去年同月都有关系。这种“前后依赖”关系一旦被打乱训练出来的模型基本没有意义。所以时间序列预测的核心不是找一堆特征和标签的对应关系而是把时间线上的依赖结构拆出来然后基于这种结构去外推未来。ARIMA就是做这件事的最经典、最成熟的方法之一。它能从历史数据里提取出三样东西趋势、周期性扰动、随机噪声然后用这三样去组合出未来的值。1.2 AR、I、MA三件套到底在干嘛ARIMA的全称是Autoregressive Integrated Moving Average翻译过来是“自回归积分滑动平均”。名字很拗口但拆开看就简单了AR(p) 自回归部分用过去p期的数值来预测当前值。可以理解成“惯性”——这个月销量高下个月大概率也不会太低。公式上是当前值等于常数项加上前p期数值的加权和再加上一个随机误差。I(d) 差分部分对原始序列做d阶差分让它变平稳。一阶差分就是每个月的销量减去上个月的销量相当于不看绝对水平只看“变化量”。差分的作用是把趋势脱掉比如销量一直在涨涨本身就带有规律但建模时我们更关注“新增量”的变化。MA(q) 移动平均部分用过去q期的预测误差来修正当前预测。你可以把它理解成“踩坑修正”——上个月预测误差大说明有外部冲击这个月要带上这个冲击的余波。其实ARIMA的通俗解释就是先用差分把不平稳的序列“熨平”再用自回归抓住惯性用滑动平均修正误差。三者结合起来就是ARIMA(p,d,q)p、d、q分别是三部分的阶数。1.3 为什么拿洗发水销售当案例洗发水销售数据是时间序列入门里非常经典的一个公开数据集一般包含36个月的月度销量。这个选择有几个原因一是数据量小只有36条适合用来跑通流程不会像生产环境的海量数据一样光预处理就耗掉半天。二是趋势明显早期销量低、后期销量高能非常直观地体现“不平稳”和“差分”的作用。三是业务背景容易理解销售预测本身就是时间序列应用最广的场景之一从洗发水到空调、从股票到流量本质上都是一套思路。当然我也要提前泼一盆冷水正因为样本少这个案例更适合学习不适合直接代表高精度业务预测。真实业务数据里缺失值、异常值、节日效应、渠道变化等因素都会影响模型效果那些才是真正的难点。这些在后面的常见问题部分会展开讲。2. 数据准备与平稳性检验2.1 数据长什么样怎么读进来网上流传的shampoo-sales数据集通常是一个CSV文件只有两列月份和销量。月份通常是文本格式例如“1-01”到“3-12”或者直接就是序号销量单位是“千件”。我习惯先读进来再统一转成Pandas的时间索引这样后面画图、切分、预测都方便。import pandas as pd import numpy as np import matplotlib.pyplot as plt # 读取数据假设文件放在当前目录 df pd.read_csv(shampoo-sales.csv) print(df.head()) print(df.info())读进来之后建议给列改名并把月份解析成时间序列索引df.columns [Month, Sales] # 数据集默认从2001年1月开始共36个月 df[Month] pd.date_range(start2001-01-01, periodslen(df), freqM) df.set_index(Month, inplaceTrue) plt.figure(figsize(10, 4)) plt.plot(df[Sales]) plt.title(Shampoo Sales by Month) plt.show()画完图你会看到明显的上升趋势尤其是后半段。这种带趋势的序列直接建模是不行的因为均值在随时间变化统计性质不稳定预测结果很容易失真。2.2 平稳性到底是什么意思为什么非要它平稳平稳性听起来很数学其实可以这样理解一个平稳时间序列的统计性质不随时间变化。具体表现是均值稳定、方差稳定没有明显的趋势和周期性。你拿任何一段子序列去看它的波动范围都差不多。为什么ARIMA需要平稳因为AR和MA模型本质上是在用过去的“稳定模式”外推未来。如果序列本身有上升趋势那不同时间段的数据均值就不同用早期数据推断晚期数据自然不靠谱。就像一个人跑步速度忽快忽慢你很难根据前5分钟的平均速度预测最后5分钟的位移。还有一个更实际的原因非平稳序列容易产生“伪回归”现象模型表面上看拟合得很好R方很高实际上只是同时抓住了两个上升趋势并没有真实的因果关系。2.3 用ADF检验判断不够平稳就差分判断是否平稳最常用的工具是ADF检验Augmented Dickey-Fuller Test。它的原假设是“序列存在单位根即不平稳”所以当p值小于0.05时我们有理由拒绝原假设认为序列是平稳的。from statsmodels.tsa.stattools import adfuller adf_result adfuller(df[Sales]) print(ADF统计量:, adf_result[0]) print(p值:, adf_result[1]) print(临界值:, adf_result[4])在实际运行这个案例时原始序列的p值通常会远大于0.05说明确实不平稳。接下来做一阶差分把每个月的增量提取出来df[Sales_diff] df[Sales].diff().dropna() adf_result_diff adfuller(df[Sales_diff].dropna()) print(差分后p值:, adf_result_diff[1])一阶差分后p值基本会小于0.05说明差分序列平稳了。这时候d定为1。我见过不少初学者拿到任何数据都先做一阶差分也不管原本是不是平稳这是不对的。差分是有代价的每差一次就会损失一条样本同时也会让原始水平的解释变弱。所以正确的做法是先检验不平稳再差差到1阶或2阶为止不要为了“看起来更像随机噪声”无限差分。3. 定阶p、d、q怎么确定3.1 看ACF和PACF图的直觉方法d确定之后接下来要定p和q。最传统的方法是看自相关函数ACF图和偏自相关函数PACF图。ACF自相关函数描述的是序列和它滞后k期之间的相关系数。PACF偏自相关函数描述的是剔除了中间滞后项影响后序列和滞后k期之间的偏相关系数。判断经验是特征ACF表现PACF表现适合模型AR(p)拖尾逐渐衰减在p阶后截尾突然为0AR(p)MA(q)在q阶后截尾拖尾MA(q)ARMA(p,q)拖尾拖尾ARMA(p,q)画图代码很简单from statsmodels.graphics.tsaplots import plot_acf, plot_pacf # 对差分序列画图 fig, axes plt.subplots(2, 1, figsize(10, 8)) plot_acf(df[Sales_diff].dropna(), lags12, axaxes[0]) plot_pacf(df[Sales_diff].dropna(), lags12, axaxes[1]) plt.show()不过说实话现实数据往往没有教科书那么标准。洗发水这个例子更是如此ACF和PACF的截尾特征不是特别清晰不同人看图会定出不同阶数。所以我的习惯是把图当作初步参考再用信息准则做一次自动搜索两边交叉验证。3.2 用AIC自动网格搜索省心还严谨AIC赤池信息准则是一个同时衡量拟合效果和模型复杂度的指标。它不是越大越好而是越小越好。AIC本质上会对参数多的模型施加惩罚避免我们一味追求“拟合完美”而把模型搞得越来越复杂。我常用的做法是在一个合理范围内暴力搜索所有p、q组合选出AIC最小的那个from statsmodels.tsa.arima.model import ARIMA from itertools import product import warnings warnings.filterwarnings(ignore) best_aic float(inf) best_order None best_model None # p、q都取0到4d按前面检验固定为1 for p_val, q_val in product(range(0, 5), range(0, 5)): try: model ARIMA(df[Sales], order(p_val, 1, q_val)) model_fit model.fit() if model_fit.aic best_aic: best_aic model_fit.aic best_order (p_val, 1, q_val) best_model model_fit except: continue print(最佳阶数:, best_order) print(最佳AIC:, best_aic)这段代码把p和q都限制在0到4之间已经是比较务实的范围。洗发水销售数据的最佳结果常见是ARIMA(1,1,2)或ARIMA(2,1,1)之类AIC会落在500到600之间。如果你把p、q范围放大到0到10理论上能找到AIC更小的模型但很容易过拟合预测新数据效果反而更差。3.3 定阶时的三条经验法则第一不要盲信AIC。AIC值差小于2时两个模型解释力差别不大这时应该选参数更少的模型。比如ARIMA(1,1,2)和ARIMA(2,1,2)只差一个AR参数AIC只差0.5那我会选前者。第二差分阶数d不宜过高。实际经验里d0或1就覆盖了绝大多数问题d2已经算比较极端。差分的目的是把序列变成平稳不是追求“差分到像白噪声为止”。差过分了反而会把原本可预测的信息也差掉。第三模型阶数要和业务逻辑匹配。如果数据有季节性比如洗发水销量每年都有固定旺季那ARIMA里的p、q可能不足以捕捉这种周期你需要考虑SARIMA或者在定阶时结合业务判断。这节的网格搜索只能给你一个统计上的“建议”最终判断还是要看残差检验和实际预测效果。4. 模型拟合与预测实操4.1 划分训练集和测试集顺序不能乱时间序列建模和普通机器学习最大的流程差异就在于数据划分。我们不能用随机打乱的方式切分数据集因为时间顺序本身就是信息的一部分。常见做法是拿前一段训练后一段验证预测效果。洗发水数据一共36个月我习惯拿前30个月训练、后6个月测试这样既保留足够训练样本又能检验模型在稍长周期上的表现train df[Sales].iloc[:-6] test df[Sales].iloc[-6:] print(训练集样本数:, len(train)) print(测试集样本数:, len(test))这里有个容易混淆的概念机器学习里训练集通常要覆盖更丰富的样本分布时间序列也一样训练集必须包含一个完整的业务周期比如有旺季也有淡季。如果训练集中恰好只包含上升段那模型学到的主要是趋势测试集中一旦出现波动误差会非常大。4.2 拟合ARIMA并检查残差定好阶数后直接拟合model ARIMA(train, orderbest_order) model_fit model.fit() print(model_fit.summary())summary里能看到各项系数的显著性。看回归系数旁边的P值如果P值远大于0.05说明这项对应的参数可能没必要存在可以考虑减少阶数。但这也不是绝对的有时候保留一项能让模型更稳定尤其是短期预测。拟合完必须检查残差。残差是模型没有解释掉的那部分信息如果残差还有明显模式说明模型没把序列里的结构提取干净。检查方法有两个一是看残差图理想状态下残差应该像随机噪声在0附近上下波动没有趋势或周期性。resid model_fit.resid plt.figure(figsize(10, 4)) plt.plot(resid) plt.title(Residuals) plt.show()二是做Ljung-Box白噪声检验原假设是残差序列没有自相关也就是白噪声。p值大于0.05时说明残差没有显著自相关模型通过检验。from statsmodels.stats.diagnostic import acorr_ljungbox lb_test acorr_ljungbox(resid, lags[10], return_dfTrue) print(lb_test)如果残差检验不过不要急着出预测结果回头调p、q或者考虑加季节项。4.3 预测未来并评估效果拟合完成后用训练好的模型预测测试集对应的6个月forecast model_fit.forecast(stepslen(test)) # 转成Series方便对比 forecast_series pd.Series(forecast, indextest.index) print(forecast_series)然后和真实值对比计算RMSE和MAEfrom sklearn.metrics import mean_squared_error, mean_absolute_error rmse np.sqrt(mean_squared_error(test, forecast_series)) mae mean_absolute_error(test, forecast_series) print(RMSE:, rmse) print(MAE:, mae)RMSE的单位和销量一致洗发水销量本身在100到500之间RMSE落在几十左右都算可接受。但更重要的是看相对误差如果RMSE动辄几百说明预测完全不能用。画图对比预测值和真实值plt.figure(figsize(10, 5)) plt.plot(train.index, train, labelTrain) plt.plot(test.index, test, labelTest) plt.plot(forecast_series.index, forecast_series, labelForecast) plt.legend() plt.title(ARIMA Forecast vs Actual) plt.show()最后可以用全部36个月数据重新训练模型预测未来12个月同时拿到置信区间final_model ARIMA(df[Sales], orderbest_order).fit() future_forecast final_model.get_forecast(steps12) future_mean future_forecast.predicted_mean future_ci future_forecast.conf_int() print(future_mean)get_forecast相比forecast的好处是能直接给出置信区间方便在报告中画出上下界让业务方知道预测的波动范围在哪里。5. 实际使用中的几个坑5.1 数据不平稳一阶差分后还是不平稳怎么办这类问题在小样本数据里很常见尤其当序列存在单调增长又突然变缓时单次差分可能不够。解决办法先试二阶差分再检验一次。如果二阶差分后平稳d2也是可以的。如果二阶差分还是不理想考虑对原数据做对数变换或者Box-Cox变换。对数变换能压缩高值部分的波动让方差更稳定特别是销量这种可能指数增长的数据。做法是import numpy as np df[LogSales] np.log(df[Sales]) # 再对LogSales做ADF检验和差分变换之后模型预测得到的是对数值最终需要exp还原但还原后要记得还原的是中位数意义上的预测和原始量纲会有一点偏差。5.2 预测结果画出来像一条水平直线是不是模型坏了不是模型坏了这是ARIMA在小样本、无强季节性数据下的常见表现。如果你拟合的是一个接近随机游走的模型比如p、q很小AR系数接近1那么多步预测的期望值会快速收敛到某个均值画出来就像一条横线。这背后其实是无条件预测的数学性质在只有过去信息、没有未来外部变量的情况下长期预测会回归到序列的均衡水平。短期预测通常还有变化拉到12个月以上就平了。要改善这种情况要么缩短预测步长只预测3到6个月要么在拟合时加入趋势项例如model ARIMA(train, orderbest_order, trendc)trendc表示允许模型包含一个常数漂移项能帮助保持一定趋势。如果数据有明显季节性还得换SARIMA而不是死磕ARIMA。5.3 残差检验发现还有自相关怎么排查残差不是白噪声说明模型没有把序列中所有的结构都吸干净。这时候按顺序做三件事第一检查使用的p、q阶数是不是太小。网格搜索得到AIC最小的结果有时会偏向简洁但残差检验会暴露问题。直接增加p或者q再看AIC和残差找到一个两者平衡的点。第二确认是否遗漏了季节性。月度销售数据往往有年度周期ARIMA只能处理非季节性部分。如果ACF在滞后12、24处明显突出说明有季节性需要试试SARIMA模型例如SARIMA(1,1,1)(1,1,1,12)。第三考虑增加外生变量。比如洗发水销售可能受促销活动、节假日、同类竞品上市影响如果这些变量有记录可以改用ARIMAX或者SARIMAX把这些外部因素作为回归项放进去。5.4 自动定阶很慢或者跑出来一个离谱的模型网格搜索本身很快但如果p、q范围设太大比如各取0到20组合数有400个每个都拟合一遍哪怕数据只有36条也要等一阵子。更重要的是范围太大容易选到高维模型导致预测时数值不稳定置信区间特别宽。我的经验是p和q都只搜索0到4最多0到7d固定为1或2。如果搜索出来的最佳模型参数非常大比如ARIMA(7,1,7)先怀疑是数据量不够或者存在异常值不是模型越复杂越能预测准。另外如果在真实业务中遇到这种小样本销售预测还可以试试用滚动预测walk-forward validation来评估。也就是每次只用截止到t期的数据预测t1期然后逐步推进这样更贴近业务端的实际使用方式而不是一次性预测6个月然后干等。ARIMA的模型更新成本很低非常适合这种滚动预测方式。最后说一点个人体会。ARIMA不是预测模型的终点但对于样本量有限、单变量、有趋势和少量波动的数据它永远是快速上场、容易解释的首选。我每次做这类预测都会坚持三步先画序列图看趋势再做ADF检验定差分拟合后一定看残差和实际预测效果而不是只看AIC。洗发水这个例子最大的价值不是让你记住某个特定阶数而是让你把“平稳性检验—定阶—拟合—残差分析—预测评估”这一整套时序建模框架跑通。后面遇到真实业务数据不管是转战SARIMA、Prophet还是LSTM底层思路都是一样的预测的前提是先理解时间线的结构而不是盲目套模型。希望对正在入门时间序列的你有所帮助。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价