Prophet时序预测中增减数据为何影响历史yhat?如何解决?
问题解答
1. 这种行为完全正常
Prophet是基于全量输入数据拟合的时间序列模型,它会用所有数据来学习趋势增长率、季节性波动(年/月周期)、节假日效应等核心参数。哪怕你只删掉最后一个月的数据,整个数据集的统计特征(比如整体均值、季节波动幅度)都会发生细微变化,导致模型重新拟合出的参数不同——最终所有时间点的yhat(包括历史时段的拟合值)都会跟着变。
你看到的历史yhat其实是模型对历史数据的回溯拟合值,不是“仅用该点之前数据做出的预测值”。默认的predict方法是用全量训练好的模型去回测所有时间点,所以只要训练数据变了,所有拟合值都会调整。
2. 实现“每个月yhat仅依赖历史数据”的方法
要满足这个需求,你需要做滚动时间序列预测(Walk Forward Validation):对每个时间点,只用该点之前的历史数据训练模型,再预测该点的值。具体实现代码如下:
import pandas as pd from prophet import Prophet pd.set_option('display.float_format', lambda x: '%.2f' % x) # 读取数据 json_data = '{"ds":{"0":"2017-10-01","1":"2017-11-01","2":"2017-12-01","3":"2018-01-01","4":"2018-02-01","5":"2018-03-01","6":"2018-04-01","7":"2018-05-01","8":"2018-06-01","9":"2018-07-01","10":"2018-08-01","11":"2018-09-01","12":"2018-10-01","13":"2018-11-01","14":"2018-12-01","15":"2019-01-01","16":"2019-02-01","17":"2019-03-01","18":"2019-04-01","19":"2019-05-01","20":"2019-06-01","21":"2019-07-01","22":"2019-08-01","23":"2019-09-01","24":"2019-10-01","25":"2019-11-01","26":"2019-12-01","27":"2020-01-01","28":"2020-02-01","29":"2020-03-01","30":"2020-04-01","31":"2020-05-01","32":"2020-06-01","33":"2020-07-01","34":"2020-08-01","35":"2020-09-01","36":"2020-10-01","37":"2020-11-01","38":"2020-12-01","39":"2021-01-01","40":"2021-02-01","41":"2021-03-01","42":"2021-04-01","43":"2021-05-01","44":"2021-06-01","45":"2021-07-01","46":"2021-08-01","47":"2021-09-01","48":"2021-10-01","49":"2021-11-01","50":"2021-12-01","51":"2022-01-01","52":"2022-02-01","53":"2022-03-01","54":"2022-04-01","55":"2022-05-01","56":"2022-06-01","57":"2022-07-01","58":"2022-08-01","59":"2022-09-01","60":"2022-10-01","61":"2022-11-01"},"y":{"0":3065,"1":3127,"2":8506,"3":2527,"4":2376,"5":2753,"6":2964,"7":3750,"8":4445,"9":3502,"10":3968,"11":3195,"12":3232,"13":3377,"14":7823,"15":2452,"16":2563,"17":2747,"18":2877,"19":3617,"20":3620,"21":4044,"22":3491,"23":2853,"24":3447,"25":3346,"26":7835,"27":2543,"28":2412,"29":1860,"30":759,"31":3630,"32":2216,"33":1247,"34":4455,"35":3178,"36":3502,"37":3475,"38":7311,"39":2296,"40":2136,"41":1717,"42":2200,"43":3764,"44":3697,"45":4007,"46":3566,"47":3043,"48":3457,"49":3256,"50":8564,"51":2218,"52":2815,"53":3389,"54":3816,"55":4853,"56":4406,"57":3859,"58":4152,"59":3421,"60":3965,"61":3590}}' data = pd.read_json(json_data) data['ds'] = pd.to_datetime(data['ds']) def walk_forward_predict(df, min_train_months=12): results = [] # 从有足够训练数据的时间点开始滚动 for end_idx in range(min_train_months, len(df) + 1): # 取当前时间点之前的所有数据做训练集 train_data = df.iloc[:end_idx] # 训练模型 model = Prophet().fit(train_data) # 预测当前时间点的值 future_df = pd.DataFrame({'ds': [train_data.iloc[-1]['ds']]}) forecast = model.predict(future_df) # 保存结果 results.append({ 'ds': forecast['ds'].iloc[0], 'yhat_walk_forward': forecast['yhat'].iloc[0], 'y': train_data.iloc[-1]['y'] }) return pd.DataFrame(results) # 执行滚动预测,至少用12个月的数据训练模型 walk_forward_df = walk_forward_predict(data, min_train_months=12) # 对比全量拟合的结果 full_model = Prophet().fit(data) full_forecast = full_model.predict(data)[['ds', 'yhat']].rename(columns={'yhat': 'yhat_full'}) comparison_df = walk_forward_df.merge(full_forecast, on='ds') # 查看10月份的对比结果 print(comparison_df[comparison_df['ds'].dt.month == 10])
关键说明
- 滚动逻辑:每次只使用当前时间点之前的历史数据训练模型,预测该点的值,这样后续数据的增减不会影响之前的预测结果。
- 训练数据量:设置
min_train_months=12是为了让模型能学习到完整的年度季节性特征,你可以根据数据情况调整这个值。 - 性能注意:滚动预测需要多次训练模型,计算量比全量拟合大很多,如果数据集很大,可以考虑并行训练或者减少滚动次数。
内容的提问来源于stack exchange,提问作者jrvidotti
相关产品推荐
相关产品推荐

