时间序列分析基石:ADF检验原理、Python实现与平稳化实战

发布时间:2026/8/23 3:32:43
时间序列分析基石:ADF检验原理、Python实现与平稳化实战 1. 项目概述为什么数据平稳性是时间序列分析的基石做时间序列分析无论是预测股票价格、分析销售趋势还是研究气象数据你遇到的第一个、也是最关键的门槛往往不是模型有多复杂而是数据本身是否“听话”。这个“听话”在统计学里有个专业术语叫做“平稳性”。想象一下你要预测一条河流明天的水位如果这条河的水位长期来看有一个明显的上升或下降趋势比如因为上游建了水库或者它的波动幅度夏天大、冬天小那么你基于历史数据做的预测大概率会失准。因为数据的“基本规则”在变。ADF检验全称Augmented Dickey-Fuller检验就是我们手里那把最常用的“尺子”用来量一量你的时间序列数据到底平不平稳。它检验的核心是数据中是否存在“单位根”。你可以把单位根理解为一个“记忆顽疾”——过去的一个冲击或波动会对未来产生永久性的影响导致序列没有固定的均值会随着时间“漂移”。带有单位根的数据就是非平稳的直接对它建模无异于沙上筑塔。我见过太多新手包括几年前的我自己拿到数据后兴奋地直接套上ARIMA模型就开始跑结果模型效果时好时坏参数怎么调都不对。折腾半天回头一做ADF检验发现数据根本不平稳前面功夫全白费了。所以无论你用的是ARMA、ARIMA还是更复杂的模型平稳性检验都是不可省略的“体检”第一步。这篇内容我就结合自己踩过的坑和实战经验把ADF检验从原理到实操再到结果解读给你彻底讲透让你能稳稳地迈出时间序列分析的第一步。2. 平稳性的深层逻辑与ADF检验原理拆解2.1 平稳性到底意味着什么很多人对平稳性的理解停留在“数据围绕一个固定值上下波动”的图表层面这没错但不够深入。严格来说平稳性分为严平稳和弱平稳宽平稳。我们时间序列分析中通常要求的是弱平稳它有三个核心特征均值恒定时间序列所有时间点上的期望值均值是一个常数不随时间变化。这意味着数据没有趋势性。方差恒定时间序列所有时间点上的方差是一个常数不随时间变化。这意味着数据的波动幅度是稳定的没有所谓的“波动聚集”现象比如股市崩盘时波动剧烈平静期波动小。协方差仅依赖于时间间隔任意两个时间点t和s的协方差只取决于它们之间的时间间隔|t-s|而与具体的时间点t或s本身无关。这意味着序列内部的结构关系是稳定的。为什么模型需要平稳数据因为绝大多数经典时间序列模型如AR, MA, ARMA的基本假设就是数据生成过程是平稳的。只有在平稳的假设下模型的参数才具有统计上的优良性质如一致性基于模型进行的预测和推断才是可靠的。非平稳数据直接建模会导致“伪回归”问题——即使两个完全无关的非平稳序列也可能因为共同的时间趋势而显示出高度的统计相关性但这种关系是虚假的。2.2. ADF检验的数学思想与模型形式ADF检验的核心是检验单位根是否存在。它的基本思想来自于对一阶自回归模型AR(1)的思考。一个简单的AR(1)模型是Y_t ρ * Y_{t-1} ε_t其中ε_t是白噪声。如果系数ρ 1那么这个模型就有一个单位根序列Y_t是一个随机游走过程是非平稳的。如果|ρ| 1序列就是平稳的。ADF检验将上述模型改写为差分形式来进行检验ΔY_t α βt γY_{t-1} Σ_{i1}^{p} φ_i ΔY_{t-i} ε_t其中ΔY_t Y_t - Y_{t-1}即一阶差分。α是常数项漂移项。βt是时间趋势项。γ ρ - 1。检验的原假设H0就是 γ 0即存在单位根数据非平稳。备择假设H1是γ 0即不存在单位根数据平稳。Σ_{i1}^{p} φ_i ΔY_{t-i}是增加的滞后差分项用于消除误差项ε_t中的自相关性确保其是白噪声。这也是“Augmented”增广一词的由来。p就是滞后阶数它的选择至关重要。ADF检验提供了三种回归模型供选择模型1既无常数项也无趋势项(ΔY_t γY_{t-1} Σφ_i ΔY_{t-i} ε_t)。适用于数据已无趋势、均值围绕0波动的序列。模型2有常数项无趋势项(ΔY_t α γY_{t-1} Σφ_i ΔY_{t-i} ε_t)。适用于数据围绕一个非零的恒定均值波动但无趋势。模型3既有常数项也有趋势项(ΔY_t α βt γY_{t-1} Σφ_i ΔY_{t-i} ε_t)。适用于数据可能存在线性趋势。实操心得在实际操作中如果你不确定该用哪个模型从模型3包含常数和趋势开始是一个稳健的选择。因为如果模型误设例如实际有趋势但你用了无趋势的模型检验的功效检出平稳性的能力会大大降低。ADF检验的结果会提供针对每个模型的检验统计量和对应的p值。2.3. 滞后阶数p的选择一个容易被忽视的关键滞后阶数p的选择直接影响检验结果。p太小残差中可能存在自相关导致检验结果不可靠p太大则会损失过多的自由度降低检验的功效即可能将原本平稳的序列误判为非平稳。常见的自动选择准则有AIC (Akaike Information Criterion): 倾向于选择更复杂的模型平衡拟合优度和模型复杂度。BIC (Bayesian Information Criterion) / SC (Schwarz Criterion): 比AIC惩罚更重倾向于选择更简单的模型。HQIC (Hannan-Quinn Information Criterion): 介于AIC和BIC之间。在Python的statsmodels库或R语言中我们可以指定最大滞后阶数让程序根据以上信息准则自动选择最优p。我个人的习惯是先让程序自动选择然后再观察不同p值下结论是否一致特别是在临界值附近时需要格外谨慎。3. ADF检验的完整实操流程与Python实现接下来我们用一个实际的例子走一遍完整的ADF检验流程。假设我们有一组某产品的月度销售额数据。3.1. 环境准备与数据初探首先导入必要的库并加载数据。import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns from statsmodels.tsa.stattools import adfuller from statsmodels.graphics.tsaplots import plot_acf, plot_pacf import warnings warnings.filterwarnings(ignore) # 设置中文显示和图形样式 plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False sns.set_style(whitegrid) # 假设我们有一个DataFrame df其中包含‘date’列和‘sales’列 # 这里我们模拟生成一组带有趋势和季节性的非平稳数据 np.random.seed(42) date_rng pd.date_range(start2018-01-01, end2023-12-01, freqMS) trend np.linspace(100, 200, len(date_rng)) seasonality 20 * np.sin(2 * np.pi * np.arange(len(date_rng)) / 12) noise np.random.normal(0, 10, len(date_rng)) sales trend seasonality noise df pd.DataFrame({date: date_rng, sales: sales}) df.set_index(date, inplaceTrue) # 初步观察数据 print(df.head()) plt.figure(figsize(12, 6)) plt.plot(df.index, df[sales], label月度销售额) plt.title(原始销售额时间序列图) plt.xlabel(日期) plt.ylabel(销售额) plt.legend() plt.show()绘制出的图表很可能会显示出一个清晰的上升趋势和年度周期性波动。这直观地告诉我们原始数据极有可能是非平稳的。3.2. 执行ADF检验并解读结果我们使用statsmodels的adfuller函数进行检验。关键是要理解每个参数和输出结果的含义。# 对原始序列进行ADF检验 # autolagAIC 表示基于AIC准则自动选择最优滞后阶数 # regressionct 表示使用模型3包含常数项和趋势项 result adfuller(df[sales], autolagAIC, regressionct) print(ADF检验结果:) print(fADF统计量 (Test Statistic): {result[0]:.4f}) print(fP值 (p-value): {result[1]:.4f}) print(f使用的滞后阶数 (Lags Used): {result[2]}) print(f观测值数量 (Number of Observations Used): {result[3]}) print(临界值 (Critical Values):) for key, value in result[4].items(): print(f\t{key}: {value:.4f}) # 结果解读 print(\n--- 结果解读 ---) if result[1] 0.05: print(P值 0.05在5%的显著性水平下拒绝原假设。) print(结论该时间序列是平稳的。) else: print(P值 0.05在5%的显著性水平下无法拒绝原假设。) print(结论该时间序列是非平稳的。)输出结果可能类似于ADF检验结果: ADF统计量 (Test Statistic): -2.3456 P值 (p-value): 0.4087 使用的滞后阶数 (Lags Used): 11 观测值数量 (Number of Observations Used): 59 临界值 (Critical Values): 1%: -4.1240 5%: -3.4890 10%: -3.1730 --- 结果解读 --- P值 0.05在5%的显著性水平下无法拒绝原假设。 结论该时间序列是非平稳的。如何解读看P值这是最直接的判断依据。通常我们以0.05为显著性水平。如果p-value 0.05则拒绝原假设H0: γ0存在单位根认为序列平稳反之则认为序列非平稳。本例中p-value0.4087 0.05所以接受原假设序列非平稳。对比ADF统计量与临界值ADF统计量本例中-2.3456与不同置信水平1% 5% 10%下的临界值比较。如果ADF统计量比临界值更负即更小则拒绝原假设。本例中-2.3456 -3.48905%临界值所以同样得出非平稳的结论。关注滞后阶数检验自动选择了11阶滞后。这提醒我们序列可能存在长期的自相关结构。3.3. 让数据变平稳差分变换既然原始数据非平稳我们就需要对其进行变换最常用的方法是差分。一阶差分可以消除线性趋势二阶差分可以消除曲线趋势季节性差分可以消除季节性。# 计算一阶差分 df[sales_diff_1] df[sales].diff(1) # 计算季节性差分周期为12个月 df[sales_diff_seasonal] df[sales].diff(12) # 绘制差分后的序列 fig, axes plt.subplots(2, 1, figsize(12, 10)) axes[0].plot(df.index[1:], df[sales_diff_1].iloc[1:], label一阶差分, colororange) axes[0].set_title(销售额一阶差分序列) axes[0].legend() axes[0].axhline(y0, colorr, linestyle--, alpha0.3) axes[1].plot(df.index[12:], df[sales_diff_seasonal].iloc[12:], label季节性差分12期, colorgreen) axes[1].set_title(销售额季节性差分序列) axes[1].legend() axes[1].axhline(y0, colorr, linestyle--, alpha0.3) plt.tight_layout() plt.show() # 对一阶差分序列再次进行ADF检验 result_diff adfuller(df[sales_diff_1].dropna(), autolagAIC, regressionc) # 注意差分后通常趋势已消除这里用‘c’仅常数项模型 print(一阶差分序列ADF检验结果:) print(fADF统计量: {result_diff[0]:.4f}) print(fP值: {result_diff[1]:.4f}) if result_diff[1] 0.05: print(一阶差分后序列已平稳。) else: print(一阶差分后序列仍不平稳可能需要二阶差分或结合季节性差分。)通常经过一阶或季节性差分后序列会变得平稳。此时我们就可以对平稳后的差分序列例如sales_diff_1进行后续的ARIMA建模了。ARIMA模型中的d参数指的就是使序列平稳所需的差分阶数。4. 高级技巧、常见陷阱与结果深度剖析4.1. 如何选择合适的回归模型‘c’, ‘ct’, ‘nc’这是实操中的一个难点。一个系统的方法是看图说话首先绘制序列时序图。如果序列明显围绕0上下波动无趋势可尝试‘nc’无常数无趋势。如果序列在非零水平波动无明显趋势用‘c’仅常数。如果序列有明显上升或下降趋势用‘ct’常数和趋势。顺序检验法这是一个更严谨的统计方法。首先用最一般的模型‘ct’进行检验。如果拒绝原假设平稳且趋势项系数β显著不为0则最终模型为‘ct’。如果拒绝原假设但趋势项系数β不显著则改用模型‘c’重新检验。如果用模型‘ct’无法拒绝原假设非平稳则进行差分再对差分序列从‘ct’模型开始检验。对于差分后序列若用模型‘c’拒绝原假设且常数项α显著则模型为‘c’若常数项不显著则考虑‘nc’。注意事项在statsmodels的adfuller函数中regression参数的可选项为‘c’: 仅常数对应模型2‘ct’: 常数和线性趋势对应模型3‘ctt’: 常数、线性和二次趋势更复杂少用‘nc’: 无常数无趋势对应模型1 选择错误的模型会严重影响检验结论。当你报告ADF检验结果时必须同时说明使用的是哪种模型。4.2. 结合自相关图ACF与偏自相关图PACF进行综合判断ADF检验是一个严格的统计检验但有时它也会“失灵”或给出模糊的结果比如p值在0.05边缘。这时结合ACF和PACF图进行视觉判断是一个非常好的补充。# 绘制原始序列和一阶差分序列的ACF/PACF图 fig, axes plt.subplots(2, 2, figsize(14, 10)) # 原始序列 plot_acf(df[sales].dropna(), lags40, axaxes[0, 0]) axes[0, 0].set_title(原始序列 - 自相关函数 (ACF)) plot_pacf(df[sales].dropna(), lags40, axaxes[0, 1]) axes[0, 1].set_title(原始序列 - 偏自相关函数 (PACF)) # 一阶差分序列 plot_acf(df[sales_diff_1].dropna(), lags40, axaxes[1, 0]) axes[1, 0].set_title(一阶差分序列 - 自相关函数 (ACF)) plot_pacf(df[sales_diff_1].dropna(), lags40, axaxes[1, 1]) axes[1, 1].set_title(一阶差分序列 - 偏自相关函数 (PACF)) plt.tight_layout() plt.show()如何解读平稳序列的ACF图自相关系数会快速衰减至0在置信区间内。衰减速度慢是单位根过程非平稳的典型特征。非平稳序列的ACF图自相关系数衰减非常缓慢或者长期显著不为0。本例中原始序列的ACF图很可能呈现缓慢下降的趋势。差分后平稳序列的ACF/PACF图除了在季节滞后点如lag 12, 24可能有峰值表明残留季节性其他位置的自相关/偏自相关会快速截尾或拖尾为识别ARIMA模型的p和q阶数提供依据。4.3. 常见问题与排查技巧实录问题1ADF检验结果说平稳但序列图明明有趋势/季节怎么回事可能原因1检验势Power不足。当趋势或季节性较弱或者样本量较小时ADF检验可能无法检测出非平稳性。排查尝试使用KPSS检验原假设是平稳进行反向验证。如果ADF说不平稳KPSS说平稳结论比较可靠如果两者都说不平稳那很可能就是非平稳如果两者结论矛盾则需要更谨慎结合图形和其他信息如业务知识判断或者考虑对数据做差分后再观察。可能原因2选择了错误的回归模型。例如有趋势的数据用了‘c’或‘nc’模型。排查用‘ct’模型重新检验一次。问题2差分后序列的均值不为0ADF检验应该用哪个模型解答差分通常是为了消除趋势。差分后的序列理论上应该围绕0波动。如果差分后序列的均值明显偏离0可通过时序图观察在ADF检验中应使用包含常数项的模型‘c’。这个常数项实际上代表了原始序列中的确定性趋势。例如一阶差分后序列的均值若为c则意味着原始序列有一个固定的每期增长量c即线性趋势。问题3滞后阶数p应该设多大自动选择准可靠吗解答自动选择准则AIC/BIC在大多数情况下是可靠的。一个经验法则是将最大滞后阶数设置为12*(n/100)^{0.25}其中n为样本量这是一个常用的启发式公式。你也可以观察ACF图看看自相关系数在哪个滞后之后基本落入置信区间内以此作为最大滞后阶数的参考。技巧可以尝试不同的最大滞后阶数观察ADF检验的p值和选择的滞后阶数是否稳定。如果结论在合理范围内变化不大则结果比较稳健。问题4数据中存在结构性突变如政策改变、突发事件怎么办解答标准的ADF检验对结构性突变非常敏感可能导致误判。例如序列前半段平稳后半段因突发事件均值突变整体检验可能得出非平稳的结论。这时需要考虑使用带结构突变的单位根检验如Zivot-Andrews检验它允许在未知时间点存在一次结构性突变。问题5ADF检验通过平稳接下来直接建ARMA模型吗解答不一定。ADF检验只是平稳性检验。你还需要确保序列是弱平稳的即方差恒定同方差。如果序列存在异方差例如波动率聚类常见于金融收益率序列即使均值平稳直接建ARMA也可能有问题。此时需要观察序列图或检验异方差并考虑使用GARCH族模型。下表总结了ADF检验结果解读的决策路径ADF检验P值与临界值比较统计结论后续行动建议 显著性水平 (如0.05)ADF统计量 临界值拒绝原假设序列平稳可进行后续建模如ARMA。检查方差是否平稳异方差。 显著性水平 (如0.05)ADF统计量 临界值无法拒绝原假设序列非平稳对序列进行差分一阶、二阶或季节性差分然后对差分后序列重新检验。接近临界值 (如0.04~0.06)ADF统计量接近临界值结论模糊结合KPSS检验、序列时序图、ACF图综合判断。或直接进行差分因为过度差分通常比差分不足的危害小。5. 超越ADF其他平稳性检验方法简介虽然ADF是最主流的检验方法但了解其他工具能让你在复杂情况下游刃有余。1. KPSS检验原假设序列是平稳的或趋势平稳的。这与ADF检验的原假设正好相反。用途常与ADF检验结合使用进行“双重检验”。如果ADF拒绝平稳且KPSS不拒绝平稳则强烈支持平稳性结论。如果ADF不拒绝非平稳且KPSS拒绝非平稳则强烈支持非平稳性结论。如果两者矛盾则需要进一步分析。2. PP检验 (Phillips-Perron Test)原理与ADF检验类似也是检验单位根。但PP检验采用了非参数的方法来修正序列相关和异方差不需要像ADF那样通过加入滞后差分项来修正。其优点是对于滞后阶数的选择不敏感。局限在小样本情况下其表现可能不如ADF检验稳健。3. DF-GLS检验 (Dickey-Fuller GLS Test)原理先对数据进行局部去趋势GLS去势然后再进行单位根检验。这种方法比标准的ADF检验具有更高的检验势即更容易检测出平稳性。适用场景当你怀疑序列有趋势且希望更灵敏地检测平稳性时使用。在实际工作中我的建议是以ADF检验为主以KPSS检验为辅结合时序图和ACF图进行综合诊断。不要仅仅依赖一个检验的p值就武断下结论。6. 从检验到建模平稳性处理的实际案例串联让我们把整个流程串起来模拟一个从数据检验到平稳化处理的完整场景。假设我们处理的是“某城市月度用电量”数据它通常有长期增长趋势和年度季节性。步骤一导入与观察加载数据绘制时序图。观察到明显的上升趋势和每年夏季、冬季的用电高峰。步骤二执行ADF检验对原始用电量序列进行ADF检验使用regressionct。结果p值远大于0.05强烈认为序列非平稳。步骤三一阶差分消除趋势计算一阶差分df[‘power_diff1’] df[‘power’].diff(1)。绘制差分后序列图发现上升趋势基本消除但季节性波动依然明显。步骤四季节性差分消除季节性对一阶差分序列进一步做周期为12的季节性差分df[‘power_diff1_seasonal’] df[‘power_diff1’].diff(12)。或者也可以直接对原始序列做一阶季节性差分df[‘power_diff_seasonal’] df[‘power’].diff(12)这取决于序列特点。通常先做普通差分消除趋势再做季节性差分是稳妥的做法。步骤五检验差分后序列对最终处理后的序列例如power_diff1_seasonal进行ADF检验此时通常用regressionc或‘nc’。检验结果显示p值小于0.05序列已平稳。同时观察其ACF/PACF图自相关和偏自相关函数呈现快速衰减或截尾模式符合平稳序列特征。步骤六确定ARIMA模型阶数根据平稳化后序列的ACF和PACF图初步识别ARIMA模型的参数d: 使序列平稳所需的普通差分次数此处为1。D: 使序列平稳所需的季节性差分次数此处为1周期12。p: 从PACF图的截尾位置判断自回归阶数。q: 从ACF图的截尾位置判断移动平均阶数。P,Q: 季节性自回归和移动平均阶数从季节滞后lag 12, 24...处的ACF/PACF图判断。至此我们完成了ARIMA模型建模前最关键的平稳性检验与处理步骤获得了可用于建模的平稳时间序列并初步确定了模型的阶数范围。这个过程是后续所有精准预测和分析的坚实基础。记住在时间序列的世界里耐心做好平稳性检验这份“体检”远比在非平稳数据上盲目调试复杂的模型参数要有效得多。