☰
MATLAB零售数据ARIMA实战:从差分定阶到季节性预测
2026/10/2 9:21:55 网站建设 项目流程

直接上代码。先说结论:MATLAB自带的那份零售销售数据,确实是拿来折腾ARIMA模型的好料子。它本身带着明显的月度周期,数据粒度统一、时间跨度稳定、还带一点趋势起伏,做季节性销量预测正好能把ARIMA从理论拉到实战。这篇文章我会老老实实把整个过程拆开讲一遍,从数据长什么样、为什么要做季节差分,到阶数怎么定、代码怎么写、预测结果怎么判,最后再聊几个我实际踩过的坑。适合刚入门时间序列、手头有MATLAB但不知道从哪下手的同学,也适合想搞明白ARIMA参数背后逻辑的兄弟。

1. 数据与场景:为什么这份零售数据适合练ARIMA

1.1 数据集长什么样,怎么加载

MATLAB的Econometrics Toolbox示例数据里带着一份零售销售数据,我这边用到的加载方式是这样:

% 加载MATLAB自带的零售销售数据集 load Data_RetailSales whos

如果用的是较新版本,加载后工作区会出现一个时间表或者包含销售数值和日期的结构体。大致字段就是时间戳和销售额。数据是按月统计的零售额,时间跨度从某年1月一直排到某年12月,中间没有断档,这种连续月度数据对建模来说特别省心。

我习惯先把字段名捋清楚,然后转成统一的序列变量:

% 统一提取销售序列 retail = Data_RetailSales; % 具体字段名以工作区为准 sales = retail.RetailSales; % 如果字段叫别的,改成对应字段 dates = retail.Date; % 日期序列

有时候加载出来是double矩阵,有时候是timetable,无所谓,关键是拿到整齐的等间隔月度序列。等间隔这件事在时间序列建模里非常重要,ARIMA模型默认把你喂进来的数据当成连续时间点处理,一旦中间有空缺,模型估计很容易出幺蛾子。

1.2 先画图,用眼睛判断趋势和周期

加载完数据我从来不急着算指标,第一件事永远是画图。这是最直观的一步,很多问题在图上扫一眼就能看出来。

figure; plot(dates, sales, 'LineWidth', 1.2); title('零售销售月度数据'); xlabel('时间'); ylabel('销售额'); grid on;

这张图通常能看到两个信号:一个是整体走势在缓慢上行,说明序列非平稳,存在趋势成分;另一个是每年同一个位置会出现明显的波峰波谷,说明有稳定的月度季节性。拿这份数据举例,年底附近通常冲高,年初回落,每年重复这个节奏。这种规律性非常强的序列,恰好是ARIMA家族最能发挥的场景。

1.3 为什么要用ARIMA而不是其他模型

很多人会问,数据都带周期了,是不是用季节分解或者简单回归就行?ARIMA强的地方在于它不把趋势和季节当成需要手动剥离的干扰项,而是把它们建模进随机过程里。靠差分把非平稳成分消掉,靠自回归和移动平均项把残留的时间依赖关系刻画出来,本质上是“让数据自己解释自己”。

再直白一点,线性回归需要你手工构造季节哑变量、月份特征、滞后项,ARIMA把这些东西通过p、d、q和季节项P、D、Q统一封装了。你不用提前告诉模型“1月通常低,12月通常高”,它自己能从历史序列里学出来。

2. 平稳性判断与差分处理:建模型前必须过的关

2.1 什么是平稳性,为什么ARIMA死磕这个

ARIMA里的I,也就是Integrated,指的就是差分。差分的目的就是为了让序列变平稳。那什么叫平稳?简单理解,就是序列的统计性质不随时间变化——均值大致稳定,方差不剧烈波动,自相关结构只跟间隔有关。为什么ARIMA要求平稳?因为AR和MA部分本质上是回归模型,如果序列均值一直在漂,你等于拿一个常数系数去拟一条一直在变化的曲线,拟合结果自然站不住脚。

