如何基于SARIMAX(MA(1)-X)模型实现样本外一步向前预测?
解决SARIMAX一步向前预测的索引匹配错误问题
问题原因
你遇到的索引错误,是因为predict方法默认会匹配训练数据的索引范围,而测试集索引不在训练数据的索引序列里,导致无法定位。另外,直接用predict传入测试集exog做的是静态预测,不是你需要的基于前一步结果的一步向前动态预测。
解决方案
方法1:使用get_prediction实现动态一步向前预测
statsmodels的get_prediction方法支持dynamic参数,设置为测试集起始索引后,会从该点开始用前一步的预测值作为输入,自动实现动态一步向前预测。
修改后的代码:
import statsmodels.api as sm import matplotlib.pyplot as plt from sklearn.metrics import r2_score import pandas as pd # 训练测试拆分(原代码保留) train_mask = xc.index < cutoff_date xc_train, xc_test, yc_train, yc_test = xc[train_mask], xc[~train_mask], yc[train_mask], yc[~train_mask] # 定义并训练MA(1)-X模型 model_ma1x = sm.tsa.SARIMAX(yc_train, exog=xc_train, order=(0, 0, 1)) results_ma1x = model_ma1x.fit() # 用get_prediction做动态一步向前预测 pred_results = results_ma1x.get_prediction( start=yc_test.index[0], end=yc_test.index[-1], exog=xc_test, dynamic=yc_test.index[0] ) pred_ma1x = pred_results.predicted_mean # 绘图和评估(修正原代码中R2计算的错误,改用测试集数据) yc_test.plot(label='Actual', legend=True) pred_ma1x.plot(label='Predicted', legend=True) plt.title('MA(1)-X Model (Dynamic One-Step Ahead)') plt.annotate(f'R2: {r2_score(yc_test, pred_ma1x):.3f}', xy=(0.95, 0.05), xycoords='axes fraction', ha='right', va='bottom', fontsize=9) plt.show() print(results_ma1x.summary())
方法2:手动循环实现一步向前预测(完全贴合需求)
如果需要严格基于前一步预测值+新实际外生变量做逐步预测,可以手动循环测试集,每次用真实值更新模型状态(保持训练好的MA系数不变):
import statsmodels.api as sm import matplotlib.pyplot as plt from sklearn.metrics import r2_score import pandas as pd # 训练模型(原代码保留) train_mask = xc.index < cutoff_date xc_train, xc_test, yc_train, yc_test = xc[train_mask], xc[~train_mask], yc[train_mask], yc[~train_mask] model_ma1x = sm.tsa.SARIMAX(yc_train, exog=xc_train, order=(0, 0, 1)) results_ma1x = model_ma1x.fit() # 初始化预测列表和当前模型状态 preds = [] current_results = results_ma1x # 逐步遍历测试集 for idx, exog_val in xc_test.iterrows(): # 执行一步向前预测 pred = current_results.predict(start=idx, end=idx, exog=[exog_val.values])[0] preds.append(pred) # 用真实值更新模型历史,保持训练好的参数不变 updated_endog = current_results.model.endog.append(yc_test.loc[idx:idx]) updated_exog = current_results.model.exog.append(exog_val.to_frame().T) # 基于更新后的历史创建新模型,加载已训练参数 updated_model = sm.tsa.SARIMAX(updated_endog, exog=updated_exog, order=(0, 0, 1)) current_results = updated_model.filter(results_ma1x.params) # 转换为Series方便绘图 pred_ma1x = pd.Series(preds, index=yc_test.index) # 绘图和评估 yc_test.plot(label='Actual', legend=True) pred_ma1x.plot(label='Predicted', legend=True) plt.title('MA(1)-X Model (Manual One-Step Ahead)') plt.annotate(f'R2: {r2_score(yc_test, pred_ma1x):.3f}', xy=(0.95, 0.05), xycoords='axes fraction', ha='right', va='bottom', fontsize=9) plt.show() print(results_ma1x.summary())
关键说明
- 方法1的
dynamic参数会自动处理状态更新,代码简洁,适合快速实现动态预测。 - 方法2的手动循环更灵活,严格按照你“基于前一步预测和新实际数据”的要求,每次用真实值更新模型历史,确保每一步预测都依赖上一步的结果。
- 原代码中的R2计算存在逻辑错误,已经修正为用测试集实际值和预测值对比。
内容的提问来源于stack exchange,提问作者user20487324
相关产品推荐
相关产品推荐

