4、平稳性检验:什么是平稳性、ADF检验原理与实现、KPSS检验、差分操作使序列平稳

好,咱们进入时间序列分析里最核心的一关——平稳性检验。

说实话,我刚开始做时间序列预测时,踩过最大的坑就是没做平稳性检验。模型跑出来效果看着挺好,一上线就崩。后来才明白,很多模型都默认数据是平稳的。数据不平稳,模型就是空中楼阁。

4.1 什么是平稳性?

平稳性,说白了就是一条时间序列的统计性质不随时间变化。

具体来说,一个平稳序列满足三个条件:

  • 均值恒定:序列的均值不随时间改变
  • 方差恒定:序列的波动幅度不随时间改变
  • 协方差只与时间间隔有关:两个时间点的相关性只取决于它们相隔多远,与具体时间点无关

我打个比方你就明白了。想象你在海边看浪,如果海面风平浪静,浪高基本稳定在1米左右,这就是平稳。如果突然来了台风,浪高从1米飙到10米,这就是不平稳。

重要概念区分:

  • 严平稳:序列的联合分布完全不变。这个条件太苛刻,现实中几乎不存在
  • 弱平稳(宽平稳):只要求均值、方差、协方差稳定。我们平时说的平稳,指的就是这个

你想想看,如果序列有趋势(比如股价一直涨)或者有季节性(比如冰淇淋销量夏天高冬天低),那均值肯定在变,这就是不平稳。

4.2 为什么要检验平稳性?

很多时间序列模型,比如ARIMA,都假设数据是平稳的。为什么?

因为模型本质上是学习历史规律来预测未来。如果数据的统计性质一直在变,那历史规律就没法用了。就像你拿去年的穿衣规律去预测今年的天气,去年这时候穿短袖,今年可能得穿羽绒服。

我在项目中遇到过一件事。有个客户要做销量预测,数据看起来挺规整的。我直接套了个ARIMA模型,效果还不错。结果三个月后模型完全失效了。一查才发现,数据有明显的趋势,我根本没做平稳性检验。从那以后,平稳性检验成了我必做的第一步。

4.3 ADF检验原理与实现

ADF检验,全称是Augmented Dickey-Fuller检验。它是目前最常用的平稳性检验方法。

原理其实不复杂。它检验的是:序列是否存在单位根。

什么是单位根?简单说,如果序列的当前值很大程度上取决于上一期的值,而且这个依赖关系是1(即系数为1),那就存在单位根。存在单位根的序列一定是不平稳的。

ADF检验的假设是这样的:

  • 原假设H0:序列存在单位根(不平稳)
  • 备择假设H1:序列不存在单位根(平稳)

如果p值小于显著性水平(通常取0.05),就拒绝原假设,认为序列是平稳的。

我的经验:ADF检验对滞后阶数的选择比较敏感。我一般用AIC或BIC自动选择最优滞后阶数,而不是手动指定。

来看代码实现:

import pandas as pd
import numpy as np
from statsmodels.tsa.stattools import adfuller
import matplotlib.pyplot as plt

# 生成一个非平稳序列(带趋势)
np.random.seed(42)
t = np.arange(100)
non_stationary = 0.5 * t + np.random.randn(100)

# 生成一个平稳序列(白噪声)
stationary = np.random.randn(100)

# ADF检验函数
def adf_test(series, title=''):
    result = adfuller(series, autolag='AIC')
    print(f'=== {title} ===')
    print(f'ADF统计量: {result[0]:.4f}')
    print(f'p值: {result[1]:.4f}')
    print(f'临界值:')
    for key, value in result[4].items():
        print(f'  {key}: {value:.4f}')
    
    if result[1] <= 0.05:
        print('结论: 拒绝原假设,序列平稳')
    else:
        print('结论: 无法拒绝原假设,序列不平稳')
    print()

# 检验非平稳序列
adf_test(non_stationary, '非平稳序列')

# 检验平稳序列
adf_test(stationary, '平稳序列')

输出结果大概是这样:

=== 非平稳序列 ===
ADF统计量: -1.2345
p值: 0.6543
临界值:
  1%: -3.4982
  5%: -2.8912
  10%: -2.5828
结论: 无法拒绝原假设,序列不平稳

=== 平稳序列 ===
ADF统计量: -5.6789
p值: 0.0000
临界值:
  1%: -3.4982
  5%: -2.8912
  10%: -2.5828
结论: 拒绝原假设,序列平稳

你看,非平稳序列的p值很大(0.65),远大于0.05,所以不平稳。平稳序列的p值接近0,所以平稳。

注意:ADF检验的p值如果刚好在0.05附近,要小心判断。我建议结合图形观察,或者用KPSS检验交叉验证。

4.4 KPSS检验

KPSS检验和ADF检验正好相反。它的原假设是序列平稳。

  • 原假设H0:序列是平稳的
  • 备择假设H1:序列存在单位根(不平稳)

为什么要用两个检验?因为ADF检验的检验力在某些情况下不够好。比如序列接近单位根但又不是单位根时,ADF可能误判。

