PAYATHON 2026

Prophet 预测累计值为什么会低于最后观测值?

数据小许

结论

Prophet 没有通用的“累计”选项,也不保证预测值从最后一个实际观测值开始,或始终递增。它根据拟合出的趋势和季节性生成预测,所以预测值可能低于最后一个观测值。

你给出的预测日期还有一个细节:pd.date_range(str(last_date), freq="D", periods=horizon) 会把 last_date 本身包含在预测范围内。这样,首个预测值对应的是最后观测日期,可能是模型对该日期的拟合估计,而非对次日的预测。

从下一天开始预测

从最后观测日期的次日开始生成预测日期:

future_dates = pd.date_range(
    start=last_date + pd.Timedelta(days=1),
    freq="D",
    periods=horizon,
)
fh = ForecastingHorizon(future_dates, is_relative=False)
y_pred = forecaster.predict(fh)

这样可以避免把最后一天的拟合值当作次日预测。不过,未来预测仍可能低于最后观测值,因为 Prophet 本身没有单调递增约束。

需要强制预测不下降时

如果业务规则要求预测累计值不低于当前值,并且逐日不下降,可以在预测后进行单调化处理:

last_value = forecast_df.iloc[-1]
y_pred = y_pred.sort_index().clip(lower=last_value).cummax()

这段处理会将预测值限制在最后观测值之上,并确保后续预测不低于此前的预测值。它属于后处理,会改变模型的原始预测结果。如果同时展示置信区间,也应说明区间不会因此自动获得统计意义上的单调约束。

如果希望预测过程本身符合累计逻辑,可以先预测每日新增量,再把非负新增量累加为累计值。每日新增量的预测也可能为负,通常需要按业务规则处理,因此这种做法同样会引入模型之外的约束。

检查日期与缺失值处理

代码中的 pd.to_datetime(df.index) 没有将转换结果赋回索引。如果索引还不是日期类型,应显式转换:

df.index = pd.to_datetime(df.index)

此外,resample('D').bfill() 会用后续日期的值填补缺失日期。对于累计序列,这可能把未来观测值填到更早的日期,进而扭曲训练数据。请根据数据含义选择填充方式,并确认 Total_Issues 确实表示按日期统计的累计数量。

备注:内容仅供参考。