本节摘要:单点预测是"猜中位数",预测区间是"猜分布"。本节区分预测区间(predictive interval)和置信带(confidence interval)两个易混概念,并演示 ARIMA 与 Prophet 的预测区间计算。
阅读完本节,你应当能够:
这两个概念在初学者中常被混用,但数学含义不同:
直观上:
大多数业务场景关心的是预测区间——比如"明天的销量大概在 800–1200 之间"。下文围绕预测区间展开。
statsmodels 的 get_forecast 同时返回预测均值和预测区间:
from statsmodels.tsa.arima.model import ARIMA # 拟合 ARIMA(1,1,1) model = ARIMA(s, order=(1, 1, 1)).fit() # 预测未来 12 期 + 95% 预测区间 forecast = model.get_forecast(steps=12) mean = forecast.predicted_mean ci = forecast.conf_int(alpha=0.05) # alpha=0.05 → 95% 区间 # ci 是 DataFrame,列名 lower/upper 对应区间 print(ci.head())
预测区间宽度随 horizon 增大。horizon=1 时区间最窄,horizon 越大区间越宽——因为预测越远,不确定性累积。
from prophet import Prophet m = Prophet(yearly_seasonality=True, weekly_seasonality=True) m.fit(df) future = m.make_future_dataframe(periods=30, freq="D") forecast = m.predict(future) # 关键列 print(forecast[["ds", "yhat", "yhat_lower", "yhat_upper"]].tail())
yhat_lower / yhat_upper 默认是 80% 区间。要 95%:
m = Prophet(interval_width=0.95).fit(df)
经验法则:
判断区间宽度是否合适的指标:区间覆盖率(interval coverage)——测试集中真实值落在预测区间内的比例。
# 假设 test 是测试集,pred 是预测区间 coverage = ((test >= ci["lower"]) & (test <= ci["upper"])).mean() # 95% 区间 → 覆盖率应接近 95%;偏离太远说明区间校准不好
如果 95% 区间的覆盖率只有 80% → 区间偏窄,需要调大 interval_width 或检查模型残差方差。如果 95% 区间的覆盖率高达 99% → 区间偏宽,可以收紧。
最常见的工程问题:预测区间过窄。ARIMA 默认的预测区间假设残差同方差、独立。如果这两个假设不成立(实际数据常常不成立),区间就会过窄。
解决思路:
# 用 Bootstrap 构造 95% 预测区间 def bootstrap_forecast(model, steps=12, n_sims=1000, alpha=0.05): residuals = model.resid sims = [] for _ in range(n_sims): # 在残差上有放回采样 future_resid = np.random.choice(residuals, size=steps, replace=True) # 假设未来没有新信息,预测是固定的 sim = model.forecast(steps) + future_resid sims.append(sim) sims = np.array(sims) lower = np.percentile(sims, 100 * alpha / 2, axis=0) upper = np.percentile(sims, 100 * (1 - alpha / 2), axis=0) return lower, upper
Bootstrap 区间对非正态残差、异方差都更鲁棒。业务上凡是对预测区间敏感的场景,都建议用 Bootstrap 校准。
回到概念区分:
业务上几乎总是要预测区间(关心"明天具体多少"),但汇报时也提一句"我们对这个预测的把握有多高"——这就是置信带。
get_forecast(steps).conf_int(alpha=0.05)。interval_width=0.95。