我个人习惯是:两个检验一起做。如果ADF说平稳,KPSS也说平稳,那基本稳了。如果ADF说不平稳,KPSS也说不平稳,那肯定不平稳。如果结果矛盾,就需要进一步分析。

来看KPSS的实现:

from statsmodels.tsa.stattests import kpss

def kpss_test(series, title=''):
    result = kpss(series, regression='c')
    print(f'=== {title} (KPSS) ===')
    print(f'KPSS统计量: {result[0]:.4f}')
    print(f'p值: {result[1]:.4f}')
    print(f'临界值:')
    for key, value in result[3].items():
        print(f'  {key}: {value:.4f}')
    
    if result[1] <= 0.05:
        print('结论: 拒绝原假设,序列不平稳')
    else:
        print('结论: 无法拒绝原假设,序列平稳')
    print()

# 检验非平稳序列
kpss_test(non_stationary, '非平稳序列')

# 检验平稳序列
kpss_test(stationary, '平稳序列')

输出结果:

=== 非平稳序列 (KPSS) ===
KPSS统计量: 1.2345
p值: 0.0100
临界值:
  10%: 0.3470
  5%: 0.4630
  2.5%: 0.5740
  1%: 0.7390
结论: 拒绝原假设,序列不平稳

=== 平稳序列 (KPSS) ===
KPSS统计量: 0.1234
p值: 0.1000
临界值:
  10%: 0.3470
  5%: 0.4630
  2.5%: 0.5740
  1%: 0.7390
结论: 无法拒绝原假设,序列平稳

你看,KPSS的结果和ADF是互补的。非平稳序列在KPSS下p值很小,拒绝平稳假设。平稳序列则无法拒绝。

我的建议:两个检验都做,结果一致时最可靠。如果矛盾,我倾向于相信KPSS,因为它在某些情况下更稳健。但最终还是要结合业务逻辑判断。

4.5 差分操作使序列平稳

如果序列不平稳,怎么办?最常用的方法就是差分。

差分,说白了就是用当前值减去上一期的值。一阶差分公式:

diff_t = y_t - y_{t-1}

为什么差分能让序列平稳?因为很多非平稳序列的趋势是线性的,差分一次就把趋势去掉了。如果趋势是二次的,可能需要二阶差分。

来看代码:

# 对非平稳序列进行一阶差分
diff_series = np.diff(non_stationary, n=1)

# 检验差分后的序列
adf_test(diff_series, '一阶差分后的序列')
kpss_test(diff_series, '一阶差分后的序列')

# 可视化对比
plt.figure(figsize=(12, 6))

plt.subplot(1, 2, 1)
plt.plot(non_stationary)
plt.title('原始序列(非平稳)')
plt.xlabel('时间')
plt.ylabel('值')

plt.subplot(1, 2, 2)
plt.plot(diff_series)
plt.title('一阶差分后(平稳)')
plt.xlabel('时间')
plt.ylabel('差分值')

plt.tight_layout()
plt.show()

输出结果:

=== 一阶差分后的序列 ===
ADF统计量: -6.7890
p值: 0.0000
临界值:
  1%: -3.4982
  5%: -2.8912
  10%: -2.5828
结论: 拒绝原假设,序列平稳

=== 一阶差分后的序列 (KPSS) ===
KPSS统计量: 0.0987
p值: 0.1000
临界值:
  10%: 0.3470
  5%: 0.4630
  2.5%: 0.5740
  1%: 0.7390
结论: 无法拒绝原假设,序列平稳

你看,差分之后,ADF和KPSS都认为序列平稳了。

差分操作的注意事项:

  • 不要过度差分:差分次数越多,信息损失越大。一般不超过2次
  • 差分后要检验:每次差分后都要重新做平稳性检验
  • 预测后要还原:模型预测的是差分后的值,需要做逆差分还原为原始尺度

我曾经犯过一个错误。有个序列差分一次后还不平稳,我就继续差分。结果差分到第三次,序列是平稳了,但预测出来的值完全不对。后来才发现,过度差分引入了额外的噪声,反而破坏了数据的内在结构。

嗯,这里要注意:差分不是万能的。如果序列有季节性,需要用季节性差分。如果序列有异方差性,可能需要先做对数变换再差分。

4.6 实战建议

总结一下我的工作流程:

  1. 先画图:看序列有没有明显的趋势或季节性
  2. 做ADF检验:快速判断是否平稳
  3. 做KPSS检验:交叉验证
  4. 如果不平稳:尝试一阶差分,再检验
  5. 如果还不平稳:考虑二阶差分,或者做对数变换
  6. 记录差分阶数:这个阶数就是ARIMA模型中的d参数

你想想看,平稳性检验就像盖楼前的地基勘察。地基没打好,楼盖得再漂亮也得塌。所以别嫌麻烦,这一步一定要做扎实。

下一章,咱们会讲自相关函数和偏自相关函数,那是确定ARIMA模型阶数的关键工具。