判断平稳性最常用的就是单位根检验。MATLAB里直接有adftest:

[h, pValue] = adftest(sales); disp(pValue);

h = 1意味着拒绝“存在单位根”的原假设,也就是序列平稳;h = 0代表不平稳,需要差分。实际操作中,这份原始零售数据大概率是不平稳的,因为趋势太明显了。

2.2 差分阶数和季节差分怎么选

先做普通差分:

d1 = diff(sales); % 一阶差分 figure; plot(d1);

一阶差分通常能把趋势抹平。但这个零售数据还有季节性,一阶差分之后你会发现周期性依然存在,只是幅度变了。这时候需要做季节差分,间隔是12个月:

ds = diff(d1, 12); % 在一阶差分基础上再做12步季节差分 figure; plot(ds);

季节差分做完,序列基本就变得“平静”了。这是不是说明d=1, D=1就是最佳选择?不一定。经验上看这个数据集很可能是这个配置,但更严谨的做法是不断尝试组合,用后面提到的信息准则来定。

建模过程中我自己有个心得:能少差分就别多差分。差分虽然能平稳化,但也会丢信息,差分过度会导致预测方差变大、置信区间宽到没法看。所以在做差分前,最好同时看一眼adftest的 p 值和差分后序列的样子,不要机械执行“不平稳就再差分”的流程。

3. 模型定阶:从ACF/PACF到网格搜索

3.1 看自相关图,建立定阶直觉

定阶是ARIMA最让人头疼的环节。第一步是看图说话:

figure; subplot(2,1,1); autocorr(ds); title('差分序列的自相关图 ACF'); subplot(2,1,2); parcorr(ds); title('差分序列的偏自相关图 PACF');

ACF看的是序列跟它自己滞后k步之间的相关程度,PACF是剔除了中间滞后影响后的偏相关。经验法则很简单:

  • ACF拖尾、PACF截尾,考虑AR模型,p取PACF显著截尾处的滞后阶数
  • ACF截尾、PACF拖尾,考虑MA模型,q取ACF显著截尾处的滞后阶数
  • 两者都拖尾,ARMA模型,阶数需要另想办法

但说实话,实际画出来的图很少这么干净。尤其带季节性的序列,ACF会在12、24、36这些位置出现明显尖峰,这是季节项的信号。所以这份零售数据只做普通ARIMA不行,必须上SARIMA,也就是带(P,D,Q)_12的季节ARIMA。

3.2 用信息准则做网格搜索

看图只能缩小范围,真正定阶我更喜欢用网格搜索配合AIC。AIC即赤池信息准则,它在拟合优度和模型复杂度之间取平衡,数值越小代表模型越“划算”。MATLAB里可以循环训练多个候选模型,挨个比较:

% 候选阶数范围 pList = 0:3; dList = 0:1; qList = 0:3; PList = 0:2; DList = 0:1; QList = 0:2; S = 12; bestAIC = inf; bestModel = []; results = []; for p = pList for d = dList for q = qList for P = PList for D = DList for Q = QList Mdl = arima(p,d,q); Mdl.Seasonality = S; Mdl.ARLags = 1:p; % 如果p=0,会自动忽略 Mdl.MALags = 1:q; Mdl.SARLags = S*(1:P); % 季节自回归滞后 Mdl.SMALags = S*(1:Q); % 季节移动平均滞后 try [EstMdl, ~, logL] = estimate(Mdl, sales, 'Display', 'off'); [~, bic] = aicbic(logL, numParams(Mdl), length(sales)); if bic < bestAIC bestAIC = bic; bestModel = EstMdl; end catch continue; end end end end end end end

注意这个循环可能跑比较久,因为组合数量不少。如果嫌慢,可以先缩小候选范围,或者把差分阶数固定成你已经验证过的d=1, D=1,只去搜索p、q、P、Q。我一般先粗搜一轮,确定大致区域后,再在最优阶数邻近的几个值之间细搜。

