Prophet:5 行写一个还不错的时间序列预测(替代手动调 ARIMA)

起因

老板让"预测下季度营收"。专业的 forecasting 上 ARIMA / SARIMA / state
space model 调一周;我们要的是"现在马上有个基础数字 + 不太离谱"。

Facebook (Meta) 开源的 Prophet 是 GAM (Generalized Additive Model)
风格的 time series 工具,对"商业数据特征 (趋势 + 周期 + 节假日 + 异常点)"
极友好,几乎零调参就有不错效果。

uv add prophet
# Mac M 系列 / Win 装 prophet 偶尔遇 cmdstanpy 编译问题
# 解决:先 conda install cmdstanpy 再 pip install prophet

5 分钟 demo

import pandas as pd
from prophet import Prophet

# 数据格式:必须两列 ds (datetime) + y (value)
df = pd.read_csv('daily_revenue.csv')
# date,revenue
# 2023-01-01,1234.5
# 2023-01-02,1289.1
# ...
df.columns = ['ds', 'y']

# 模型
m = Prophet()
m.fit(df)

# 预测未来 90 天
future = m.make_future_dataframe(periods=90)
forecast = m.predict(future)

# yhat = 预测值;yhat_lower/upper = 80% 置信区间
print(forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail())

# 可视化
m.plot(forecast).savefig('forecast.png')
m.plot_components(forecast).savefig('components.png')

输出:

  • forecast.png:历史 + 预测曲线 + 置信带
  • components.png:拆解成 trend / weekly / yearly 三个成分

调参为 0。

加节假日

中国春节 / 双 11 等是营业额异常点,加进 model 让预测更准:

holidays = pd.DataFrame({
    'holiday': 'major_sale',
    'ds': pd.to_datetime([
        '2023-11-11', '2024-11-11',     # 双 11
        '2023-06-18', '2024-06-18',     # 618
    ]),
    'lower_window': -1,   # 节前 1 天也算
    'upper_window': 2,    # 节后 2 天也算
})

m = Prophet(holidays=holidays)
m.fit(df)

或者内置节假日:

m.add_country_holidays(country_name='CN')
# 自动加入春节 / 国庆 / 五一等

多季节性

默认 yearly + weekly + daily。要月度 / 自定义周期:

m = Prophet()
m.add_seasonality(name='monthly', period=30.5, fourier_order=5)
m.fit(df)

fourier_order 是季节性的"灵活度"(越大越能拟合复杂周期;过高 overfit)。

外部回归量(exogenous)

业务量受外部因素影响:

df['ads_spend'] = ...   # 广告投入
df['is_promotion'] = ... # 0/1 促销标志

m = Prophet()
m.add_regressor('ads_spend')
m.add_regressor('is_promotion')
m.fit(df)

# 预测时也要提供未来值
future['ads_spend'] = expected_ads_for_next_90d
future['is_promotion'] = expected_promo_flag
forecast = m.predict(future)

广告 / 促销作为协变量进 model,预测更贴合业务现实。

异常点

Prophet 对异常点(COVID 期间 / 单日大促)相对鲁棒(用 GAM + 平滑)。
不需要手工剔除。要进一步控制可以:

# 把已知异常段 mark 为 NaN 让 Prophet 忽略
df.loc[(df['ds'] >= '2020-02-01') & (df['ds'] <= '2020-05-01'), 'y'] = None
m.fit(df)

或者用 add_seasonality 处理"上下界" 让 trend 不被极端值影响。

cross-validation 评估

from prophet.diagnostics import cross_validation, performance_metrics

# initial 365 天训练,每 30 天滚动一次,预测 horizon=30 天
df_cv = cross_validation(m, initial='365 days', period='30 days', horizon='30 days')

metrics = performance_metrics(df_cv)
print(metrics[['horizon', 'mape', 'rmse']].head())
#   horizon       mape       rmse
# 0  1 days      0.08      102.3
# 1  2 days      0.09      105.1
# ...

MAPE (Mean Absolute Percentage Error) < 10% 在大多数商业数据是
"还行",5% 算优秀。

与替代品对比

Prophet ARIMA / SARIMA statsmodels NeuralProphet NHITS / TimeGPT
学习曲线 极低 高(要懂 ACF/PACF)
调参量 几乎 0 几乎 0
自动季节性 手动 手动
节假日 手动 手动
多变量 regressor VAR VAR
高频数据 中(小时级)
长期趋势

简单业务时间序列 → Prophet。
学术 / 严谨需求 → ARIMA / state space。
现代深度方法 → NeuralProphet / Darts / Nixtla statsforecast。

真实业务案例

我们用 Prophet 做月度营收预测:

  • 训练数据:2 年日级营收
  • 加节假日:春节 + 双 11 + 618
  • 外部回归:广告预算 + 促销日
  • horizon:90 天

vs 之前的"人拍" 预测:

人拍 Prophet Prophet + 节假日 + regressor
MAPE 25% 12% 7%
工时 4 hour/月 10 min/月 30 min/月

不仅准、还省时。

输出多 model + ensemble

# 跑 3 个不同 changepoint_prior_scale 取均值
forecasts = []
for cps in [0.001, 0.05, 0.5]:
    m = Prophet(changepoint_prior_scale=cps)
    m.fit(df)
    forecasts.append(m.predict(future)['yhat'])

ensemble = pd.concat(forecasts, axis=1).mean(axis=1)

不同超参对不同部分敏感;平均通常更稳。

何时不该用 Prophet

  • 特别短的时间序列(< 2 季):信号不够,model 拟合差
  • 复杂多变量交互:要用 LightGBM / 神经网络
  • 极高频(毫秒级)实时:Prophet 慢,用 specialized 工具
  • 概率性预测要严格 calibrated:bootstrap interval 仅近似

踩过的坑

  1. 列名必须 ds / y:用了别的名 Prophet 不识别。df.rename(...) 后再 fit。

  2. datetime 时区:Prophet 内部假设 naive datetime(无时区)。
    带 tz 会报错。df['ds'] = df['ds'].dt.tz_localize(None)

  3. make_future_dataframe(periods=90, freq='D'):默认日级;
    月级数据要 freq='M'。漏指定就把月数据当日数据预测。

  4. logistic growth 需要 cap:用 growth='logistic' 时必须给
    cap 列(上界),否则报错。

  5. changepoint 自动检测不靠谱:业务有大转折(疫情 / 公司战略
    变化)时,手动指定:
    python m = Prophet(changepoints=['2020-02-01', '2022-06-15'])

精确评价 共 0 人评价
可复现性
可复现 · 0 不可复现 · 0
文风
文风流畅 · 0 文风晦涩 · 0
立场
支持 · 0 反对 · 0

登录后即可对本帖作出评价。

评论区 0 条 · 所有人可在此交流

登录后参与评论。

还没有评论,来说两句。