ARTICLE DETAIL

资讯详情

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

ARIMAX多变量时间序列预测:从原理到Python实现与避坑指南

ARIMAX多变量时间序列预测:从原理到Python实现与避坑指南 简介基于ARIMAX的多变量预测模型Python源码与配套数据集面向有一定时间序列分析基础、希望用Python实现多元外生变量预测的读者常用于经济指标、销量预测、能源负荷等场景也是科研与竞赛中常用的预测方案。压缩包内共7个文件2个Python脚本分别承担数据预处理与ARIMAX建模训练2个CSV文件提供示例多变量数据2张图片辅助展示数据特征与模型结果另有1份Markdown说明文档梳理全流程。所有代码均带注释配合README可从数据读取、平稳性检验、外生变量构造、参数调优到模型评估逐步理解方便替换自己的数据快速复用覆盖实用建模闭环。整个包仅146KB轻量专注已有408人学习/下载是多变量时间序列建模入门与工程落地的直接参考尤其适合课程设计与项目原型验证。1. ARIMAX 多变量预测模型这份源码包能解决什么问题提到时间序列预测绝大多数人第一个想到的是 ARIMA。但单变量 ARIMA 只吃自己的历史值当预测目标明显受其他变量影响时——比如气温影响空调销量、广告投放影响网站流量——ARIMA 就给不出令人满意的答案。ARIMAX带外生变量的自回归移动平均模型就是在 ARIMA 的框架里多加一组输入 X把外部影响和自身惯性一起建模。这份源码包恰好覆盖了从数据预处理、平稳性检验、定阶、拟合到预测评估的完整链路适合拿来做课程设计也适合作为多变量预测的起步模板。包里两个 Python 脚本、两组 CSV 数据集和 README 都齐解压后对照注释改参数就能跑通。2. ARIMAX 原理与建模思路外生变量是怎么进入时间序列模型的2.1 ARIMA 到 ARIMAX多了一组输入模型发生了什么变化ARIMA 模型由三部分组成自回归项 AR(p) 用目标变量自身的历史值解释当前值移动平均项 MA(q) 用过去的预测误差修正当前值差分阶数 d 负责把非平稳序列变成平稳序列。它的表达式可以粗略理解为当前值等于常数项加上前 p 个自身历史值的加权和再加上前 q 个误差项的加权和最后加一个白噪声。ARIMAX 在这个基础上增加了一行把外生变量 X 的当期值也可以包含滞后项以线性回归的形式加入方程。表达式大致变成当前值等于常数项加上外生变量的线性组合加上 AR 项、MA 项和误差项。注意这里的 X 和普通线性回归里的特征不太一样——它不负责解释全部方差而是在 ARIMA 已经处理了目标变量自相关结构之后补充外部因素带来的增量影响。这里有个关键区别普通回归模型假设误差独立但时间序列的误差往往是自相关的。ARIMAX 的好处在于AR 和 MA 项已经把自相关结构吸收了X 的系数估计才不会被残差自相关污染。换句话说外生变量的系数是「扣除历史惯性之后」的净效应这比直接拿 X 和目标变量做 OLS 回归要可靠得多。我在看这份源码的时候注意到 arimax.py 里对 X 的处理顺序是放在差分之后的这个顺序很关键。statsmodels 的 SARIMAX 类在建模时会先对目标变量做差分而外生变量是作为回归项直接进入状态空间方程的所以外生变量本身是否平稳模型不会自动帮你处理。要不要对外生变量也做差分或变换需要自己判断这也是后面避坑章节要展开的重点。2.2 适用场景什么时候该用 ARIMAX什么时候别用ARIMAX 最适合的场景是目标变量有明确的外部驱动力而且这些外生变量在预测未来时也能拿到。我见过比较典型的用法是门店销售预测——把天气温度、节假日标记、促销活动作为外生变量预测未来一周的日销售额。这类场景中外生变量要么是已知的计划值要么是气象预报值未来值可得模型才有意义。宏观指标预测也类似比如用工业增加值、货币供应量等指标预测 GDP 季度的走势。反过来有三种情况我建议直接放弃 ARIMAX。第一外生变量本身的未来值拿不到比如你想用「消费者信心指数」预测消费但这个指数发布滞后预测当期时它的最新值还没公布输入就是残缺的。第二目标变量和外生变量存在强双向因果比如股价和成交量互相影响你用成交量预测股价本质上是在用内生变量当外生变量系数解释会失控。第三金融高频序列这种信噪比极低的场景很多做量化交易的人拿 ARIMAX 去预测价格结果往往不如随机游走假设。不是说模型不行而是金融数据的驱动机制远超「线性外生变量」能表达的范围。那这份源码适合拿来做什么从它配套的数据集看data.csv 和 datacf.csv 是典型的目标序列加外部因素序列比较接近经济指标或运营监控数据的组织方式。拿它做课程设计、做毕业论文的实证部分或者作为你第一个多变量预测模型来练手都比从零开始写合适。README 里把文件关系和运行顺序写清楚了对新手友好程度不错。2.3 选型落点statsmodels 里没有 ARIMAX 类用 SARIMAX 传 exog一个容易让新人卡壳的点是statsmodels 里找不到一个叫 ARIMAX 的类。ARIMAX 的实现在 statsmodels 里被统一收纳在SARIMAX这个类下面——它本来是为了处理带季节性的 SARIMA 模型设计的但加上了exog参数之后它同时也是一个功能完整的 ARIMAX 实现。你只需要传入order(p, d, q)和exogX模型内部就会把外生变量当回归项处理。看一下它的调用形态from statsmodels.tsa.statespace.sarimax import SARIMAX # y 是目标变量序列X 是外生变量矩阵可以是多列 model SARIMAX(y, exogX, order(1, 1, 1)) result model.fit(dispFalse)dispFalse是为了关掉迭代过程的输出训练的时候不会刷屏。这里的order(1, 1, 1)意思是 p1、d1、q1。p 和 q 的取值通常靠 ACF/PACF 图或者 AIC 网格搜索确定d 的取值靠平稳性检验确定这些在 arimax.py 里都有对应的实现逻辑。把这个 API 映射到这份源码包上arimax.py 里的核心流程应该是先读取数据处理好的目标序列和外生变量做平稳性检验确定 d再用 AIC 遍历确定 p 和 q最后用 SARIMAX 拟合和预测。我拆这份代码时也是按这条主线去读的读完基本就能把这套逻辑迁移到自己的数据集上。3. 环境与文件梳理把压缩包跑通的最小路径3.1 Python 环境与依赖安装这份源码是基于 Python 写的依赖集中在 pandas、numpy、statsmodels、matplotlib 这四个库上。pandas 负责数据读取和对齐numpy 做数组运算statsmodels 提供 SARIMAX 模型matplotlib 用来画预测对比图。建议直接用 pip 安装pip install pandas numpy statsmodels matplotlib如果你用的是 Anaconda也可以换成conda install pandas numpy statsmodels matplotlib。版本方面没有特别苛刻的要求Python 3.8 以上都能跑statsmodels 的 API 在近几个版本里对SARIMAX的调用方式保持一致所以不太需要担心版本冲突。在 vscode 里配好 Python 解释器之后直接在终端里运行脚本就行。我自己拿到压缩包的习惯是先建一个干净的虚拟环境再装依赖避免和系统 Python 里的包互相污染。创建虚拟环境的命令python -m venv arimax_env source arimax_env/bin/activate # Windows 下是 arimax_env\Scripts\activate pip install pandas numpy statsmodels matplotlib装好依赖之后最好先跑一个最简单的导入测试确认 statsmodels 能正常加载。很多时候报错不是源码的问题而是环境里缺包或者包的版本不对。3.2 压缩包文件结构与各文件用途解压之后你会看到六个东西两个 Python 脚本、两个 CSV 数据集、一个 README、两张运行截图。文件分工如下表文件作用README.md运行说明包含执行顺序和预期输出datapre.py数据预处理脚本负责读取原始数据、对齐时间索引、处理缺失值arimax.py主模型脚本负责定阶、建模、预测、评估data.csv目标变量序列内生变量datacf.csv外生变量序列从文件名看 cf 大概率是控制因素/协变量微信图片_*.png运行结果截图方便对照输出格式两个 Python 脚本的分工很清晰datapre.py 是上游arimax.py 是下游。运行顺序上必须先跑 datapre.py 把数据处理好再跑 arimax.py 建模。如果你直接运行 arimax.py 报错找不到数据基本就是因为跳过了预处理步骤。3.3 最小复现路径从解压到出预测图要在你自己的机器上把这份源码跑通按下面步骤来# 1. 进入解压目录 cd 你解压的目录路径 # 2. 先跑数据预处理 python datapre.py # 3. 再跑模型训练与预测 python arimax.py跑完之后屏幕上应该会输出模型拟合的摘要信息、预测评价指标比如 RMSE 或 MAE并且弹出或保存一张预测值和真实值的对比图。压缩包里的两张微信图片就是当时跑出来的截图可以拿来对照自己的输出是否正常。这里有一个小提醒Windows 下不要直接双击.py文件运行闪退之后没有任何报错信息可查。应该打开终端cmd 或 PowerShell先cd到目录再执行python xxx.py。这样即使报错也能看到完整的 traceback。如果你用的数据文件不在脚本同级目录记得改脚本里的路径变量这个在 README 里应该也提到了。4. 核心代码拆解datapre 到 arimax 的完整预测链路4.1 datapre.py数据对齐和缺失值处理是第一步拿到时间序列数据第一件事永远是把两张表的索引对齐。data.csv 和 datacf.csv 的采样频率和起止时间未必完全一致直接扔进模型一定会报错。datapre.py 里核心做的事情就是把两张表读进来、统一时间格式、按时间索引对齐、处理缺失值。常见的处理逻辑是这样的import pandas as pd # 读取目标变量和外生变量 y_raw pd.read_csv(data.csv, parse_dates[date], index_coldate) X_raw pd.read_csv(datacf.csv, parse_dates[date], index_coldate) # 按时间索引对齐left 保证目标变量的时间点全部保留 df y_raw.join(X_raw, howleft) # 缺失值处理先向前填充再向后填充兜底用线性插值 df df.fillna(methodffill).fillna(methodbfill).interpolate() # 去掉完全没数据的行 df df.dropna()逻辑说明parse_dates把日期列解析成时间类型index_col把日期列设为索引join里的howleft表示以目标变量 y 的时间点为准X 里多出来的时间点会被舍弃这样可以保证模型训练时 y 和 X 的行数严格一致。缺失值处理顺序是先向前填充再向后填充最后用插值兜底这个顺序是为了避免序列开头和结尾的缺失值处理不了。参数说明fillna(methodffill)表示用上一个有效值填充适合处理偶发的缺失点interpolate()对连续缺口的处理更平滑。如果你的数据缺口特别大建议回头检查数据源而不是依赖填充。这一步最容易翻车的地方是join之后行数变化。y 有 500 条记录X 只有 400 条left join 之后有 100 行 X 是 NaN如果直接 dropna 会把这 100 行丢掉模型实际训练样本就缩水了。这块属于数据质量问题不是代码问题得回到业务层面判断要不要补数据。4.2 arimax.py平稳性检验与定阶ARIMAX 的建模起点是确定差分阶数 d。常见做法是用 ADF 检验判断序列是否平稳p 值大于 0.05 就做一阶差分再检验直到序列平稳。这份源码里大概率封装了这样一个循环from statsmodels.tsa.stattools import adfuller def find_diff_order(series, max_d2): s series.copy() for d in range(max_d 1): p_value adfuller(s.dropna(), autolagAIC)[1] if p_value 0.05: return d # 当前阶数下序列已经平稳 s s.diff() return max_d逻辑说明adfuller返回的第二个元素是 p 值小于 0.05 可以拒绝「存在单位根」的原假设认为序列平稳。每做一次差分就对差分后的序列重新检验最多试到 max_d 阶如果超过两阶还不行可能是数据有季节性或者结构突变需要另外处理。参数说明autolagAIC表示检验时用 AIC 自动选择滞后阶数max_d2限定了最大差分阶数实际建模中超过 2 阶的差分会让模型解释变得困难我一般不会继续往上加。确定 d 之后再确定 p 和 q。经验做法是画 ACF 和 PACF 图看截尾情况但看图定阶这回事多少有点玄学——不同人看同一个图能定出不同的阶数。所以我更推荐用网格遍历加 AIC 选优把这个过程自动化import warnings warnings.filterwarnings(ignore) best_aic, best_order float(inf), None for p in range(5): for q in range(5): try: model SARIMAX(y_train, exogX_train, order(p, d, q)) result model.fit(dispFalse) if result.aic best_aic: best_aic, best_order result.aic, (p, d, q) except Exception: continue逻辑说明遍历 p 和 q 从 0 到 4 的全部组合用 AIC 作为模型质量的衡量指标。AIC 在拟合优度和模型复杂度之间取平衡值越小越好。try-except是必要的——某些参数组合下模型可能收敛失败直接跳过就行不影响最终选优。参数说明order(p, d, q)里的 d 来自上一步的find_diff_orderexogX_train必须传否则这就退化成普通 ARIMA 了。这个网格的规模是 5×525 个组合每个组合拟合一次数据量不大的时候几秒就能跑完。4.3 模型拟合与预测评估定好阶之后整个流程进入最后一段划分训练测试集、拟合模型、做预测、算指标。这块的代码逻辑是整份源码最核心的部分from sklearn.metrics import mean_squared_error, mean_absolute_error import numpy as np # 按 8:2 划分训练集和测试集 split_idx int(len(df) * 0.8) y_train, y_test df[y].iloc[:split_idx], df[y].iloc[split_idx:] X_train, X_test df[[x1, x2]].iloc[:split_idx], df[[x1, x2]].iloc[split_idx:] # 用最优参数拟合 final_model SARIMAX(y_train, exogX_train, orderbest_order) final_result final_model.fit(dispFalse) # 预测测试集长度对应的未来值必须同时传入未来外生变量 forecast final_result.get_forecast(stepslen(y_test), exogX_test) pred_mean forecast.predicted_mean # 计算评估指标 rmse np.sqrt(mean_squared_error(y_test, pred_mean)) mae mean_absolute_error(y_test, pred_mean)逻辑说明get_forecast(stepsn, exogX_test)返回一个对象predicted_mean是点预测结果。这里最容易忽略的是exogX_test——ARIMAX 预测未来时外生变量的未来值必须由你自己提供模型不会自动生成。你要预测未来 10 期就必须传给模型未来 10 期的 X 数据。参数说明stepslen(y_test)表示预测的期数等于测试集长度这样预测值和真实值才能逐位对比RMSE和MAE都是越小越好RMSE 对大误差更敏感如果预测在某些时间点偏差特别大RMSE 会明显拉开差距。预测完成之后再把预测值和真实值画在一张图上视觉上确认预测是跟着真实走势走的。压缩包里那两张微信图片就是运行到这一步的产出。到这一步这个包的完整链路就跑通了。5. ARIMAX 常见问题与避坑五个容易翻车的多变量预测细节5.1 exog 行数和 y 对不齐拟合时报 ValueError现象运行 arimax.py 到model.fit()这一步报错提示exog must have the same number of rows as the endog。原因外生变量和目标变量的行数不一致。datapre.py 处理后如果dropna()把部分行删掉了或者两张表的时间索引没有对齐就会出现这个错。另一种情况是你自己换了数据集X 的日期范围比 y 窄。解决先用print(len(y), len(X))确认两个序列的长度再检查df.index有没有重复值。对齐的逻辑应该以目标变量的时间索引为基准做 left join不要把两张表直接横向拼接。处理完缺失值之后最后再打印一次长度确保完全一致再进模型。5.2 预测时报错或结果全是 NaN未来外生变量没传现象模型训练成功但get_forecast()报错提示需要提供外生变量或者预测结果返回的全是 NaN。原因ARIMAX 和 ARIMA 在预测阶段的差异就在这里——ARIMAX 必须知道预测期内每个时间点的外生变量值否则状态空间方程算不出来。很多人习惯了 ARIMA 的get_forecast(stepsn)不带额外参数迁移到 ARIMAX 时漏传了exog。解决在调用get_forecast之前先确认 X_test 的长度等于 steps。如果要用模型预测未来的具体日期那段时间的外生变量你得自己准备——常见做法是直接用历史均值、单独跑一个预测模型、或者用它实际对应的业务计划值。没有未来 X 的数据ARIMAX 就预测不了。5.3 差分之后预测曲线变成一条水平线外生变量完全没起作用现象预测结果的曲线几乎是一条直线RMSE 大得离谱外生变量似乎对预测没有任何贡献。原因这个坑通常出在差分阶数 d 和外生变量的关系上。当 d≥1 时模型建模的对象是差分后的 y但外生变量 X 还是原始值。如果 X 本身也是非平稳的差分后的 y 和原始 X 之间几乎没有有效协变关系X 的系数估计出来接近零预测自然退化成了纯 ARIMA。解决对 X 做和目标变量一样的差分处理再把差分后的 X 放进模型。具体做法是在预处理阶段手动对每列外生变量也做diff()然后和差分后的 y 对齐建模。另一种思路是先确认 X 对 y 的驱动关系把毫无相关性的外生变量直接移除变量不是越多越好。5.4 ADF 检验 p 值在 0.05 附近反复横跳定出的 d 不稳定现象同一份数据某次运行定出的差分阶数是 1换一段训练集再跑就变成 0 或者 2模型预测结果也随之大起大落。原因ADF 检验的 p 值在样本量不够大、序列存在弱季节性或结构性断点的时候检验功效会下降p 值经常在临界值附近摇摆。这不是代码问题是数据本身给不出明确答案。解决搭配 KPSS 检验一起判断两个检验结论一致时才确定差分阶数。KPSS 的原假设是平稳和 ADF 正好互补。如果 ADF 说非平稳、KPSS 说平稳那大概率是数据量不足需要扩充训练集如果序列有明显的周期性先做季节分解或直接上seasonal_order不要急着加大 d。d 不是越高越好过差分会让模型丢失长期信息。5.5 中文路径和中文列名引发编码报错脚本在 Windows 上闪退现象在 Windows 上运行python datapre.py报UnicodeDecodeError或者直接闪退错误指向pd.read_csv。原因CSV 文件的编码和系统默认编码不一致。Windows 下 pandas 默认用gbk解码但很多 CSV 是utf-8存的另外脚本所在路径带中文时部分环境下 Python 的读取也会出问题。解决在pd.read_csv里显式指定编码写成pd.read_csv(data.csv, encodingutf-8)如果还报错就换encodinggbk试。项目目录和文件名尽量用英文不要放在「桌面」这种含中文字符的路径下跑。运行的时候在终端里执行而不是双击脚本这样所有报错信息都能完整看到。6. 进阶让 ARIMAX 预测结果更可信到这一步模型能跑通、能出数了但预测结果到底靠不靠谱还需要额外验证。我一般会做两件事滚动预测和残差检验。一次性预测容易把误差放大——模型在测试集第一期的误差会传导到后面所有期看起来误差很大但不是模型真实水平。滚动预测每次只预测一期然后把真实的下一期值追加到训练集里模拟实际业务中「每天收到新数据、预测明天」的节奏history y_train.copy() history_X X_train.copy() predictions [] for i in range(len(y_test)): model SARIMAX(history, exoghistory_X, orderbest_order) result model.fit(dispFalse) pred result.get_forecast(steps1, exogX_test.iloc[[i]])[predicted_mean].iloc[0] predictions.append(pred) # 把真实值加入历史推进到下一个时间点 history pd.concat([history, y_test.iloc[[i]]]) history_X pd.concat([history_X, X_test.iloc[[i]]])这样得到的误差更接近真实上线表现。配合残差检验看模型有没有吃干净信息from statsmodels.stats.diagnostic import acorr_ljungbox residuals y_test - predictions ljung_box acorr_ljungbox(residuals, lags10, return_dfTrue)Ljung-Box 检验的 p 值如果大于 0.05说明残差接近白噪声模型把该抓住的时间结构都抓住了如果 p 值很小说明残差里还有自相关模型漏掉了某些规律该回去调整 p、q 或者补充外生变量。从那以后我每次做多变量预测第一件事是先确认未来外生变量拿不拿得到再决定到底要不要用 ARIMAX——这个包让你把整个流程跑通但最关键的判断永远在数据这一侧。希望帮到你。本文还有配套的精品资源点击获取
返回列表