3.3 建模时的参数代表什么

有些教材喜欢直接写模型公式,我换个方式解释:

  • p:现在这个月的销量受之前几个月销量影响的程度。12月卖得好,1月是不是也会连带着好一点?p管的就是这种“自己影响自己”的关系
  • q:现在这个月的“意外波动”。比如某月搞促销突然冲高,这个冲击会不会延续到下个月?q管的就是这种随机冲击的残留
  • D和Q:作用同上,只不过是针对“今年12月影响去年同期12月”这种跨年关系

理解了这些,你就明白为什么零售数据一定要有季节项了。因为年末大促、周期备货这些效应每年都在,你不给模型季节通道,它就只能靠非季节项硬学,效果会差很多。

4. 完整代码:从数据到预测的一段式流程

4.1 直接可以跑的完整脚本

这里给出一份可以直接复制运行的完整代码。这个版本我会把数据处理、模型训练、预测和画图全串起来:

%% 1. 加载数据 load Data_RetailSales; sales = Data_RetailSales.RetailSales; % 具体字段名按工作区实际调整 dates = Data_RetailSales.Date; %% 2. 快速可视化 figure; plot(dates, sales, 'LineWidth', 1.2); title('零售销售原始序列'); grid on; %% 3. 平稳性检验 [h, pValue] = adftest(sales); fprintf('ADF检验p值: %.4f\n', pValue); %% 4. 模型定义 % 本例采用经过网格搜索确认的阶数 p = 1; d = 1; q = 1; P = 1; D = 1; Q = 1; S = 12; Mdl = arima(p,d,q); Mdl.Seasonality = S; Mdl.ARLags = 1:p; Mdl.MALags = 1:q; Mdl.SARLags = S*(1:P); Mdl.SMALags = S*(1:Q); %% 5. 模型估计 [EstMdl, EstParamCov, logL] = estimate(Mdl, sales, 'Display', 'params'); %% 6. 残差诊断 res = infer(EstMdl, sales); figure; subplot(2,1,1); autocorr(res); title('残差ACF'); subplot(2,1,2); parcorr(res); title('残差PACF'); %% 7. 未来12个月预测 [forecastY, forecastMSE] = forecast(EstMdl, 12, sales); forecastCI = forecastY + 1.96 * sqrt(forecastMSE) * [-1 1]; %% 8. 画预测结果 figure; h1 = plot(dates, sales, 'LineWidth', 1.2); hold on; futureDates = dates(end) + calmonths(1:12); h2 = plot(futureDates, forecastY, 'r-', 'LineWidth', 1.5); h3 = plot(futureDates, forecastCI(:,1), 'r--', 'LineWidth', 0.8); plot(futureDates, forecastCI(:,2), 'r--', 'LineWidth', 0.8); legend([h1 h2 h3], {'历史数据', '预测值', '95%置信区间'}, 'Location', 'northwest'); title('零售销售未来12个月预测'); xlabel('时间'); ylabel('销售额'); grid on;

4.2 脚本里几个关键步骤的意图

infer函数用来提取模型残差。这一点很多人容易跳过去,直接拿预测结果说事。实际上残差诊断是判断模型是否把信息提取干净的关键。如果残差序列里还能看出明显的自相关,说明模型阶数不够,信息还留在里面没被解释干净。这时盲目预测,结果看着再漂亮也是碰运气。

forecast的第二个输入 12 表示预测未来12个点,也就是未来12个月。第三个输入是历史观测值,模型会拿它作为起点往后递推。注意forecast不是重新训练模型,它用的是你已经估计好的EstMdl,只是把过去的路径延伸到未来。

置信区间我直接用1.96 * sqrt(forecastMSE)构造。这里隐含假设残差正态分布,实际数据不一定满足,但作为快速评估够用了。严格的预测区间应该用模拟法构造,后面我可以补一段,但这对于题主这种快速实战场景不是必须。

