如何修复Python中statsmodels.tsa.statespace.SARIMAX代码的报错问题
先看你遇到的两个核心问题:KeyError: Timestamp('2016-06-01 00:00:00') 和 ValueError: index must be monotonic increasing or decreasing,我来逐个拆解并给出修复方案:
1. 时间切片越界导致KeyError
这个错误是最直接的:你的原始数据stand_data_full的时间范围只到2016-05-31 17:00,但当循环到time = 2016-05-31 17:00时,代码里的切片time + relativedelta(hours=7)会生成2016-06-01 00:00,这个时间戳不在你的原始数据索引里,所以抛出KeyError。
修复方法:
调整time_range的结束时间,确保所有time加上7小时后仍在原始数据的时间范围内。比如你要预测time+1到time+6,那么time的最晚值应该是原始数据的结束时间减去7小时(保证切片终点不越界),代码如下:
# 先获取原始数据的最大时间(假设stand_data_full的索引是时间戳) max_data_time = stand_data_full.index.max() # 调整time_range的结束时间:最晚的time要满足 time + relativedelta(hours=7) <= max_data_time adjusted_end = max_data_time - relativedelta(hours=7) time_range = pd.date_range(start_range, adjusted_end, freq='H')
如果你的原始数据确实只到2016-05-31 17:00,那adjusted_end就是2016-05-31 10:00,这样所有切片都不会越界。
2. 索引非单调导致的ValueError
这个错误说明stand_data_full的时间索引不是严格单调递增/递减的,pandas无法处理非单调索引的切片操作。
修复方法:
在使用数据前,强制对索引进行排序:
# 确保时间索引严格单调递增 stand_data_full = stand_data_full.sort_index() # 可选:验证索引单调性,提前发现问题 assert stand_data_full.index.is_monotonic_increasing, "数据索引必须单调递增"
3. 优化切片写法,避免潜在问题
你当前的切片写法stand_data_full[cell] [time - ... : time + ...]存在空格,虽然语法上没问题,但用.loc进行标签索引更清晰、更可靠,也能减少索引相关的bug:
# 替换原来的endog和exog切片 start_slice = time - relativedelta(hours=60) end_slice = time + relativedelta(hours=7) endog_data = stand_data_full.loc[start_slice:end_slice, cell] exog_data = stand_data_full.loc[start_slice:end_slice, feature_list] model_fitted = sm.tsa.statespace.SARIMAX( endog=endog_data, exog=exog_data, order=(param[0], d, param[1]), seasonal_order=(param[2], D, param[3], S) ).filter(best_stand_model[zone].params)
4. 额外的验证步骤(可选)
为了确保每个循环的切片都有效,可以在循环内添加检查,跳过超出范围的时间:
for cell in tqdm_notebook(selected_cells): for time in time_range: start_slice = time - relativedelta(hours=60) end_slice = time + relativedelta(hours=7) # 检查切片范围是否在数据索引内 if start_slice < stand_data_full.index.min() or end_slice > stand_data_full.index.max(): print(f"跳过超出数据范围的时间: {time}") continue # 后续的模型拟合代码...
最终修改后的代码片段
把以上修改整合后,你的代码应该类似这样:
import statsmodels.api as sm import pandas as pd from dateutil.relativedelta import relativedelta from tqdm import tqdm_notebook # 确保数据索引单调递增 stand_data_full = stand_data_full.sort_index() assert stand_data_full.index.is_monotonic_increasing, "数据索引必须单调递增" # 时间序列范围:2016.04.30 23:00 至 调整后的不越界时间 start_range = '2016-04-30 23:00:00' max_data_time = stand_data_full.index.max() end_range = max_data_time - relativedelta(hours=7) # 调整结束时间 time_range = pd.date_range(start_range, end_range, freq='H') print(f"调整后的time_range长度: {len(time_range)}") forecast_juny = [] R = len(selected_cells) for cell in tqdm_notebook(selected_cells): for time in time_range: start_slice = time - relativedelta(hours=60) end_slice = time + relativedelta(hours=7) n_kluster = labels[selected_cells.index(cell)] # 聚类编号 zone = typical_zones[n_kluster] # 聚类对应的典型单元格 param = best_param[zone] # 使用.loc进行可靠的切片 endog_data = stand_data_full.loc[start_slice:end_slice, cell] exog_data = stand_data_full.loc[start_slice:end_slice, feature_list] model_fitted = sm.tsa.statespace.SARIMAX( endog=endog_data, exog=exog_data, order=(param[0], d, param[1]), seasonal_order=(param[2], D, param[3], S) ).filter(best_stand_model[zone].params) predicted_data = model_fitted.predict(time + relativedelta(hours=1), time + relativedelta(hours=6), dynamic=True) # 反标准化转换 predicted_data = predicted_data * data_std[cell] + data_mean[cell] forecast_juny.append((time, predicted_data)) print('len(forecast_juny):', len(forecast_juny))
这样修改后,应该能解决你遇到的索引和越界问题,顺利得到预期的len(forecast_juny): 72930结果。
内容的提问来源于stack exchange,提问作者Ilya Ivanov

