今天咱们聊聊用MATLAB玩时间序列预测的野路子。别被AR、ARIMA那些缩写吓到——说白了这就是一个拿历史数据找规律、猜未来的游戏。这篇文章没有高深理论的堆砌,只有能直接跑起来的代码和我在实际项目里踩过的坑。不管你是刚接触MATLAB的新手,还是已经在用Python调包的老手,只要你想快速上手时间序列预测,这篇都能让你少走弯路。
我平时做数据分析,手边最顺手的工具就是MATLAB,时间序列这块摸了挺长时间,发现网上教程要么太理论,要么直接甩一堆工具箱函数不给解释。今天这篇我打算换个节奏:不整复杂的数学推导,就用“先跑通代码、再讲为什么”的方式,把AR和ARIMA模型从定阶、拟合到预测完整走一遍。中间会插一些货真价实的报错调试记录,都是我在命令行里被折磨出来的经验。
1. 时间序列预测到底是干嘛的
1.1 说白了就是找规律猜未来
时间序列预测这个词听起来很高端,但你把它拆开看就特别朴素。给你一组按时间排列的数据点,比如过去365天的气温、过去100天的股价、过去24个月的销量,然后让你根据这些历史值去推断明天的气温、后天的股价、下个月的销量,这就是时间序列预测。
它和普通的回归预测最大的区别在于:普通回归看的是“X怎么影响Y”,时间序列看的是“Y自己的过去怎么影响Y的现在和未来”。所以你会听到一个词叫“自回归”,英文叫Autoregressive,缩写就是AR。所谓自回归,可以理解成“昨天的我”和“今天的我”之间的关系,它是用自己过去的数据来回归自己现在的结果。
很多初学者一上来就被“平稳性”“单位根”“协整”这些词劝退,觉得这玩意儿非得是统计学博士才能碰。实际上你完全可以把这些概念先放到一边,拿一份数据试着跑一遍,看着预测曲线出来之后,你自然就明白这些概念是干嘛用的了。这也是我这篇文章想带大家做的事:先跑起来,边跑边理解。
1.2 AR和ARIMA到底在说什么
AR模型的核心逻辑很直接:你想预测今天的数据,那你就看看昨天、前天、甚至前好几天的数据分别对今天有多大影响,然后把它们加权求和。
写成数学符号大概是这样的:
y(t) = c + φ1 * y(t-1) + φ2 * y(t-2) + ... + φp * y(t-p) + ε(t)这里的p是你回头看多少天的数据,φ1到φp就是那些“影响权重”。如果只看昨天,就是AR(1);看昨天和前天,就是AR(2);看p天之前的数据,就是AR(p)。
ARIMA模型在AR的基础上多做了两件事。第一个是“差分”,也就是用今天的值减昨天的值,把本来不稳定的数据变得稳定一点。第二个是引入“MA”部分,拿过去的预测误差来修正当前预测。它的完整名字是Autoregressive Integrated Moving Average,即自回归积分滑动平均模型。你只要知道它是AR模型的升级版,专门处理那些有明显趋势、不太“听话”的数据就行。
1.3 为什么我一直用MATLAB干这个
我知道现在一提时间序列预测,好多人第一反应是Python的statsmodels或者Prophet,甚至直接上LSTM。但就我自己这几年的体感来说,MATLAB做时间序列预测有几个无法拒绝的优势。
首先是矩阵运算和画图一体化,数据导入之后,plot一下、autocorr一下、parcorr一下,全在一个窗口里就能完成,不像Python需要来回切换各种库,还要处理中文字体问题。其次是Econometrics Toolbox里现成的时间序列函数非常完整,arima、estimate、forecast、adftest、aicbic这些函数封装得都很好,对于一个不想抠底层原理、只求快速验证想法的场景来说,效率高到离谱。
另外,MATLAB对新手特别友好的一点是变量实时可见。命令窗口敲一个命令,工作区立刻显示结果,数据长什么样、模型估计出了几个参数,一清二楚。这种直观反馈对建立“模型直觉”非常有帮助。
2. 准备数据和环境,先别急着写模型
2.1 检查工具箱,踏实的开始
第一个建议:开始之前先确认你装的MATLAB里有Econometrics Toolbox,也就是计量经济学工具箱。因为这个工具箱里有arima、estimate、forecast、adftest这些时间序列预测的核心函数。缺了这个工具箱,下面的代码基本跑不动。
怎么检查?打开MATLAB,命令行里敲一行:
ver('econ')如果返回一段版本信息,说明工具箱已经装好了。如果提示未找到,或者显示“无法使用此功能”,那你就得先装或者激活这个工具箱。很多学校或公司的正版授权是包含全部工具箱的,只是安装的时候没有勾选,可以去MATLAB的“附加功能”里补装一下。
还有一点要提醒的是,不同版本的MATLAB对arima函数的行为有一点点差异,尤其是显示方式和默认设置。我自己用的是R2021a,下面演示的代码放在R2019a到R2023a之间应该都能跑,新版R2026b也保留了这些核心函数,不用担心兼容性。
2.2 把数据倒进MATLAB
数据导入这件事看着简单,但80%的新手报错都出在这一步。MATLAB要求时间序列数据是一个列向量,很多人从Excel复制出来的是行向量,直接塞给模型就报“Input data must be a column vector”。
我一般习惯用readtable函数读入数据,它兼容xlsx、csv、txt等常见格式。假设你有一个销量数据文件sales.xlsx,里面有日期和销量两列:
data = readtable('sales.xlsx'); y = data.Sales; % 取出销量列 y = y(~isnan(y)); % 去掉空值 y = y(:); % 强制变成列向量强制转成列向量这个y = y(:)是野路子里的野路子,别嫌啰嗦,这行能省掉后面一大堆烦人的维度报错。如果你要预测的目标列有缺失值,要么用前面的值填充,要么直接删除那几行,千万别留着NaN继续跑,很多模型函数碰到NaN会直接罢工。
如果数据量比较大,比如几万条,你也可以直接用load命令读取mat格式的数据文件,速度更快。总之记住一句话:进模型的y必须是一列、没有缺口的数值。
2.3 画图说话,看数据长什么样
拿到数据的第一步千万不要急着建模。先画图,用眼睛看。
plot(y) xlabel('时间') ylabel('数值') title('原始数据') grid on这一步能告诉你很多信息:数据有没有整体上升或下降的趋势?有没有明显的周期性波动?波动幅度是均匀的还是越往后越大?噪声大不大?
举个例子,如果数据呈现一条明显的上升直线,那说明均值在随时间变化,这种序列拿AR模型直接拟合效果通常不怎么样,因为AR模型假设数据围绕一个稳定水平波动。如果看到类似正弦波那样的重复形态,说明存在季节性或周期性成分,建模时要考虑差分或者周期长度。如果数据看起来乱糟糟的,没啥明显规律,也别灰心,可能只是你还没找到合适的滞后阶数。
每次做项目我都要反复强调这一步:先画图,再建模。图一旦看起来不对劲,模型大概率也会不对劲,这不是玄学,是经验。
2.4 平稳性:AR模型的老家在哪
现在到了很多人最头疼的概念——平稳性。我用一个特别生活化的说法来解释:平稳序列就是那些围绕一个固定水平上下波动,均值不飘、方差也不飘的数据。比如某城市每天的温度在全年看来是季节波动的,但如果我们只看特定季节的一段,它就是围绕某个温度水平波动的。
为什么要关注平稳性?因为AR模型的基本假设就是“规律是稳定的”,昨天和今天的关系跟大前天和前天之间的关系应该是同一种规律。如果数据的均值一直在跑,模型的系数就很难估计准,预测也就无从谈起。
有一个功能可以帮你量化判断序列是否平稳,叫ADF检验,全称是Augmented Dickey-Fuller检验。用法非常简单:
adftest(y)这条命令返回1表示数据平稳,返回0表示不平稳。返回0的时候也别慌,这正是后面要引入差分的理由。我在实践中最常见的情况是:原始销量数据ADF检验返回0,把数据做一阶差分之后再检验,通常就变成1了。
3. 先跑通AR模型:定阶、拟合、预测一次到位
3.1 用ACF/PACF图给AR模型定阶
AR模型里那个p到底取几?这是新手问得最多的问题。有一种土办法特别直观:看自相关图(ACF)和偏自相关图(PACF)。
ACF看的是当前值和过去各个滞后值之间的“总相关”,PACF看的是剔除中间变量影响后的“净相关”。对于AR(p)模型,图形上有个经典特征:PACF图会在p阶之后突然截断,掉进置信区间;而ACF图会拖拖拉拉地衰减下去,像一条慢慢变淡的尾巴。
在MATLAB里画这两个图非常方便:
figure; subplot(2,1,1); autocorr(y); title('ACF'); subplot(2,1,2); parcorr(y); title('PACF');画完之后你看PACF图,找最后一个明显超出蓝色置信区间的柱子位置,那个位置基本就是你想要的p。比如PACF在第2个柱子明显冒头、后面都缩回去了,那可以考虑AR(2)。当然这个判断有一定主观性,万一图比较模糊,还有更偷懒的办法,后面会讲AIC比较。
3.2 关键代码:用arima函数拟合AR(2)
这里我用一组模拟数据来演示。我生成了一个AR(2)过程的数据,意思是从第3个数据点开始,当前值等于0.5乘以前面第1个值加上0.3乘以前面第2个值,再加上随机噪声。给它加了一个10的均值水平,模拟现实中围绕稳定水平波动的场景。
数据分两部分,前250个作为训练集,后50个作为测试集,用来检验预测效果:
rng(42); T = 300; e = 1.2 * randn(T,1); y = zeros(T,1); for t = 3:T y(t) = 0.5*y(t-1) + 0.3*y(t-2) + e(t); end y = 10 + y; train = y(1:250); test = y(251:300);运行完这段之后,你可以先画一下PACF,理论上你会看到第2阶截断,这正是AR(2)的表现。接下来就用工具箱的arima函数定义模型。注意这个函数虽然名字叫arima,但你把后面两个参数设为0,它就是一个纯AR模型:
mdl = arima(2, 0, 0); % AR(2)模型 mdl = estimate(mdl, train, 'Display', 'params');estimate会自动用最大似然估计法把系数算出来。运行之后会打印出常数项、AR系数和方差等估计结果。你会看到估计出来的AR1系数大约在0.5附近,AR2系数大约在0.3附近,跟生成数据的真实系数非常接近。这就是模型估计给你“找规律”的过程,看起来确实就是这么一回事。
如果想看模型内部的具体信息,直接命令行输入:
mdl它会告诉你模型结构、系数值、标准误差等。一般来说标准误差越小,说明这个估计值越可靠。
3.3 手写最小二乘估AR参数,彻底搞懂原理
工具箱函数好用,但如果你对AR模型的原理还停留在“会用但不懂”的状态,我强烈建议你手写一次最小二乘估计。这个过程只需五行代码,但对理解模型的帮助极大。
AR(2)模型的意思是:
y(t) = c + φ1 * y(t-1) + φ2 * y(t-2) + ε(t)如果忽略常数项,这其实就是一个线性回归:把y(t-1)和y(t-2)当自变量,把y(t)当因变量。所以可以直接用MATLAB左除符号\来算回归系数:
n = length(train); X = [train(2:n-1), train(1:n-2)]; % 两列滞后变量 yfit = train(3:n); % 当前值 beta = X \ yfit;这里X的第一列是y(t-1),对应第2到第n-1个观测;第二列是y(t-2),对应第1到第n-2个观测;yfit对应第3到第n个观测。如果你想把常数项也算进去,就在X前面加一列全1:
X = [ones(n-2,1), train(2:n-1), train(1:n-2)];算出来的beta里第一个是常数项,后面两个是AR系数。拿这个结果跟estimate的估计值对比一下,你会发现它们非常接近。这个过程做完,你对“自回归”三个字会有一个完全不一样的体感。
做完了估计,还可以手写一个简单的多步预测循环:
pred = zeros(10,1); last2 = train(end-1:end); % 取最后两个观测值 for k = 1:10 pred(k) = beta(1) + beta(2)*last2(2) + beta(3)*last2(1); last2 = [last2(2); pred(k)]; end每预测一步,就把新预测值放进历史窗口,然后继续预测下一步。这就是最原始的自回归预测方式。用过一次这个循环,你就明白那些高大上的forecast函数内部其实也是这么回事。
3.4 用forecast做预测并画置信区间
工具箱自带的forecast函数比手写循环更方便,输出也更多。比如我们要基于训练集预测后面50个值:
[Yhat, YMSE] = forecast(mdl, 50, 'Y0', train);输出Yhat是预测值,YMSE是每一步的预测方差。有了方差就可以画出95%的置信区间,这对评估预测不确定性非常有用:
upper = Yhat + 1.96 * sqrt(YMSE); lower = Yhat - 1.96 * sqrt(YMSE); figure; plot(test, 'k', 'LineWidth', 1.5); hold on; plot(Yhat, 'r--', 'LineWidth', 1.5); plot(upper, 'b:', 'LineWidth', 1); plot(lower, 'b:', 'LineWidth', 1); legend('真实值', '预测值', '95%置信区间');窄的置信区间说明模型比较自信,宽的置信区间说明模型自己心里也没底。预测步数越长,置信区间通常就越宽,这很正常,预测未来这件事本来就是越远越不确定。
4. 数据不平稳就换ARIMA:差分和拟合一次说清
4.1 先做差分,把数据拉回平稳
现实中你拿到的数据很多都不是平稳的,比如销量逐月增长、股价长期上行,均值一直在变。这时候AR模型直接拟合,效果往往很差。先做一个“差分”操作,也就是计算每个点的变化量而不是原始数值。
dy = diff(y);这里的y是原始数据,dy就是差分后的序列。注意diff算完,数据长度会少1。做一阶差分之后,如果序列已经围绕某个水平上下波动,那就可以在这个差分数据上建AR模型。
也可以继续做二阶差分,比如一阶差分之后发现还有趋势,那就再diff一次。不过在实际项目中,绝大多数数据一阶差分就够用了,差分次数太多会把原本的信息也差掉,导致模型过度处理。
判断差分之后是否变平稳,还是那句话:
adftest(dy)返回1说明差分后的序列已经平稳,可以进入建模阶段。这个流程我每天都在用,基本就是“不平稳就差分,差分完再检验,还不行就再差分”,非常机械,但非常有效。
4.2 ARIMA(p,d,q)完整拟合流程
到了这一步,ARIMA模型就可以登场了。ARIMA(p,d,q)里的三个字母分别对应:p是自回归阶数,d是差分次数,q是移动平均阶数。我们刚刚把d定成了1,p和q一般还是结合图像和数据特征来选。
在MATLAB里定义ARIMA模型同样是用arima函数,区别是中间的差分参数不为0。比如我们尝试一个ARIMA(2,1,1)模型,含义是:差分一次,AR部分用2阶,MA部分用1阶:
lags = diff(y); % 差分后看定阶 figure; subplot(2,1,1); autocorr(lags); title('差分后ACF'); subplot(2,1,2); parcorr(lags); title('差分后PACF'); mdl2 = arima(2, 1, 1); % ARIMA(2,1,1) mdl2 = estimate(mdl2, train, 'Display', 'params');注意这里传给estimate的还是差分前的原始数据train,不用你手动把差分结果喂进去,arima函数会自动处理差分。这就是工具箱设计得好的地方,你只要在定义模型的时候告诉它d=1,它内部就会自动先差分再估计。
MA部分的作用是用过去的预测误差来修正当前预测。你可以把它理解为“自我纠错机制”。加了MA项之后,模型对短期波动和冲击的适应能力通常更强,这也是ARIMA比单纯AR模型更能打的主要原因之一。
4.3 效果对比:ARIMA比AR强在哪
我跑完上面的模拟数据后,顺手用AR(2)和ARIMA(2,1,1)分别对同一组带趋势的数据做预测,对比相当直观:AR模型的预测值很快就开始偏离真实曲线的走势,因为它本质上默认数据会围绕某个均值波动;而ARIMA模型因为做了一次差分,相当于先识别出“整体上涨的趋势”,在差分后的世界里预测变化量,然后再把趋势加回去,所以预测曲线能更贴住真实走势。
当然,ARIMA也不是万能的。如果数据里还有明显的季节性周期,你可能需要在ARIMA的基础上加季节差分,或者直接用SARIMA模型,MATLAB的arima函数也支持季节部分的指定,只是参数会多几个。
这里放一句我常跟朋友说的话:模型永远是为数据服务的,不要因为ARIMA听起来高级就无脑用。你把带趋势的数据丢给AR模型,然后把同一份数据丢给ARIMA模型,两个结果并排放一起,你自然就知道该用谁了。
5. 评估预测效果,心里要有杆秤
5.1 三个常用的评估指标
模型拟合完,光看训练集上误差小不算本事,关键要看在没见过的测试集上表现怎么样。我常用的三个指标是MSE、RMSE和MAPE。
MSE是均方误差,把误差平方后取平均,惩罚大误差比较狠;RMSE是它的开根号版本,单位跟原始数据一致,解释起来更直观;MAPE是平均绝对百分比误差,适合用来跟业务方汇报,因为大家都能看懂“误差百分之多少”。
在MATLAB里算这三个指标非常简单:
err = Yhat - test; mse = mean(err.^2); rmse = sqrt(mse); mape = mean(abs(err ./ test)) * 100;在同样的数据、同样的预测步长下,RMSE越小说明模型整体预测越准,MAPE越小说明相对偏差越小。我自己习惯三个都算,全放在一张表里,横向比较不同模型时一眼就能看出高下。
5.2 训练集测试集划分的潜规则
时间序列的划分和普通机器学习不一样,绝对不能随机打乱,必须按时间顺序切。原因很简单:时间序列的过去到未来是有方向的,你用未来的数据去训练模型,再拿过去的“未来”去检验,那就是开了天眼的作弊行为。
常见的做法是留出后面约20%到30%作为测试集,前面大部分作为训练集。如果数据里有明显的季节周期,最好保证训练集和测试集都覆盖完整的周期,不然模型可能只学到了季节的一部分。
还有一点我在实战中踩过:同一份数据如果反复拿来调参、选模型,那你其实已经通过测试集反馈了一些信息,最后得到的评估结果会偏乐观。所以涉及到正式汇报的时候,最好把数据切成三段:训练集用来拟合参数,验证集用来选模型,测试集最后碰一次。
5.3 AIC和BIC帮你选模型
ACF/PACF图看了半天还是不确定p和q,怎么办?用AIC和BIC。它们是模型选择界的评分系统,在拟合效果和模型复杂度之间做平衡。简单说就是:模型拟合得越好分越低,模型参数越多分越高,最后取总分最低的模型。
MATLAB里用完估计后可以直接算出AIC和BIC:
[estMdl, ~, logL] = estimate(mdl, train, 'Display', 'off'); [~, aic, bic] = aicbic(logL, p+q+2, numel(train));那个p+q+2是我野路子使用的参数个数估算方式,对应AR项数、MA项数、常数项和方差项。你不用抠得太死,关键看相对大小。我一般会从AR(1)开始,到AR(3),再到ARIMA(1,1,1)、ARIMA(2,1,1),每个模型都算一下AIC和BIC,挑数值最低的那个。
这招比眼睛看图靠谱,尤其是当数据不那么“标准”的时候。它也是我平时最常用的定阶方式。
5.4 残差检验:模型有没有榨干净
模型选完了,预测也做了,最后一步一定要看残差。残差就是真实值和模型拟合值之间的差距。如果模型把数据里的规律都提取干净了,残差应该表现得像白噪声,也就是没什么自相关性,随机散布。
在MATLAB里可以这样检查:
res = infer(estMdl, train); figure; subplot(2,1,1); autocorr(res); title('残差ACF'); subplot(2,1,2); qqplot(res); title('残差QQ图');如果残差ACF图里大多数柱子都在置信区间内,没有明显的周期性或趋势性,说明模型基本够用。如果残差还有明显的自相关,尤其是低阶滞后那里冒出好几根柱子,说明模型没吃完数据里的信息,你需要增加阶数,或者考虑加MA项、季节项。
QQ图则用来检查残差是否接近正态分布。虽然不完全正态分布也不致命,但如果偏离太厉害,说明噪声结构可能不是模型假设的那样,需要多留个心眼。
6. 常见报错和野路子心得
6.1 我踩过的几个坑
第一个坑就是维度问题。报错一般是“Input data must be a column vector.”这种情况十有八九是矩阵转置没转好,用y = y(:)直接解决。别笑,这个问题我见过无数人问,包括我自己刚入坑时也栽过。
第二个坑是arima函数未定义。你明明记得自己装了工具箱,结果一调用就报Undefined function。检查方式就是前面提到的ver('econ'),确认没装的话就去补装,同时看看是不是许可证里没勾选Econometrics Toolbox。有时候MATLAB本体装了,但许可证只买了部分工具箱,也会导致这个报错。
第三个坑是数据里带NaN或者Inf。时间序列数据经常在录入时出现空行,readtable读进来就变成了NaN。不清理的话,estimate函数跑一半就报错,或者估计出一些离谱的参数。用isnan和isinf先过滤一遍是最稳妥的。
第四个坑比较隐蔽,是预测步数过长导致预测发散。比如你用AR(2)预测后面200个点,结果曲线飞上天或者掉入深渊,这通常是模型参数估计不稳或者数据本身不适合这么长远的预测。解决办法是把预测长度缩短到你能接受的范围内,比如5步、10步,别一上来就贪多。
6.2 几点野路子经验
做时间序列预测这几年,我形成了一套自己的土办法,写出来给大家参考。第一条,任何数据都先试AR(1),哪怕你觉得模型太简单,它都是判断后续模型有没有提升空间的基准线。第二个,定阶别死磕ACF/PACF图,跑几个候选模型的AIC,谁低选谁,省时省力。第三,预测结果要拿到真实场景里验证,模拟数据跑得再好,也不如一次真实业务预测给你的信息量大。
我个人还有一个习惯,就是遇到复杂问题先不急着上高级模型。很多人一听说要预测就想到LSTM、深度学习,其实包括我自己测试过的一些案例里,ARIMA在短中期预测上的表现完全不落LSTM下风,而且可解释性强、训练成本低、调参不玄学。先用手里的简单模型把手头的事情跑通,再考虑要不要叠Buff,这才是效率最高的路径。
还有一点是关于数据质量的:很多时候模型效果差不是模型的问题,是数据没洗干净。缺失值、异常值、量纲不统一、时间间隔不均,这些问题才是预测翻车的最常见原因。把数据收拾整齐了,哪怕用最简单的AR模型,也能给你出个过得去的预测。