4.3 模型类型在代码里的体现

很多刚接触的人搞不明白arima(p,d,q)后面的Seasonality、SARLags、SMALags是什么意思。这里有个关键点:MATLAB的arima对象,当你设定Seasonality = 12后,模型就等于自动变成 SARIMA 形式。此时:

  • ARLags指定非季节AR项的滞后阶数,比如1:p
  • SARLags指定季节AR项的滞后位置,比如12*(1:P)
  • MALags和SMALags对应非季节和季节MA项

为什么SARLags要乘12?因为季节效应发生在相同的月份位置,12月影响12月,24月影响24月。你不指定SARLags的话,MATLAB默认把季节项当成12阶MA处理,那模型含义就完全不同了。

5. 预测效果评估:模型好不好,不能只靠眼睛

5.1 用滚动预测模拟真实场景

预测这种事,光看训练集拟合得好坏没意义。真正的考验是拿历史数据做“回头测”:把前80%的数据当训练集,后20%当测试集,用训练好的模型去预测测试集那一段,再跟真实值对比。

% 划分训练集和测试集 trainLen = floor(length(sales) * 0.8); trainData = sales(1:trainLen); testData = sales(trainLen+1:end); % 在训练集上估计模型 Mdl2 = arima(1,1,1); Mdl2.Seasonality = 12; Mdl2.ARLags = 1; Mdl2.MALags = 1; Mdl2.SARLags = 12; Mdl2.SMALags = 12; [EstMdl2, ~, ~] = estimate(Mdl2, trainData, 'Display', 'off'); % 预测整个测试集长度 [testForecast, testMSE] = forecast(EstMdl2, length(testData), trainData); % 计算误差 rmse = sqrt(mean((testData - testForecast).^2)); mae = mean(abs(testData - testForecast)); fprintf('RMSE: %.3f, MAE: %.3f\n', rmse, mae);

5.2 评估指标怎么解读

RMSE和MAE一个惩罚大误差,一个反映平均绝对误差水平。但在销量场景里,我更建议同时看相对误差MAPE:

mape = mean(abs((testData - testForecast) ./ testData)) * 100;

如果MAPE在10%以内,对于零售销量这种噪声不小的数据,算很不错了。要是飙到20%以上,就该回头查阶数、检查是否有异常月份,或者看看要不要加外生变量。

我自己回头看这份零售数据的滚动预测时,一个比较明显的规律是:预测前3个月误差可控,越往后误差越大。这很正常,ARIMA本质上是靠历史惯性外推,时间越远,不确定性越大。所以做业务预测时,我会建议别一口气预测太久,拆成“每季度滚动重训一次”的方式更实用。

5.3 模型对比,增加说服力

做项目汇报时,单说“我建了个ARIMA模型”说服力不够,最好摆几个对比。最简单的做法是拿一个基准模型跟SARIMA比。比如用“上一年的同月值”作为朴素预测:

% 朴素季节基准:用去年同月数据 naiveForecast = sales(trainLen+1-12 : trainLen-12); % 不太严谨,这里示意 % 正确写法应该按月份对齐

这种基准模型简单到不行,但如果ARIMA连它都比不过,说明模型白搭了。实际对比中ARIMA通常能赢,因为它不仅用了去年同期信息,还综合了临近几个月的动态变化,比单纯的“去年同月”灵活得多。

做这种对比还有个好处:能让你更冷静看待模型效果。ARIMA不是魔法,它只是把“季节性、趋势、滞后影响”这几件事用一套框架整合起来了,效果优劣最终要靠数据说话。

6. 调参细节与常见坑,我替你踩过了

6.1 关于MATLAB版本和工具箱的提醒

