📑 查看全课大纲(第 4 / 20 节)
- 1.时间序列分析简介
- 2.平稳性检验与特征
- 3.纯随机性检验(白噪声检验)
- 4.时间序列预处理代码实战
- 5.自回归模型(AR 模型)
- 6.自相关系数与偏自相关系数
- 7.移动平均模型(MA)与自回归移动平均模型(ARMA)
- 8.平稳时序模型识别与参数估计
- 9.模型显著性检验、优化与序列预测
- 10.平稳时间序列建模代码实战
- 11.确定性序列分解与趋势分析
- 12.季节效应分析与综合波动分析
- 13.确定性时序分析代码实战
- 14.差分平稳化与 ARIMA 模型
- 15.残差自回归模型
- 16.ARCH / GARCH 模型及其衍生
- 17.异方差检验:Portmanteau Q 检验与 LM 检验
- 18.随机性非平稳建模与 GARCH 实战
- 19.ARIMAX 模型与单位根(DF/ADF)检验
- 20.协整检验与误差修正模型(ECM)
时间序列预处理代码实战
约 31 分钟
小象实战讲义 · 时间序列分析
时间序列分析的核心在于从看似杂乱的数据中提取规律性信息。本节我们将通过Python实战,系统掌握时间序列预处理的两大基石:平稳性检验与纯随机性检验。学完本节,你将能够使用Python对任意时间序列数据进行初步诊断,判断其是否适合建模分析,为后续的ARIMA、GARCH等高级模型奠定坚实基础。
💡 核心导读
- 平稳性检验:通过时序图直观判断,结合ADF单位根检验进行统计验证,识别序列是否具有常数均值、方差和自协方差结构
- 纯随机性检验:使用Ljung-Box检验判断序列是否为白噪声,避免对无信息序列进行无效建模
- 自相关分析:通过ACF/PACF图揭示序列内部的相关结构,为模型定阶提供依据
- Python现代化实现:使用
statsmodels、pandas、matplotlib等主流库替代原课程的R语言实现 - 实战全流程:从数据读取、可视化到统计检验的完整代码演示
平稳性检验:理论与Python实现
平稳性的统计定义
在时间序列分析中,我们通常使用宽平稳(弱平稳)定义,要求序列满足以下三个条件:
- 常数均值:,对所有成立
- 常数方差:,对所有成立
- 自协方差仅依赖于时间间隔:,只与滞后阶数有关,与时间起点无关
对应的自相关系数定义为:
平稳性的图检验方法
时序图检验
平稳序列的时序图应显示序列在常数均值附近随机波动,波动范围有界,且无明显趋势或周期性特征。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.stattools import adfuller, acf, pacf
from statsmodels.stats.diagnostic import acorr_ljungbox
import warnings
warnings.filterwarnings('ignore')
# 设置随机种子确保结果可复现
np.random.seed(42)
# 生成示例数据:模拟一个带漂移项的自回归过程
n = 150
dates = pd.date_range('2021-01-01', periods=n, freq='D')
# 生成数据:y_t = 0.5 + 0.6 * y_{t-1} + ε_t, ε_t ~ N(0, 1.2^2)
ε = np.random.normal(0, 1.2, n) # 白噪声扰动
y = np.zeros(n)
for t in range(1, n):
y[t] = 0.5 + 0.6 * y[t - 1] + ε[t]
# 创建时间序列对象
series = pd.Series(y, index=dates, name='模拟时序数据')
# 绘制时序图
plt.figure(figsize=(12, 6))
plt.plot(series, linewidth=1.5)
plt.axhline(y=series.mean(), color='r', linestyle='--', alpha=0.7, label=f'均值线 ({series.mean():.2f})')
plt.fill_between(series.index,
series.mean() - series.std(),
series.mean() + series.std(),
alpha=0.2, color='gray', label='±1标准差区间')
plt.title('时序图检验:模拟时间序列', fontsize=14, fontweight='bold')
plt.xlabel('日期')
plt.ylabel('观测值')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 基础描述统计
desc_stats = series.describe()
print("=== 描述性统计 ===")
print(f"数据长度: {len(series)}")
print(f"均值: {desc_stats['mean']:.4f}")
print(f"标准差: {desc_stats['std']:.4f}")
print(f"最小值: {desc_stats['min']:.4f}")
print(f"25%分位数: {desc_stats['25%']:.4f}")
print(f"中位数: {desc_stats['50%']:.4f}")
print(f"75%分位数: {desc_stats['75%']:.4f}")
print(f"最大值: {desc_stats['max']:.4f}")自相关图(ACF)检验
平稳序列通常具有短期相关性,随着滞后阶数增加,自相关系数会快速衰减到零附近。
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
# 绘制ACF和PACF图
fig, axes = plt.subplots(2, 1, figsize=(12, 8))
# ACF图(自相关函数)
plot_acf(series, lags=20, ax=axes[0], alpha=0.05,
title='自相关图(ACF)检验', color='blue')
axes[0].set_xlabel('滞后阶数')
axes[0].set_ylabel('自相关系数')
axes[0].grid(True, alpha=0.3)
# PACF图(偏自相关函数)
plot_pacf(series, lags=20, ax=axes[1], alpha=0.05,
title='偏自相关图(PACF)检验', color='green')
axes[1].set_xlabel('滞后阶数')
axes[1].set_ylabel('偏自相关系数')
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 计算具体的ACF和PACF值
acf_vals = acf(series, nlags=10, fft=False)
pacf_vals = pacf(series, nlags=10)
print("\n=== 自相关分析 ===")
print("滞后阶数 | 自相关系数(ACF) | 偏自相关系数(PACF)")
print("-" * 50)
for k in range(0, 6):
print(f" {k:2d} | {acf_vals[k]:7.4f} | {pacf_vals[k]:7.4f}")ADF单位根检验
图检验方法主观性强,我们需要更客观的统计检验。Augmented Dickey-Fuller (ADF) 检验是最常用的单位根检验方法。
检验原理:
- 原假设:序列存在单位根(非平稳)
- 备择假设:序列不存在单位根(平稳)
检验模型:
其中,检验(存在单位根) vs (平稳)。
# ADF单位根检验
print("\n=== ADF单位根检验 ===")
adf_result = adfuller(series, autolag='AIC') # 使用AIC准则自动选择滞后阶数
adf_statistic = adf_result[0]
adf_pvalue = adf_result[1]
adf_critical_values = adf_result[4]
print(f"ADF统计量: {adf_statistic:.6f}")
print(f"p值: {adf_pvalue:.6e}")
print("\n临界值(显著性水平):")
for key, value in adf_critical_values.items():
print(f" {key}: {value:.6f}")
# 判断结论
if adf_pvalue < 0.05:
print(f"\n结论: p值({adf_pvalue:.6e}) < 0.05,拒绝原假设")
print(" → 序列是平稳的")
else:
print(f"\n结论: p值({adf_pvalue:.6e}) >= 0.05,不能拒绝原假设")
print(" → 序列可能存在单位根(非平稳)")纯随机性(白噪声)检验
白噪声的统计定义
白噪声序列满足:
- 零均值:,对所有
- 常数方差:,对所有
- 无自相关:,对所有
即:
Ljung-Box检验原理
检验假设:
- :(序列为白噪声)
- :至少存在某个,(序列非白噪声)
检验统计量: 其中为样本量,为检验的最大滞后阶数,为样本自相关系数。
Python实现白噪声检验
# 纯随机性(白噪声)检验
print("\n=== Ljung-Box白噪声检验 ===")
# 对原始序列进行白噪声检验
lb_result = acorr_ljungbox(series, lags=[6, 12, 18], return_df=True)
print("Ljung-Box检验结果:")
print("-" * 60)
print(f"{'滞后阶数':<10} {'LB统计量':<15} {'p值':<15} {'结论':<20}")
print("-" * 60)
for lag, row in lb_result.iterrows():
lb_stat = row['lb_stat']
p_value = row['lb_pvalue']
if p_value < 0.05:
conclusion = "拒绝H_0(非白噪声)"
else:
conclusion = "不能拒绝H_0(可能是白噪声)"
print(f"{lag:<10} {lb_stat:<15.4f} {p_value:<15.6f} {conclusion:<20}")
# 生成并检验真正的白噪声序列
print("\n=== 对比:标准正态白噪声序列检验 ===")
# 生成标准正态白噪声
white_noise = np.random.normal(0, 1, 1000)
white_series = pd.Series(white_noise,
index=pd.date_range('2021-01-01', periods=1000, freq='D'),
name='白噪声序列')
# 白噪声的LB检验
lb_white = acorr_ljungbox(white_series, lags=[6, 12], return_df=True)
print("白噪声序列检验结果:")
print("-" * 60)
print(f"{'滞后阶数':<10} {'LB统计量':<15} {'p值':<15} {'结论':<20}")
print("-" * 60)
for lag, row in lb_white.iterrows():
lb_stat = row['lb_stat']
p_value = row['lb_pvalue']
if p_value < 0.05:
conclusion = "拒绝H_0(非白噪声)"
else:
conclusion = "不能拒绝H_0(可能是白噪声)"
print(f"{lag:<10} {lb_stat:<15.4f} {p_value:<15.6f} {conclusion:<20}")
# 绘制白噪声的时序图和ACF图
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# 原始序列时序图
axes[0, 0].plot(series, linewidth=1, color='blue')
axes[0, 0].set_title('原始序列时序图', fontsize=12)
axes[0, 0].set_xlabel('日期')
axes[0, 0].set_ylabel('观测值')
axes[0, 0].grid(True, alpha=0.3)
# 原始序列ACF图
plot_acf(series, lags=20, ax=axes[0, 1], alpha=0.05, color='blue')
axes[0, 1].set_title('原始序列ACF图', fontsize=12)
axes[0, 1].set_xlabel('滞后阶数')
axes[0, 1].set_ylabel('自相关系数')
axes[0, 1].grid(True, alpha=0.3)
# 白噪声时序图
axes[1, 0].plot(white_series[:150], linewidth=1, color='green') # 只显示前150个点
axes[1, 0].set_title('白噪声序列时序图(前150期)', fontsize=12)
axes[1, 0].set_xlabel('日期')
axes[1, 0].set_ylabel('观测值')
axes[1, 0].grid(True, alpha=0.3)
# 白噪声ACF图
plot_acf(white_series, lags=20, ax=axes[1, 1], alpha=0.05, color='green')
axes[1, 1].set_title('白噪声序列ACF图', fontsize=12)
axes[1, 1].set_xlabel('滞后阶数')
axes[1, 1].set_ylabel('自相关系数')
axes[1, 1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()综合实战:完整预处理流程
下面我们将所有检验整合到一个完整的预处理流程中:
def time_series_preprocessing(data_series, series_name="时间序列"):
"""
时间序列预处理完整流程
参数:
-----------
data_series : pandas.Series
待处理的时间序列数据
series_name : str
序列名称,用于输出标识
返回:
-----------
dict : 包含所有检验结果的字典
"""
print(f"=== {series_name} 预处理分析报告 ===\n")
results = {}
# 1. 基础描述统计
print("1. 描述性统计")
print("-" * 40)
desc = data_series.describe()
for stat, value in desc.items():
print(f" {stat}: {value:.6f}")
results['description'] = desc
# 2. 平稳性检验(ADF)
print(f"\n2. 平稳性检验(ADF单位根检验)")
print("-" * 40)
adf_result = adfuller(data_series, autolag='AIC')
adf_stat = adf_result[0]
adf_p = adf_result[1]
results['adf_statistic'] = adf_stat
results['adf_pvalue'] = adf_p
results['adf_critical_values'] = adf_result[4]
print(f" ADF统计量: {adf_stat:.6f}")
print(f" p值: {adf_p:.6e}")
if adf_p < 0.05:
print(f" → 结论: 序列平稳 (p < 0.05)")
results['is_stationary'] = True
else:
print(f" → 结论: 序列非平稳 (p >= 0.05)")
results['is_stationary'] = False
# 3. 纯随机性检验(Ljung-Box)
print(f"\n3. 纯随机性检验(Ljung-Box)")
print("-" * 40)
# 检验多个滞后阶数
lags_to_test = [6, 12, 18]
lb_result = acorr_ljungbox(data_series, lags=lags_to_test, return_df=True)
results['lb_test'] = lb_result
print(f" 检验滞后阶数: {lags_to_test}")
print(f" {'滞后阶数':<8} {'LB统计量':<12} {'p值':<15} {'结论':<20}")
print(f" {'-'*8} {'-'*12} {'-'*15} {'-'*20}")
all_white_noise = True
for lag, row in lb_result.iterrows():
lb_stat = row['lb_stat']
p_value = row['lb_pvalue']
if p_value < 0.05:
conclusion = "非白噪声"
all_white_noise = False
else:
conclusion = "可能是白噪声"
print(f" {lag:<8} {lb_stat:<12.4f} {p_value:<15.6e} {conclusion:<20}")
if all_white_noise:
print(f" → 综合结论: 序列可能是白噪声")
results['is_white_noise'] = True
else:
print(f" → 综合结论: 序列包含可提取信息(非白噪声)")
results['is_white_noise'] = False
# 4. 自相关分析
print(f"\n4. 自相关分析")
print("-" * 40)
# 计算ACF和PACF
acf_vals = acf(data_series, nlags=10, fft=False)
pacf_vals = pacf(data_series, nlags=10)
results['acf'] = acf_vals
results['pacf'] = pacf_vals
print(f" 前5阶自相关系数(ACF): {np.round(acf_vals[1:6], 4).tolist()}")
print(f" 前5阶偏自相关系数(PACF): {np.round(pacf_vals[1:6], 4).tolist()}")
# 5. 综合诊断建议
print(f"\n5. 综合诊断与建模建议")
print("-" * 40)
if not results['is_stationary']:
print(" → 序列非平稳,建议进行差分处理后再分析")
print(" → 可考虑使用ARIMA模型(包含差分项)")
elif results['is_white_noise']:
print(" → 序列为白噪声,无进一步建模价值")
print(" → 建议检查数据或更换分析对象")
else:
print(" → 序列平稳且包含可提取信息,适合进一步建模")
print(" → 根据ACF/PACF截尾拖尾特征选择AR/MA/ARMA模型")
return results
# 运行完整预处理流程
print("=" * 60)
preprocessing_results = time_series_preprocessing(series, "模拟自回归序列")
print("=" * 60)📝 动手练一练
平稳性判断实战 使用以下代码生成三个不同的时间序列,分别判断它们的平稳性:
import numpy as np import pandas as pd from statsmodels.tsa.stattools import adfuller np.random.seed(123) n = 200 # 序列1:随机游走(非平稳) random_walk = np.cumsum(np.random.normal(0, 1, n)) # 序列2:带趋势的序列(非平稳) trend = 0.05 * np.arange(n) + np.random.normal(0, 1, n) # 序列3:平稳AR(1)过程 ar1 = np.zeros(n) for t in range(1, n): ar1[t] = 0.7 * ar1[t-1] + np.random.normal(0, 1) # 请分别对这三个序列进行ADF检验,判断它们的平稳性参考答案:
# 对随机游走序列进行ADF检验 adf_rw = adfuller(random_walk) print(f"随机游走: ADF统计量={adf_rw[0]:.4f}, p值={adf_rw[1]:.4f}") # 预期结果:p值 > 0.05,非平稳 # 对带趋势序列进行ADF检验 adf_trend = adfuller(trend) print(f"带趋势序列: ADF统计量={adf_trend[0]:.4f}, p值={adf_trend[1]:.4f}") # 预期结果:p值 > 0.05,非平稳 # 对AR(1)过程进行ADF检验 adf_ar1 = adfuller(ar1) print(f"AR(1)过程: ADF统计量={adf_ar1[0]:.4f}, p值={adf_ar1[1]:.4f}") # 预期结果:p值 < 0.05,平稳白噪声检验对比 生成以下两个序列并进行Ljung-Box检验:
from statsmodels.stats.diagnostic import acorr_ljungbox # 序列A:真正的白噪声 white_noise_A = np.random.normal(0, 1, 500) # 序列B:具有自相关的序列 correlated = np.zeros(500) for t in range(1, 500): correlated[t] = 0.6 * correlated[t-1] + np.random.normal(0, 1) # 请分别对这两个序列进行滞后6阶和12阶的LB检验参考答案:
# 对白噪声序列进行LB检验 lb_white = acorr_ljungbox(white_noise_A, lags=[6, 12], return_df=True) print("白噪声序列LB检验:") print(lb_white) # 预期结果:p值均大于0.05,不能拒绝白噪声原假设 # 对自相关序列进行LB检验 lb_corr = acorr_ljungbox(correlated, lags=[6, 12], return_df=True) print("\n自相关序列LB检验:") print(lb_corr) # 预期结果:p值均小于0.05,拒绝白噪声原假设
本章小结
本节我们系统学习了时间序列预处理的两大核心检验方法,并通过Python实战掌握了完整的分析流程:
关键要点回顾
平稳性检验是建模前提
- 通过时序图直观判断趋势和周期性
- 使用ADF单位根检验进行统计验证
- 平稳序列具有常数均值、方差和仅依赖于时间间隔的自协方差
纯随机性检验避免无效建模
- 白噪声序列无分析价值,相邻观测相互独立
- Ljung-Box检验判断序列是否包含可提取信息
- 非白噪声序列才值得进一步建模分析
自相关分析揭示序列结构
- ACF图显示序列的自相关衰减模式
- PACF图帮助识别AR模型的阶数
- 结合ACF/PACF特征为模型选择提供依据
Python现代化实现
- 使用
statsmodels库进行统计检验 pandas处理时间序列数据matplotlib实现数据可视化
- 使用
行动清单
学完本节后,你可以立即开始:
数据诊断:对你感兴趣的任何时间序列数据(股票价格、气温、销售额等)运行完整的预处理流程,判断其平稳性和随机性
代码实践:将本节提供的预处理函数封装成自己的工具库,方便后续分析直接调用
对比分析:寻找平稳与非平稳序列、白噪声与非白噪声序列的实际案例,通过对比加深理解
时间序列预处理是建模分析的第一步,也是至关重要的一步。正确的预处理能够避免”垃圾进,垃圾出”的问题,确保后续建模的有效性。在接下来的课程中,我们将基于平稳非白噪声序列,学习AR、MA、ARMA等经典时间序列模型。
— 小象教研组
领取《小象 11GB VIP 课件资料包与大厂真题手册》
包含全套实战 Jupyter 源码、清洗后数据集、大厂高频面试真题与专属学员答疑交流群。
- ✔完整 Python / 数据分析 Jupyter 实战源码
- ✔大厂真实业务数据集与练习题
- ✔微信扫码添加顾问免费领取;想学什么,直接告诉顾问
微信扫码添加顾问