本节摘要:趋势是时间序列在较长时间窗口内的总体走向,可以是线性、指数、S 形甚至分段式。本节给出趋势的工程定义、三种典型形态的可视化对照,并演示移动平均如何从"噪声"中提取"方向"。
阅读完本节,你应当能够:
本节从"长方向"切入:先给趋势下个工程定义(尺度相对),再用代码合成三种典型形态并对照,最后用一段含噪数据演示 MA(7/30/90) 三种窗口的"平滑-滞后"权衡。
教科书里对趋势的定义是"长期内单调或准单调的方向变化"。但实战中更精确的描述是:趋势 = 时间序列中周期长于业务关注周期的成分。如果你关心的是"明年销量"(关注周期 ~1 年),那"三年滚动平均"就是趋势;如果你关心的是"下季度销量"(关注周期 ~3 个月),"三个月滚动平均"就够看。
换句话说,趋势的尺度是相对的。没有绝对的趋势,只有"对你这个目标来说够长的趋势"。这条认知对窗口选择至关重要:MA(7) 看的是周内的趋势,MA(30) 看的是月度趋势,MA(365) 看的是年度趋势。
下面用一段 numpy 代码合成三种典型形态,并画出对照图:
import numpy as np import matplotlib.pyplot as plt n = 200 t = np.arange(n) linear = 0.5 * t + 10 # 线性:每期 +0.5 exp_grow = 10 * np.exp(0.02 * t) # 指数:每期约 +2% sigmoid = 100 / (1 + np.exp(-0.05 * (t - 100))) # S 形:早期慢、中期快、后期饱和 fig, axes = plt.subplots(1, 3, figsize=(15, 3.5)) for ax, y, title in zip(axes, [linear, exp_grow, sigmoid], ["线性趋势", "指数趋势", "S 形趋势"]): ax.plot(t, y) ax.set_title(title) plt.tight_layout()
从图上可以直观看到:
💡 关键直觉:选错趋势形态是预测误差的常见来源。早期 SaaS 公司用线性外推会低估 10 倍,用指数外推会在饱和阶段爆雷;选 S 形或 Gompertz 曲线更稳。
工程上最朴素的趋势提取是简单移动平均(Simple Moving Average, SMA):
import pandas as pd import numpy as np rng = np.random.default_rng(0) n = 365 trend = 0.05 * np.arange(n) # 真实趋势 noise = rng.normal(0, 1, n) # 噪声 seasonal = 3 * np.sin(2 * np.pi * np.arange(n) / 7) # 周季节性 y = trend + seasonal + noise s = pd.Series(y, index=pd.date_range("2024-01-01", periods=n)) # 窗口 = 7:抹平周季节性,看日趋势 s_ma7 = s.rolling(window=7, center=True).mean() # 窗口 = 30:抹平日波动,看月趋势 s_ma30 = s.rolling(window=30, center=True).mean() # 窗口 = 90:抹平短期波动,看季度趋势 s_ma90 = s.rolling(window=90, center=True).mean()
center=True 让窗口对称(中心移动平均),这样趋势线不会整体往右偏 1/2 窗口长度。窗口越大,曲线越平滑,但对趋势的"响应"也越迟钝——这是趋势估计的"平滑-滞后"权衡,没有银弹。
工程上更推荐两种改进:
s.ewm(span=14).mean(),权重按指数衰减,既平滑又对最新数据敏感。statsmodels 里的 Holt-Winters 就是 EWMA 的多变量升级版。最后列出三个会让"单一趋势"假设失效的工程场景:
changepoint_prior_scale 自动检测变点。rolling(window=W, center=True).mean();窗口越大越平滑、但响应越迟钝。拿到一条曲线,先做 3 件事判别"有没有趋势、是什么趋势":
这 3 步走完,"是什么趋势"基本就能确定了。具体的"用什么模型"留到第三章。