做ARIMA必须要有Econometrics Toolbox,有些旧版本或者基础版MATLAB没装的话,运行arima会直接报错“未定义函数或变量”。遇到这种情况,先确认工具箱是否可用,再考虑换数据或换实现方式。另外,不同版本的arima对象字段名有些细微差别,老版本可能是ARLags需要额外的set方法设置,新版本直接在构造后点号赋值就行。脚本跑不起来的时候,先检查版本差异,不一定是代码逻辑问题。

6.2 模型不收敛,先别急着加阶数

estimate有时候会报“无法计算标准误差”,或者迭代半天不收敛。常见原因有三个:

  • 阶数太高导致参数冗余,尤其p和q同时使劲调大的时候,模型参数之间高度相关,优化器找不到干净的极值点
  • 数据里有极端值。比如某个月销量突然暴跌暴涨,会严重干扰最大似然估计
  • 优化初值不合适,MATLAB有时候给的初值离最优解太远,导致数值震荡

我的处理顺序是:先检查有没有异常月份;其次简化模型,从(1,1,1)(1,1,1)_12开始,确认基线效果后再慢慢加复杂度;最后再考虑调整估计选项里的优化算法和容差。

6.3 预测值明显偏低或“糊成一条直线”

这个现象挺常见,尤其是对带强季节性的数据。原因多半是季节项没有正确设定,或者D选得不对。如果只做普通差分没有季节差分,模型会把季节性当成某种“平均噪声”,预测出来自然趋平。遇到“预测直线”,优先检查是不是忘了设Seasonality=12,以及有没有在网格搜索里包含D和季节项的组合。

6.4 残差诊断不过关怎么办

残差ACF图里如果12、24这些位置还有显著尖峰,说明季节信息没提取干净。两个方向:一是提高季节项阶数,把P或Q从1调到2;二是重新检查数据,有时候节假日效应在不同年份发生的时间不完全对齐,会导致季节模式不稳定,此时可以尝试加节假日哑变量。这份零售数据相对规整,调大季节阶数通常能解决问题,但不要盲目加高,否则容易过拟合。

6.5 常见问题速查表

现象可能原因处理方式
报错未定义arima缺少Econometrics Toolbox检查工具箱并重新安装
估计不收敛阶数过高或数据异常减阶数,检查异常值,调整优化选项
预测结果接近直线季节项未设置或D不对设置Seasonality,加入季节差分
残差ACF仍有显著尖峰季节效应没提取干净调大P或Q,加入哑变量
MAPE过高模型定阶不合适网格搜索重新定阶,尝试加入外生变量
预测置信区间过宽差分过度减少差分阶数,检查d是否过大

7. 一个我常用的收尾技巧:多步预测拆成滚动重训

最后分享一个我实际项目里常用的做法。刚才提到过滚动回头测,其实生产环境的预测也应该做成滚动式。不要训练一次模型就反复预测一年,而是每个月结束后,把新数据塞进样本,重新估计一遍模型参数。ARIMA模型参数更新成本低,重训一次也就几秒钟,完全没必要一劳永逸。

% 滚动预测示意:每来一个月的新数据就重训一次 for t = trainLen+1 : length(sales) currentData = sales(1:t-1); [~, ~] = estimate(Mdl2, currentData, 'Display', 'off'); [nextForecast, ~] = forecast(Mdl2, 1, currentData); predStore(t - trainLen) = nextForecast; end

这样做的逻辑很简单:模型学到的“最近规律”永远是最新的,促销活动、消费习惯变化都会被及时吸收。如果你做的是连续多个月的预测,这个方法比一次性预测到底稳得多,误差至少能降一个档次。

我个人折腾这套数据下来,最大的体会是:ARIMA不难,难的是你得对每个步骤有感觉——为什么差分、为什么设季节项、为什么定这个阶数。有了这层理解,配合网格搜索和滚动验证,就算数据换成别的行业,你也能很快搭出一套靠谱的预测流程。这份零售数据只是起点,同样的套路换个电商、餐饮、库存场景,照样能用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询