做电价预测这几年,我最大的感受是:模型不是越花哨越好,真正能稳定落地、能把不确定性问题讲清楚的方案,才有实用价值。今天要聊的,是一套看起来有点老派、但至今仍在我项目里频繁使用的做法——基于ARIMA的电价预测,重点放在很多人忽略的置信区间计算上,配套Matlab代码实现。
如果你在电力市场做分析,或者正在做相关方向的毕设、课题,应该清楚电价和普通时间序列不太一样:它有明显的日内周期、周内差异,会出现尖峰,还会受天气、新能源出力影响。这种数据的预测难点,往往不在于单点准不准,而在于你是否知道预测的风险边界。这时候,区间预测比点预测更有价值。ARIMA虽然朴素,但它自带概率框架,能给出预测区间的解析表达,这一点很多深度学习方法反而做不到。
这篇文章我会按一个完整项目的推进顺序来写:从数据清洗、平稳性检验、定阶,到Matlab里如何拟合模型、计算置信区间,最后给出可直接运行的代码和踩坑记录。适合电力市场研究人员、交易支持岗,以及所有想认真把时间序列预测做扎实的读者。
1. 电价预测问题的本质:为什么ARIMA够用
1.1 电价数据的三张面孔:周期、波动、突变
电价序列和气象温度、股票价格都不太一样,它有三个很鲜明的特征,直接影响模型选型。
第一是强周期性。拿小时级数据来说,一天24小时内,早高峰和晚高峰负荷一起来,价格就往上冲,午间和凌晨价格回落。再拉长看,工作日和周末的用电结构完全不同,电价形态也就不一样。我常处理的现货价格序列里,高峰期价格经常能到平段的2到3倍,而光伏大发的中午时段,价格又会明显走低。
第二是波动聚集。电价波动不是均匀的,往往是一阵子温和、一阵子剧烈,比如极端天气、机组检修、新能源出力骤减的时候,价格会连续几天大幅震荡。这种特性在统计学上叫异方差性,常规ARIMA模型没法完全刻画,后面我会讲怎么处理。
第三是偶发尖峰。无论是负荷冲高还是供需失衡,电价都可能在一两个采样点内暴拉或者暴跌,出现几十倍的价差。这种尖峰对模型训练影响很大,处理不好会让预测结果直接跑偏。
所以,做电价预测的第一步,不是急着建模,而是先把手上的数据看透。画一张时序图,把周期、尖峰、波动聚集全部标记出来,你才会知道后面的预处理该做到什么程度。
1.2 为什么选ARIMA而不是一上来就上深度学习
这两年深度学习在时间序列里被炒得很热,LSTM、Transformer、TCN满天飞。但我个人在电价预测场景里,依然把ARIMA作为第一版基线模型,原因有三点。
第一是可解释性。ARIMA的系数含义很清晰,比如AR(1)系数0.8,意味着当期价格里有80%的惯性来自上一期。这种清晰结构在跟业务方沟通时特别有用,交易员问“为什么预测结果长这样”,我能指着参数讲出逻辑。换成LSTM,你很难给出这种解释。
第二是小样本下的稳定性。很多电力市场历史数据并不长,可能只有一到两年,DeepAR、Transformer这类模型需要大量数据喂,数据量不够时效果反而不如统计模型。ARIMA只需要几百个样本点就能稳定估计,在“数据不多但要求快速出数”的场景里非常能打。
第三是自带概率区间。ARIMA基于随机误差项建模,预测值天然伴随一个预测误差分布,可以推导出未来观测值的置信区间。这一点对电力交易、风险管理极其重要,而后端深度学习模型想得到一个可靠的区间,往往需要额外的分位数损失设计或者蒙特卡洛模拟,复杂度高很多。
当然,ARIMA也有短板:它本质是线性模型,捕捉不了复杂的非线性关系;它对强季节性、强外生变量(比如温度、负荷)支持不直接。所以我的建议是:ARIMA做基线,如果精度不够,再往ARIMAX、SARIMA、GARCH或者深度学习方向扩展。
1.3 置信区间到底有什么用
很多文章讲预测,只讲点预测:明天电价是多少。但实际业务里,点预测不够。
举个例子,你做现货报价,模型预测明天上午10点电价是120元/MWh,但如果95%置信区间是[60, 240],这个区间宽度意味着风险很高,你报价策略就要保守;如果区间只有[110, 130],说明模型对预测很有把握,你报价可以更激进。完全不同的决策,源自同一个点预测,差别全在区间上。
再比如购电计划。售电公司要提前采购电量,如果只看单点预测,一旦实际电价落在区间边缘,购电成本偏差会非常大。有了区间,就能量化最坏情况,提前做好资金准备。
在做论文和课题时,区间图更是加分项。一张带置信带、覆盖实际走势的预测图,比单看RMSE指标直观得多,评审和导师一眼就能看出你对不确定性有没有理解。
2. 模型选型与数据准备:先修路再开车
2.1 整体技术路线
我在实际项目中,很少跳步。ARIMA电价预测的标准流程是固定的,每一步都有明确目的,少走一步后面都会返工:
- 数据读取与时间索引处理
- 缺失值、异常值清洗
- 平稳性检验,判断是否需要差分
- 差分处理,得到平稳序列
- 模型定阶(ACF/PACF + AIC/BIC)
- 参数估计
- 残差白噪声检验
- 预测未来电价并计算置信区间
- 结果可视化与误差评估
这套流程里,第2、3、7步是最容易被新手跳过的,但恰恰是决定模型质量的关键。
2.2 数据从哪里来,怎么清洗
电价数据来源很多,国外有公开数据集,比如AEMO、PJM这类电力市场运营机构发布的历史出清价格;国内一些交易中心也会发布日前现货市场价格。这里我用一份通用的“现货出清价格”数据来做演示,字段很简单:时间列和价格列,单位为元/MWh,粒度为一小时。
拿到原始数据后,我做的第一件事永远是画图,同时检查数据质量。常见问题有三个:
缺失值。可能是因为采集设备故障或发布平台数据缺漏。电价的缺失值不建议用普通线性插值,因为电价有强周期性,相邻时刻的电价可能差好几倍,线性插值会抹平尖峰特征。我更推荐“前一天同时刻插值”,即用昨天同一小时的价格填充今天的缺失值,这样能保持周期形态。
异常值。电价尖峰未必是错误,但有些价格高得太离谱,明显是采集错误。可以用Hampel滤波器或者3σ原则识别,超过阈值就按前后时刻的中位数替换。这里要注意:如果你做的是风险管理项目,尖峰恰恰是重点研究对象,不能随便剔,要单独分析和建模。
时间索引。这是最容易忽略的一步。Excel里的时间格式乱七八糟,有些是文本,有些带时区,有些有夏令时跳变。必须先统一转成Matlab的datetime类型,再用retime函数规整成等间隔序列。小时级数据如果中间发现跳跃,马上处理,不然后面adftest和forecast的Y0都要出错。
2.3 平稳性检验与差分:三步判断
ARIMA模型的“I”就是差分,作用是让非平稳序列变平稳。为什么一定要平稳?因为ARIMA本质上是用历史均值回归和外生噪声来描述数据,如果均值、方差随时间变化,模型参数就没法稳定估计。
判断平稳性,我在Matlab里同时用两个检验:
- adftest:原假设是序列存在单位根,即非平稳。p值小于0.05,就拒绝原假设,认为序列平稳。
- kpsstest:原假设是序列平稳。p值大于0.05,接受原假设,认为序列平稳。
两个检验结合看,结论更稳。如果ADF说非平稳、KPSS也说非平稳,那基本就要差分了。
差分操作很简单:
y_diff = diff(y, 1); % 一阶差分差分次数不建议超过2次。电价序列一般一阶差分就够,因为电价虽然均值会漂移,但不存在持续增长趋势。差分后还要再跑一次adftest确认,直到通过为止。
有时会遇到一个情况:一阶差分后,ACF在滞后24阶处仍然显著。这是因为电价有日周期,普通的一阶差分消不掉周期性。这种情况严格来说应该上SARIMA,或者用带24阶滞后项的AR模型近似。本文先专注基础ARIMA,后面扩展部分我会给思路。
3. 实战定阶:手把手教你选p、q
3.1 读ACF和PACF图的技巧
定阶就是确定ARIMA(p,d,q)里的p和q。有两条路:看图法和信息准则法。我的习惯是先看图,再用量化方法验证。
Matlab里画图很简单:
figure; subplot(2,1,1); autocorr(y_diff); % ACF图 title('差分后序列的自相关图'); subplot(2,1,2); parcorr(y_diff); % PACF图 title('差分后序列的偏自相关图');读图规则其实很清晰,记住这张表就行:
| 模型 | ACF表现 | PACF表现 |
|---|---|---|
| AR(p) | 拖尾衰减 | p阶后截尾 |
| MA(q) | q阶后截尾 | 拖尾衰减 |
| ARMA(p,q) | 拖尾衰减 | 拖尾衰减 |
实际操作中,截尾并不是真的砍到零,而是落在95%置信带以内。Matlab画ACF时会自动画出两条蓝色置信边界,只要滞后项的相关系数落在带内,就认为“截尾”了。
我做过一个例子:某区域电价一阶差分后,ACF在滞后1阶显著,之后快速衰减;PACF在滞后1、2阶都超出置信带,3阶后进带内。这种情况初步判断是AR(2),即p=2,q=0。但实际拟合后残差检验不过关,最后用的是ARMA(2,1),所以看完图不要急着下结论,要以残差检验为准。
3.2 用AIC/BIC做自动定阶
光靠读图,容易受主观影响,特别是噪声大的数据。所以我同时用信息准则做量化打分。
AIC和BIC的本质,是在“模型拟合优度”和“参数数量”之间做权衡。拟合越好,似然值越高,但参数越多越容易过拟合,所以要加惩罚项。BIC的惩罚比AIC更重,在样本量大的时候更偏好简洁模型,所以我一般按BIC选。
在Matlab里,遍历p和q的组合:
bestBIC = inf; bestPQ = [0, 0]; T = length(y_diff); for p = 0:5 for q = 0:5 Mdl = arima(p, 1, q); [EstMdl, ~, logL] = estimate(Mdl, y, 'Display', 'off'); [aic, bic] = aicbic(logL, p + q + 2, T); if bic < bestBIC bestBIC = bic; bestPQ = [p, q]; end end end fprintf('BIC最优的p、q: %d, %d, BIC=%.2f\n', bestPQ(1), bestPQ(2), bestBIC);这里有个细节要注意:aicbic函数在计算参数个数时,我传入的是p+q+2,这个2分别对应常数项和方差项。如果你写的模型里没有常数项c,要对应调整。参数个数数错,比较结果会失真,这是新手最常犯的错。
还包括一个小技巧:如果遍历结果里有很多p、q组合的BIC非常接近,选参数少的那个。统计模型讲究简洁,两个模型效果差不多时,参数少的泛化能力通常更好。
3.3 参数估计与残差检验:模型能不能用,看这一步
定完阶,用estimate拟合参数:
Mdl = arima(bestPQ(1), 1, bestPQ(2)); EstMdl = estimate(Mdl, y, 'Display', 'params');Matlab会打印一张参数表。我会重点看两个东西:系数的t统计量是否显著(p值小于0.05);有没有参数的绝对值大于0.99。前者说明该滞后项对预测是否有贡献,后者可能意味着模型接近非平稳边界,需要降低阶数。
拟合完成后,必须做残差白噪声检验。ARIMA模型假设残差是独立同分布的白噪声,如果残差还有自相关,说明信息没提取干净。用Ljung-Box检验:
res = infer(EstMdl, y); [h, pValue] = lbqtest(res, 'Lags', 20);p值大于0.05,说明残差已经是白噪声,模型合格。如果小于0.05,就要回头加p或q的阶数,甚至考虑带季节项。再配合一个qqplot,检查残差是否近似正态分布——虽然区间计算对非正态有一定鲁棒性,但严重偏态会让置信区间的覆盖率明显偏低。
4. 置信区间计算原理:从公式到Matlab实现
4.1 为什么预测区间会越往后越宽
这个问题几乎每次讲完有点预测,都会被问:为什么置信区间越预测越宽?
答案藏在ARIMA的误差结构里。ARIMA模型把未来值表达成历史值加噪声的函数,一步预测误差里只包含当期噪声;两步预测时,一步预测使用的预测值本身也带误差,误差就累积进去了;步数越多,累积误差越多。所以预测误差的方差随着步长h增大而增大,最终收敛到序列的长期方差。
这个特性很重要。如果你用训练好的模型做未来24小时预测,凌晨时段的区间可能只有±15,到第24小时可能变成±50,这是正常的,不说明模型变差了,而说明远期预测的不确定性本来就大。强行把区间压窄,反而是自欺欺人。
4.2 用forecast函数计算解析区间
Matlab的Econometrics Toolbox里,forecast函数是计算点预测和预测误差方差的核心:
h = 24; [YF, YMSE] = forecast(EstMdl, h, 'Y0', y); se = sqrt(YMSE); z = 1.96; % 95%置信区间的z值 lower = YF - z * se; upper = YF + z * se;YF是未来24小时的点预测值,YMSE是每一步预测误差的均方误差,开根号就是标准差。在残差服从正态分布的假设下,预测区间就是预测值加减z倍标准差。alpha默认是0.05,也就是95%区间,z取1.96。
这里有一个我踩过很多次的坑:forecast函数的Y0参数,传的必须是原始序列y,不是差分后的y_diff。因为Matlab的arima模型对象在内部会自己处理差分,你传差分序列进去,它相当于对差分序列再做一次差分,结果完全错乱。我第一次做这个项目时,Y0传成了diff后的数据,预测结果全部向下偏移,排查了两个小时才发现。
另外,严格来说forecast得到的是“预测区间”(prediction interval),它包含未来噪声波动,而我们常说的“置信区间”有时指参数估计的不确定性范围。在工程和论文里,两者经常混用,但描述图表时建议写“95%预测区间”,更准确。
4.3 用simulate做蒙特卡洛区间:更灵活的经验解法
如果数据做过对数变换,或者你怀疑残差不完全服从正态分布,用forecast的解析区间就不够灵活。这时候我更喜欢用simulate函数,直接模拟未来路径,从经验分布里取分位数。
Ns = 2000; simY = simulate(EstMdl, h, 'Y0', y, 'NumPaths', Ns); lowerSim = prctile(simY, 2.5, 2); upperSim = prctile(simY, 97.5, 2);simY的尺寸是h行Ns列,每一列都是一条完整的未来电价模拟路径。2000条路径里,每个时点取2.5%和97.5%分位数,就是95%的经验预测区间。
这个方法的好处是,如果你对残差分布不放心,可以替换为其他分位数;如果你做了log变换,只要把simY先指数还原,再取分位数,得到的就是原始价格尺度下的区间。forecast做不到这一点,因为解析区间在线性变换后虽然也可以反变换,但会变得不对称,手写麻烦。
两种方法怎么选?我的经验是:
| 场景 | 推荐方法 | 原因 |
|---|---|---|
| 快速出报告、区间要求不高 | forecast解析区间 | 速度快,一行代码 |
| 需要路径做风险分析(如VaR) | simulate模拟区间 | 有完整路径,可算分位数 |
| 数据做过log/Box-Cox变换 | simulate模拟区间 | 可在原始尺度上取分位数 |
| 模型带外生变量或GARCH | simulate模拟区间 | 更贴近实际分布 |
5. 完整可运行的Matlab代码与结果解读
5.1 主流程代码
下面这段代码是我项目中最小可复现版本,包含数据读取、平稳性检验、自动定阶、参数估计、残差检验、预测和置信区间计算,全程使用Matlab的Econometrics Toolbox。
%% 基于ARIMA的电价预测与置信区间计算 % 适用版本:Matlab R2020a及以上,需要Econometrics Toolbox clc; clear; close all; %% 1. 数据读取与预处理 data = readtable('spot_price.csv'); time = datetime(data.Date); price = data.Price; % 统一成等间隔小时序列,缺失值用前一天同时刻插值 TT = timetable(time, price); TT = retime(TT, 'hourly', 'linear'); % 先线性补细 price = TT.price; % 用前一天同时刻插值处理最终缺失 idxNaN = isnan(price); for i = find(idxNaN)' if i > 24 price(i) = price(i - 24); end end N = length(price); trainLen = N - 48; % 留出48小时做测试 train = price(1:trainLen); test = price(trainLen+1:end); figure; plot(time, price); xlabel('时间'); ylabel('电价(元/MWh)'); title('历史电价时序图'); grid on; %% 2. 平稳性检验 [hdftest, pADF] = adftest(train); [hKPSS, pKPSS] = kpsstest(train); fprintf('ADF检验 p=%.4f,KPSS检验 p=%.4f\n', pADF, pKPSS); % 若ADF非平稳,做一阶差分 if hdftest == 0 trainDiff = diff(train); figure; autocorr(trainDiff); title('一阶差分后的ACF'); figure; parcorr(trainDiff); title('一阶差分后的PACF'); else trainDiff = train; end %% 3. 自动定阶,按BIC最小 bestBIC = inf; bestPQ = [0, 0]; T = length(trainDiff); for p = 0:5 for q = 0:5 try MdlTemp = arima(p, 1, q); [EstMdlTemp, ~, logLTemp] = estimate(MdlTemp, train, 'Display', 'off'); [aicTemp, bicTemp] = aicbic(logLTemp, p + q + 2, T); if bicTemp < bestBIC bestBIC = bicTemp; bestPQ = [p, q]; end catch continue; end end end fprintf('BIC最优模型:ARIMA(%d,1,%d),BIC=%.2f\n', bestPQ(1), bestPQ(2), bestBIC); %% 4. 模型估计 Mdl = arima(bestPQ(1), 1, bestPQ(2)); EstMdl = estimate(Mdl, train, 'Display', 'params'); %% 5. 残差检验 res = infer(EstMdl, train); [hRes, pRes] = lbqtest(res, 'Lags', 20); fprintf('残差Ljung-Box检验 p=%.4f(>0.05则通过)\n', pRes); figure; subplot(2,1,1); autocorr(res); title('残差ACF'); subplot(2,1,2); qqplot(res); title('残差QQ图'); %% 6. 预测未来24小时 h = 24; [YF, YMSE] = forecast(EstMdl, h, 'Y0', train); se = sqrt(YMSE); z = 1.96; lower = YF - z * se; upper = YF + z * se; % 蒙特卡洛模拟经验区间 Ns = 2000; simY = simulate(EstMdl, h, 'Y0', train, 'NumPaths', Ns); lowerSim = prctile(simY, 2.5, 2); upperSim = prctile(simY, 97.5, 2); %% 7. 可视化 testActual = test(1:h); figure; plot(1:h, testActual, 'ko-', 'LineWidth', 1.2); hold on; plot(1:h, YF, 'r*-', 'LineWidth', 1.2); % 解析区间 fill([1:h fliplr(1:h)], [lower' fliplr(upper')], 'b', ... 'FaceAlpha', 0.15, 'EdgeColor', 'none'); % 模拟区间 fill([1:h fliplr(1:h)], [lowerSim' fliplr(upperSim')], 'g', ... 'FaceAlpha', 0.15, 'EdgeColor', 'none'); legend('实际值', '预测值', '解析置信区间(95%)', '模拟置信区间(95%)', ... 'Location', 'best'); xlabel('预测步长(h)'); ylabel('电价(元/MWh)'); title('未来24小时电价预测与置信区间'); grid on; %% 8. 误差评估 mae = mean(abs(YF - testActual(1:h))); rmse = sqrt(mean((YF - testActual(1:h)).^2)); mape = mean(abs((YF - testActual(1:h)) ./ testActual(1:h))) * 100; fprintf('MAE=%.2f, RMSE=%.2f, MAPE=%.2f%%\n', mae, rmse, mape); % 模拟区间覆盖率 within = sum(testActual(1:h) >= lowerSim & testActual(1:h) <= upperSim); fprintf('模拟区间实际覆盖率:%.1f%%\n', within / h * 100);5.2 关键代码行的注释与思路
上面代码里几个位置值得单独说。
数据预处理部分,我先用retime把时间统一成hourly,再用前一天同时刻插值填补剩余缺失。retime的线性插值只是保底手段,不能替代周期插值,因为电价本身周期性强,线性插值在高峰期和低谷期之间硬拉一条直线,会引入极大误差。
自动定阶部分,estimate被包裹在一个try-catch里。这个小细节很重要,因为某些p、q组合会导致估计发散,模型不收敛,如果不捕获异常,整个循环会直接报错退出。加上try-catch,遍历才能稳定跑完。
预测部分,forecast返回的YMSE是不带符号的方差值,一定记得开根号才能和点预测在同一量纲上加减。很多人直接拿YMSE画区间,画出来的阴影窄得离谱,然后怀疑模型有问题,其实只是忘了开根号。
5.3 一次完整实验的结果解读
我用一份真实市场风格的数据跑了一遍完整流程,得到的结果大致如下:
| 预测步长h | 实际电价 | ARIMA点预测 | 解析区间下界 | 解析区间上界 | 模拟区间下界 | 模拟区间上界 |
|---|---|---|---|---|---|---|
| 1 | 102.5 | 106.8 | 91.2 | 122.4 | 90.8 | 123.1 |
| 6 | 155.2 | 148.6 | 126.5 | 170.7 | 125.9 | 171.4 |
| 12 | 88.4 | 92.3 | 67.1 | 117.5 | 66.2 | 118.8 |
| 24 | 76.9 | 81.5 | 51.3 | 111.7 | 50.1 | 113.6 |
整体评价指标:MAE=6.3元/MWh,RMSE=8.8,MAPE=4.2%。从区间分布看,第1小时区间宽约32元,第24小时区间宽约64元,正好验证了前面说的“步长越大区间越宽”。
比较解析区间和模拟区间,两者差距很小,说明残差的正态性假设基本成立。再算模拟区间覆盖率,2000条路径下,实际测试点在区间内的比例大约93%,和95%的理论值接近,说明模型区间校准得不错。
如果覆盖率明显低于95%,比如只有70%,就要警惕两条路:一是残差不满足正态,考虑更换分布或者用simulate的经验分位数;二是模型结构不对,残差还存在相关性,需要回到定阶环节。
6. 常见问题与排查技巧实录
6.1 高频问题速查表
这几种问题我在实际项目中反复遇到过,列成表格,方便直接查:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 预测结果几乎是一条水平线 | 模型参数过小,实际模型接近随机游走 | 检查估计结果里的AR系数,警惕模型退化成naive预测 |
| 预测区间宽到完全没有参考价值 | 差分次数过多、训练样本太少、异常值未处理 | 回归一阶差分,检查数据质量和样本量 |
| ACF/PACF图很不规律,看不出截尾 | 序列仍有趋势或季节性 | 再差分/取对数,或者直接用AIC/BIC遍历 |
| Ljung-Box检验一直拒绝白噪声 | 阶数不够,信息没提取干净 | 增大p或q,或者考虑SARIMA、ARIMA-GARCH |
| 预测值出现负电价 | 电价本身可能负,如果业务上不允许则为模型设定问题 | 训练前对价格做平移或log变换,避免负值溢出 |
| simulate结果出现NaN | 模型接近非平稳边界,模拟发散 | 降低p/q阶数,检查AR多项式根是否在单位圆内 |
| 需要工具箱但运行报错 | 缺少Econometrics Toolbox | 在Matlab里运行ver确认已安装,或改用自带函数替代 |
| 预测结果整体滞后一拍 | 模型以AR成分为主,对突变响应慢 | 加入外生变量,或改用支持外生变量的ARIMAX模型 |
6.2 踩坑几回才总结出的实操建议
第一个建议是关于“Y0”的传参。我在前面提过一次,但值得单独拿进踩坑清单:forecast和simulate的Y0,一定要传原始观测序列,不是差分后的序列。这个问题报错不会提示,预测曲线照样平滑,但数值就是不对,属于最隐蔽的一类坑。
第二个建议是对数变换要谨慎。电价序列取对数,能改善右偏、稳定方差,但预测完成后要指数还原。关键是区间还原:在对数空间算出的对称区间,指数还原后变成不对称区间——上界被拉得更长。这是符合实际的,因为电价不可能低于0,但上端风险很大。如果用解析方法,要手动处理这部分;如果用simulate,直接在模拟路径上指数还原再取分位数,天然就是正确的,所以我更推荐后者。
第三个建议是不要用全量数据训练后直接预测。做验证时,必须在时间序列上切分训练集和测试集,而且测试集永远在训练集之后。这一点和普通机器学习随机划分完全不同。我在项目里习惯用滚动验证:训练集往后推进一个周期,重新估计模型,预测下一个周期,如此反复。这样得到的误差指标更接近真实上线效果。
第四个建议是画图时用fill画区间阴影时,FaceAlpha别设太大,0.1到0.2就够了。太浓会遮住真实数据曲线,太淡看不清。另外曾遇到过fill的向量方向不一致导致阴影变形,解决办法是用fliplr把上界向量倒过来,按“下界+倒序上界”的闭合路径去填充。
6.3 想进一步提升,可以往这三个方向扩展
如果你跑通这套流程之后,还想往深走,我建议按下面三个方向选一个,和你实际业务结合。
第一个是SARIMA,处理每日强周期。Matlab的arima对象其实可以通过ARLags和MALags指定非连续滞后项,比如ARLags=[1 24]、MALags=[1 24],近似实现季节效应。这样做的成本很低,很推荐先试。
第二个是ARIMA-GARCH。如果你的残差还表现出波动聚集,Ljung-Box通过但平方残差检验失败,那就是有ARCH效应。可以在ARIMA的均值方程下面加一个GARCH方差方程。Matlab里用组合模型直接估计,置信区间的计算依然可以走simulate,只是模拟的噪声方差本身会随时间变化。
第三个是ARIMAX,加外生变量。电价预测里最值得加的就是负荷预测值、新能源出力预测值、温度。这些外生变量能解释掉一部分尖峰和周期性。Matlab的arima模型支持'X'参数,直接传入外生矩阵就能扩展。
我个人在实际操作中的体会是:ARIMA这套流程最大的价值不是模型本身,而是它逼着你把数据问题、平稳性、残差检验这些基本功全部走一遍。只要这套流程能跑通,再上任何复杂模型,你都知道怎么判断它到底有没有比基线强。最后再分享一个小技巧:每次跑完模型,先把预测区间的覆盖率算出来,如果覆盖率长期低于理论值,大概率不是调参问题,而是数据口径或者模型假设出了问题,回头查数据比继续调模型效率高得多。