你的时间序列真的平稳吗?手把手教你用ADF检验(Dickey-Fuller)和滚动统计为预测模型打好基础
时间序列平稳性诊断实战从理论到Python实现时间序列分析中平稳性检验是建模前的关键步骤。许多经典预测模型如ARIMA都建立在数据平稳的假设之上。但现实中的时间序列往往带有趋势或季节性直接建模会导致预测失效。本文将系统讲解平稳性的核心概念并手把手演示如何通过ADF检验和滚动统计方法诊断时间序列的平稳性。1. 平稳性的本质与分类1.1 严平稳与宽平稳严平稳要求序列的所有统计特性包括高阶矩都不随时间变化。数学上对任意时间位移τ联合概率分布满足F_{X}(x_{t_1},...,x_{t_k}) F_{X}(x_{t_1τ},...,x_{t_kτ})宽平稳弱平稳则只需满足三个条件均值恒定E[X_t] μ与t无关方差恒定Var(X_t) σ²有限且不变自协方差仅与时滞相关Cov(X_t, X_s) γ(|t-s|)实际分析中通常使用宽平稳标准因其更易检验且大多数模型只需满足弱平稳条件1.2 非平稳序列的典型特征特征类型表现形式对建模的影响趋势性长期上升/下降导致伪回归问题季节性周期性波动预测出现系统性偏差结构突变统计特性突然变化模型参数失效2. 视觉诊断滚动统计法2.1 移动窗口技术实现import pandas as pd import matplotlib.pyplot as plt def plot_rolling_stats(series, window12): 绘制滚动统计量 # 计算滚动统计量 rolling_mean series.rolling(windowwindow).mean() rolling_std series.rolling(windowwindow).std() # 创建画布 plt.figure(figsize(12, 6)) # 绘制原始序列 plt.plot(series, colorblue, labelOriginal) plt.plot(rolling_mean, colorred, labelRolling Mean) plt.plot(rolling_std, colorblack, labelRolling Std) plt.legend(locbest) plt.title(Rolling Mean Standard Deviation) plt.show() # 示例使用 from statsmodels.datasets import co2 data co2.load().data plot_rolling_stats(data[co2], window24)2.2 解读要点平稳序列滚动均值和标准差应在小范围内波动存在趋势滚动均值呈现明显上升/下降方差非恒定滚动标准差呈现规律性变化窗口大小的选择对于月度数据通常选择12或24季度数据选择4。窗口过大会平滑过多细节过小则波动剧烈难以判断3. 统计检验ADF测试详解3.1 ADF检验的数学模型ADF检验通过以下回归模型进行Δy_t α βt γy_{t-1} δ_1Δy_{t-1} ... δ_pΔy_{t-p} ε_t其中关键假设检验为H₀: γ 0 存在单位根序列非平稳H₁: γ 0 序列平稳3.2 Python实现与结果解读from statsmodels.tsa.stattools import adfuller def adf_test(series, regressionc): 执行ADF检验并格式化输出 result adfuller(series, regressionregression) print(ADF Statistic: %f % result[0]) print(p-value: %f % result[1]) print(Critical Values:) for key, value in result[4].items(): print(\t%s: %.3f % (key, value)) if result[1] 0.05: print(未能拒绝原假设 - 序列可能非平稳) else: print(拒绝原假设 - 序列可能是平稳的) # 检验CO2数据 adf_test(data[co2].dropna())典型输出结果解析ADF Statistic: -2.312 p-value: 0.167 Critical Values: 1%: -3.440 5%: -2.866 10%: -2.569判断规则ADF统计量临界值绝对值比较→ 拒绝H₀p-value 0.05 → 拒绝H₀3.3 检验类型选择回归类型模型包含项适用场景c常数项无趋势序列ct常数趋势项有趋势序列nc无附加项罕见理论分析4. 非平稳序列的转换方法4.1 差分处理一阶差分公式diff_1 series.diff().dropna()季节性差分以12个月为例diff_seasonal series.diff(12).dropna()4.2 对数转换适用于指数增长趋势log_series np.log(series)4.3 组合策略# 先对数再差分 transformed np.log(series).diff().dropna()转换效果评估矩阵方法适用场景优点缺点一阶差分线性趋势简单直接可能过度差分季节性差分周期性波动消除季节影响需要知道周期长度对数变换指数趋势稳定方差对零值敏感5. 进阶诊断KPSS与PP检验5.1 KPSS检验与ADF检验互为补充from statsmodels.tsa.stattools import kpss def kpss_test(series, regressionc): result kpss(series, regressionregression) print(KPSS Statistic: %f % result[0]) print(p-value: %f % result[1]) print(Critical Values:) for key, value in result[3].items(): print(\t%s: %.3f % (key, value))5.2 组合判断策略ADF结果KPSS结果结论拒绝H₀不拒绝H₀确定平稳不拒绝H₀拒绝H₀确定非平稳都拒绝H₀都拒绝H₀需进一步分析6. 实战案例股票价格序列分析以苹果公司股价为例import yfinance as yf # 获取数据 aapl yf.download(AAPL, start2020-01-01, end2023-01-01)[Adj Close] # 可视化分析 plot_rolling_stats(aapl, window30) # ADF检验 adf_test(aapl, regressionct) # 转换后检验 log_returns np.log(aapl).diff().dropna() adf_test(log_returns)关键发现原始股价序列ADF检验p值为0.98强烈非平稳对数收益率序列p值0.01可视为平稳7. 建模前的完整性检查清单视觉检查绘制原始序列和滚动统计量ADF检验选择适当的回归类型转换验证每次转换后重新检验残差诊断建模后检查残差是否白噪声def full_diagnostic(series): 完整诊断流程 # 1. 绘制原始序列 plt.figure(figsize(12,4)) plt.subplot(121) series.plot(titleOriginal Series) # 2. 绘制滚动统计 plt.subplot(122) plot_rolling_stats(series) # 3. 执行ADF检验 print(\nADF Test Results:) adf_test(series) # 4. 执行KPSS检验 print(\nKPSS Test Results:) kpss_test(series)在真实项目中我发现同时使用多种检验方法可以避免单一方法的局限性。特别是对于金融时间序列结合ADF和KPSS检验能给出更可靠的结论。当检验结果出现矛盾时建议优先考虑ADF结果并辅以视觉分析。