本节摘要:指数平滑族是 ARIMA 之外另一类主流时序模型。本节介绍 SES(简单指数平滑)、Holt(带趋势)、Holt-Winters(带趋势 + 季节)三套方法,并用一段带趋势和周季节性的合成数据演示完整拟合。
阅读完本节,你应当能够:
ExponentialSmoothing 拟合数据,并解释 trend / seasonal / seasonal_periods 参数。本节介绍 ARIMA 之外的另一类主流——指数平滑族。读完后你掌握三参数 (α、β、γ) 的工程含义,以及"加法 vs 乘法季节"的选型决策。
SES(Simple Exponential Smoothing)只建模水平项,假设序列没有趋势也没有季节性。递推公式:
ℓ(t) = α y(t) + (1 - α) ℓ(t-1) ŷ(t+h) = ℓ(t)
α ∈ (0, 1) 是平滑系数,控制"对最新观测的权重"。
from statsmodels.tsa.holtwinters import SimpleExpSmoothing, ExponentialSmoothing ses = SimpleExpSmoothing(s, initialization_method="estimated").fit(smoothing_level=0.3) forecast = ses.forecast(12)
SES 适用:没有趋势、没有季节性、只有随机波动的序列(如某些金融资产价格、传感器噪声)。
Holt 在 SES 基础上加了一个趋势项 b(t):
ℓ(t) = α y(t) + (1 - α) (ℓ(t-1) + b(t-1)) b(t) = β (ℓ(t) - ℓ(t-1)) + (1 - β) b(t-1) ŷ(t+h) = ℓ(t) + h b(t)
β 是趋势平滑系数。trend="add" 表示加法趋势;trend="mul" 表示乘法趋势(趋势随水平放大)。
holt = ExponentialSmoothing(s, trend="add").fit()
Holt 适用:有趋势但无季节性的序列(如持续增长的销售额、用户数)。
⚠️ 常见坑:Holt 默认会"无限外推趋势"——预测 10 年后销量会按当前斜率一直涨。实战中要给趋势一个上限,或者用
damped_trend=True让趋势逐渐衰减到 0。
holt_damped = ExponentialSmoothing(s, trend="add", damped_trend=True).fit() # φ 控制趋势衰减速度,φ=0.98 是常用经验值
Holt-Winters 在 Holt 基础上再加季节项 s(t),有三组参数 (α, β, γ) 分别控制水平、趋势、季节的平滑。
# 加法季节 ℓ(t) = α (y(t) - s(t-m)) + (1 - α) (ℓ(t-1) + b(t-1)) b(t) = β (ℓ(t) - ℓ(t-1)) + (1 - β) b(t-1) s(t) = γ (y(t) - ℓ(t-1) - b(t-1)) + (1 - γ) s(t-m) ŷ(t+h) = ℓ(t) + h b(t) + s(t+h-m)
m 是季节周期(7 = 周、12 = 月、24 = 小时数据中的日季节)。
hw = ExponentialSmoothing( s, trend="add", seasonal="add", seasonal_periods=12 ).fit()
Holt-Winters 适用:同时有趋势和季节性的序列(如零售月度销量——上升 + 12 月高峰)。
参考 2.5 节,季节性幅度随水平变化时用乘法:
hw_mul = ExponentialSmoothing( s, trend="add", seasonal="mul", seasonal_periods=12 ).fit()
实战经验:销售类数据用乘法季节;物理量类数据用加法季节。
from statsmodels.datasets import airline data = airline.load_pandas().data data["Month"] = pd.to_datetime(data["Month"]) data = data.set_index("Month") y = data["AirPassengers"] # Holt-Winters 加法趋势 + 乘法季节(航空数据经典配置) hw = ExponentialSmoothing( y, trend="add", seasonal="mul", seasonal_periods=12, initialization_method="estimated" ).fit() # 预测未来 24 个月 forecast = hw.forecast(24) print(hw.summary().tables[1]) # 拟合参数 α、β、γ
输出参数表里会有 smoothing_level (α)、smoothing_trend (β)、smoothing_seasonal (γ) 三个值。每个都在 0–1 之间,接近 0 表示对应成分稳定,接近 1 表示新数据贡献大。
| 维度 | ARIMA | 指数平滑 |
|---|---|---|
| 推导方式 | 统计(自相关、平稳性) | 启发式(递推、误差反馈) |
| 季节性 | 显式季节项 | 显式季节项 |
| 趋势 | 隐式(通过差分) | 显式(独立参数) |
| 阻尼趋势 | 需手动 | damped_trend=True |
| 预测区间 | 严格推导 | 经验区间 |
| 数据需求 | 至少 50 个观测 | 至少 2 个完整周期 |
工程选择:
如果数据是工作日数据,"季节周期"是 5 而不是 7。如果错选 7,模型会试图学"7 天后的同一天",但工作日没有 7 天周期——预测会显著偏差。
# 错误:业务上只有工作日,但选了 m=7 hw_wrong = ExponentialSmoothing(workday_sales, seasonal="add", seasonal_periods=7).fit() # 正确:m=5 匹配业务周期 hw_right = ExponentialSmoothing(workday_sales, seasonal="add", seasonal_periods=5).fit()
damped_trend=True 让趋势不无限外推。