1. 时间序列平稳性检验的重要性
在时间序列分析中,平稳性是一个核心概念。与传统的分类和回归问题不同,时间序列数据具有时间依赖性,这意味着我们需要特别关注数据的统计特性是否随时间变化。
一个平稳的时间序列意味着其统计特性(如均值、方差等)不随时间变化。这种特性使得建模和预测变得更加可靠。相反,非平稳时间序列可能包含趋势、季节性或其他时间依赖性结构,这会使得传统统计方法失效。
提示:在实际应用中,大多数经济、金融和商业时间序列都是非平稳的,因此平稳性检验成为建模前的必要步骤。
2. 识别平稳与非平稳时间序列的直观方法
2.1 通过可视化初步判断
最直接的判断方法是绘制时间序列图。平稳时间序列的图形通常表现为围绕一个固定均值波动,而非平稳序列则可能显示出明显的趋势或季节性模式。
以"每日女性新生儿数量"数据集为例:
from pandas import read_csv from matplotlib import pyplot series = read_csv('daily-total-female-births.csv', header=0, index_col=0) series.plot() pyplot.show()这个数据集显示出稳定的波动模式,没有明显的趋势或季节性,初步判断为平稳序列。
相比之下,"航空公司乘客数量"数据集:
series = read_csv('international-airline-passengers.csv', header=0, index_col=0) series.plot() pyplot.show()这个数据集显示出明显的上升趋势和季节性波动,属于典型的非平稳序列。
2.2 统计量检验法
2.2.1 均值-方差分割检验
我们可以将时间序列分成两部分,比较两部分的统计特性:
X = series.values split = round(len(X) / 2) X1, X2 = X[0:split], X[split:] mean1, mean2 = X1.mean(), X2.mean() var1, var2 = X1.var(), X2.var()对于平稳序列,两部分的均值和方差应该相近。例如女性新生儿数据:
mean1=39.76, mean2=44.19 variance1=49.21, variance2=48.71而非平稳的航空公司乘客数据:
mean1=182.90, mean2=377.69 variance1=2244.09, variance2=7367.962.2.2 数据分布检验
检查数据是否符合正态分布也很重要,因为许多统计检验都基于这一假设:
series.hist() pyplot.show()对于非正态分布的数据,可以考虑进行对数变换:
from numpy import log X = log(X) pyplot.hist(X) pyplot.show()3. 统计检验方法:ADF检验
3.1 ADF检验原理
Augmented Dickey-Fuller (ADF)检验是最常用的平稳性检验方法。它检验的原假设是时间序列存在单位根(非平稳),备择假设是不存在单位根(平稳)。
检验结果包括:
- ADF统计量:负值越大,越倾向于拒绝原假设(平稳)
- p值:小于显著性水平(通常0.05)时拒绝原假设
- 临界值:与ADF统计量比较
3.2 Python实现
对于女性新生儿数据:
from statsmodels.tsa.stattools import adfuller result = adfuller(X) 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))输出:
ADF Statistic: -4.808291 p-value: 0.000052 Critical Values: 5%: -2.870 1%: -3.449 10%: -2.571p值远小于0.05,强烈拒绝原假设,表明序列平稳。
对于航空公司乘客数据(原始数据):
ADF Statistic: 0.815369 p-value: 0.991880 Critical Values: 5%: -2.884 1%: -3.482 10%: -2.579p值大于0.05,无法拒绝原假设,序列非平稳。
即使进行对数变换后:
ADF Statistic: -1.717017 p-value: 0.422367 Critical Values: 5%: -2.884 1%: -3.482 10%: -2.579仍然无法拒绝原假设,说明对数变换不足以使该序列平稳。
4. 实际应用中的注意事项
4.1 检验方法的选择
- 对于有明显趋势或季节性的数据,ADF检验可能不够敏感
- 可以结合KPSS检验等其他方法进行综合判断
- 对于高频金融数据,可能需要考虑ARCH效应
4.2 数据预处理
- 对于非平稳数据,常用的处理方法包括:
- 差分(消除趋势)
- 季节差分(消除季节性)
- 对数变换(稳定方差)
- 分段平稳建模
4.3 模型选择
- 对于平稳数据:可以直接使用AR、MA、ARMA等模型
- 对于非平稳数据:可能需要使用ARIMA、SARIMA等模型
- 对于波动聚集现象:考虑GARCH类模型
5. 常见问题与解决方案
5.1 ADF检验结果与图形判断不一致
可能原因:
- 数据存在非线性趋势
- 样本量不足
- 存在结构性断点
解决方案:
- 尝试更高阶差分
- 增加样本量
- 使用子样本检验
5.2 如何处理季节性非平稳
对于季节性数据:
- 先进行季节性差分
- 再进行常规差分
- 重新检验平稳性
# 季节性差分示例 diff = series.diff(12).dropna()5.3 小样本数据的平稳性检验
小样本下ADF检验功效较低,可以:
- 使用更严格的显著性水平
- 结合自相关函数(ACF)和偏自相关函数(PACF)图形判断
- 考虑使用滚动窗口检验
6. 进阶技巧与经验分享
6.1 自动化平稳性检验流程
在实际项目中,可以建立自动化检验流程:
def check_stationarity(series, max_diff=3): """自动检查序列平稳性并返回所需差分阶数""" for i in range(max_diff + 1): if i == 0: current = series.copy() else: current = current.diff().dropna() result = adfuller(current) if result[1] <= 0.05: print(f'序列在{i}阶差分后平稳') return i, current print('在最大差分阶数内未能使序列平稳') return None, None6.2 滚动窗口平稳性检验
对于长时间序列,可以检查其局部平稳性:
def rolling_stationarity_test(series, window_size=100): """滚动窗口平稳性检验""" results = [] for i in range(len(series) - window_size + 1): window = series.iloc[i:i+window_size] p_value = adfuller(window)[1] results.append(p_value) pd.Series(results, index=series.index[window_size-1:]).plot() pyplot.axhline(0.05, color='red') pyplot.show()6.3 多变量平稳性检验
对于多变量时间序列,需要考虑协整关系:
from statsmodels.tsa.vector_ar.vecm import coint_johansen def cointegration_test(series, det_order=0, k_ar_diff=1): """Johansen协整检验""" result = coint_johansen(series, det_order, k_ar_diff) print('特征值:', result.eig) print('临界值:', result.cvt) print('统计量:', result.lr1) return result7. 实际案例分析
7.1 股票价格序列分析
股票价格通常是非平稳的,但收益率序列可能是平稳的:
# 获取股票数据 import yfinance as yf data = yf.download('AAPL', start='2020-01-01', end='2023-01-01') # 原始价格序列检验 result = adfuller(data['Close'].dropna()) print(f'价格序列p值: {result[1]:.4f}') # 通常>0.05 # 收益率序列检验 returns = data['Close'].pct_change().dropna() result = adfuller(returns) print(f'收益率序列p值: {result[1]:.4f}') # 通常<0.057.2 经济指标分析
以GDP数据为例,通常需要进行对数变换和差分:
# 假设gdp_series是GDP时间序列 log_gdp = np.log(gdp_series) dlog_gdp = log_gdp.diff().dropna() # 检验 print('原始GDP:', adfuller(gdp_series.dropna())[1]) print('对数GDP:', adfuller(log_gdp.dropna())[1]) print('对数差分GDP:', adfuller(dlog_gdp.dropna())[1])8. 模型建立前的最后检查
在建立时间序列模型前,建议进行以下检查:
- 可视化检查:观察图形是否有明显趋势/季节性
- ACF/PACF检查:观察自相关函数衰减情况
- 统计检验:至少使用两种不同的平稳性检验方法
- 残差检查:对转换后的序列再次检验
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf def full_diagnostic(series): """完整的时间序列诊断""" # 可视化 series.plot() pyplot.show() # ACF/PACF plot_acf(series, lags=40) pyplot.show() plot_pacf(series, lags=40) pyplot.show() # 平稳性检验 print('ADF检验p值:', adfuller(series)[1]) # 正态性检验 print('Shapiro检验p值:', shapiro(series)[1])9. 处理非平稳序列的实用方法
当确认序列非平稳时,可以考虑以下方法:
9.1 差分法
# 一阶差分 diff1 = series.diff().dropna() # 季节性差分(以12个月为例) seasonal_diff = series.diff(12).dropna()9.2 变换法
常用变换包括:
- 对数变换:
np.log(series) - Box-Cox变换:
from scipy.stats import boxcox - 标准化:
(series - series.mean())/series.std()
9.3 分解法
使用季节性分解:
from statsmodels.tsa.seasonal import seasonal_decompose result = seasonal_decompose(series, model='multiplicative') result.plot() pyplot.show()9.4 模型法
直接使用能处理非平稳数据的模型:
- ARIMA:处理趋势
- SARIMA:处理趋势和季节性
- VAR:多变量非平稳序列
10. 总结与个人实践建议
时间序列平稳性检验是建模的基础步骤,但在实际应用中我发现:
- 不要过度依赖单一检验方法,应该结合多种方法和可视化分析
- 对于边界情况(如p值接近0.05),建议考虑不同差分阶数的影响
- 在金融时间序列中,即使序列平稳,也可能存在波动聚集效应
- 大数据场景下,考虑计算滚动平稳性可能更有意义
最后分享一个实用技巧:在Python中可以使用tsfresh包自动计算大量时间序列特征,包括各种平稳性指标:
from tsfresh import extract_features features = extract_features(pd.DataFrame({'id':1, 'time':range(len(series)), 'value':series}), column_id='id', column_sort='time')这可以快速评估序列的多种统计特性,辅助平稳性判断。