我们已经看到,许多时间序列表现出趋势或季节性,使得它们的统计特性(如均值和方差)随时间变化。这种非平稳性对假定数据平稳的ARMA等标准预测模型构成难题。幸运的是,将非平稳数据转换为平稳数据的一种常用且有效的方法是差分。差分的原理差分计算时间序列中连续观测值之间的变化。对于时间序列 $y_t$,一阶差分记作 $\Delta y_t$,计算方式为:$$ \Delta y_t = y_t - y_{t-1} $$这种简单的操作通常可以通过消除趋势来稳定时间序列的均值。设想一个具有线性上升趋势的序列。$y_t$ 的值持续增长。然而,连续值之间的差值($y_t - y_{t-1}$)可能大致保持不变,围绕一个稳定的均值波动(代表趋势的斜率)。通过差分消除趋势让我们考虑一个具有明显趋势的时间序列。应用一阶差分有助于消除此趋势,使序列在均值上平稳。import pandas as pd import numpy as np import plotly.graph_objects as go from statsmodels.tsa.stattools import adfuller # 生成带有趋势的样本数据 np.random.seed(42) time = pd.date_range(start='2022-01-01', periods=100, freq='D') # 趋势成分 + 随机噪声 trend = np.linspace(0, 20, 100) noise = np.random.normal(0, 2, 100) data = pd.Series(trend + noise, index=time, name='Original Data') # 计算一阶差分 differenced_data = data.diff().dropna() # dropna 删除第一个 NaN 值 # 检查差分前后的平稳性 adf_original = adfuller(data) adf_differenced = adfuller(differenced_data) print(f"原始数据上的ADF检验:p值 = {adf_original[1]:.3f}") print(f"差分数据上的ADF检验:p值 = {adf_differenced[1]:.3f}") # 创建图表 fig = go.Figure() fig.add_trace(go.Scatter(x=data.index, y=data, mode='lines', name='原始序列 (趋势)', line=dict(color='#4263eb'))) fig.add_trace(go.Scatter(x=differenced_data.index, y=differenced_data, mode='lines', name='一阶差分', line=dict(color='#12b886'))) fig.update_layout( title='一阶差分对趋势的影响', xaxis_title='时间', yaxis_title='值', legend_title='序列', template='plotly_white', width=700, height=400 ) # fig.show() # 在实际环境中 {"layout": {"title": "一阶差分对趋势的影响", "xaxis_title": "时间", "yaxis_title": "值", "legend_title": "序列", "template": "plotly_white", "width": 700, "height": 400}, "data": [{"x": ["2022-01-01", "2022-01-02", "2022-01-03", "2022-01-04", "2022-01-05", "2022-01-06", "2022-01-07", "2022-01-08", "2022-01-09", "2022-01-10", "2022-01-11", "2022-01-12", "2022-01-13", "2022-01-14", "2022-01-15", "2022-01-16", "2022-01-17", "2022-01-18", "2022-01-19", "2022-01-20", "2022-01-21", "2022-01-22", "2022-01-23", "2022-01-24", "2022-01-25", "2022-01-26", "2022-01-27", "2022-01-28", "2022-01-29", "2022-01-30", "2022-01-31", "2022-02-01", "2022-02-02", "2022-02-03", "2022-02-04", "2022-02-05", "2022-02-06", "2022-02-07", "2022-02-08", "2022-02-09", "2022-02-10", "2022-02-11", "2022-02-12", "2022-02-13", "2022-02-14", "2022-02-15", "2022-02-16", "2022-02-17", "2022-02-18", "2022-02-19", "2022-02-20", "2022-02-21", "2022-02-22", "2022-02-23", "2022-02-24", "2022-02-25", "2022-02-26", "2022-02-27", "2022-02-28", "2022-03-01", "2022-03-02", "2022-03-03", "2022-03-04", "2022-03-05", "2022-03-06", "2022-03-07", "2022-03-08", "2022-03-09", "2022-03-10", "2022-03-11", "2022-03-12", "2022-03-13", "2022-03-14", "2022-03-15", "2022-03-16", "2022-03-17", "2022-03-18", "2022-03-19", "2022-03-20", "2022-03-21", "2022-03-22", "2022-03-23", "2022-03-24", "2022-03-25", "2022-03-26", "2022-03-27", "2022-03-28", "2022-03-29", "2022-03-30", "2022-03-31", "2022-04-01", "2022-04-02", "2022-04-03", "2022-04-04", "2022-04-05", "2022-04-06", "2022-04-07", "2022-04-08", "2022-04-09", "2022-04-10"], "y": [0.9934, 1.9209, 1.9314, 3.2694, 2.9788, 4.5928, 5.9098, 6.7517, 8.2069, 6.7315, 8.8004, 10.5648, 10.7223, 14.1182, 11.8588, 15.4814, 16.1851, 16.0885, 17.4016, 17.8024, 17.5757, 20.7798, 20.8002, 23.7158, 23.9806, 24.0685, 28.0489, 26.6354, 27.7205, 26.3894, 30.6338, 27.8921, 33.0424, 33.3333, 33.3517, 32.6099, 33.9279, 34.2861, 38.2198, 37.1665, 37.0824, 41.3989, 43.1318, 40.0883, 44.3077, 43.7197, 45.5589, 48.0163, 45.4707, 47.7819, 50.1892, 51.7266, 50.2644, 50.8081, 53.2074, 54.4384, 55.2889, 56.5337, 56.9581, 58.7805, 60.1698, 61.1505, 60.2261, 62.1635, 64.6297, 62.1017, 65.2999, 66.4731, 66.5075, 67.5438, 69.9369, 72.5362, 69.6384, 72.0816, 71.4606, 74.0881, 74.5707, 76.3944, 77.4599, 77.3669, 79.6118, 81.0477, 80.2023, 80.9055, 81.8585, 83.2186, 84.0553, 85.9874, 85.9633, 86.6106, 87.9027, 90.1117, 88.1207, 90.0834, 91.8029, 91.3384, 93.9708, 93.6483, 95.3359], "mode": "lines", "name": "原始序列 (趋势)", "line": {"color": "#4263eb"}}, {"x": ["2022-01-02", "2022-01-03", "2022-01-04", "2022-01-05", "2022-01-06", "2022-01-07", "2022-01-08", "2022-01-09", "2022-01-10", "2022-01-11", "2022-01-12", "2022-01-13", "2022-01-14", "2022-01-15", "2022-01-16", "2022-01-17", "2022-01-18", "2022-01-19", "2022-01-20", "2022-01-21", "2022-01-22", "2022-01-23", "2022-01-24", "2022-01-25", "2022-01-26", "2022-01-27", "2022-01-28", "2022-01-29", "2022-01-30", "2022-01-31", "2022-02-01", "2022-02-02", "2022-02-03", "2022-02-04", "2022-02-05", "2022-02-06", "2022-02-07", "2022-02-08", "2022-02-09", "2022-02-10", "2022-02-11", "2022-02-12", "2022-02-13", "2022-02-14", "2022-02-15", "2022-02-16", "2022-02-17", "2022-02-18", "2022-02-19", "2022-02-20", "2022-02-21", "2022-02-22", "2022-02-23", "2022-02-24", "2022-02-25", "2022-02-26", "2022-02-27", "2022-02-28", "2022-03-01", "2022-03-02", "2022-03-03", "2022-03-04", "2022-03-05", "2022-03-06", "2022-03-07", "2022-03-08", "2022-03-09", "2022-03-10", "2022-03-11", "2022-03-12", "2022-03-13", "2022-03-14", "2022-03-15", "2022-03-16", "2022-03-17", "2022-03-18", "2022-03-19", "2022-03-20", "2022-03-21", "2022-03-22", "2022-03-23", "2022-03-24", "2022-03-25", "2022-03-26", "2022-03-27", "2022-03-28", "2022-03-29", "2022-03-30", "2022-03-31", "2022-04-01", "2022-04-02", "2022-04-03", "2022-04-04", "2022-04-05", "2022-04-06", "2022-04-07", "2022-04-08", "2022-04-09", "2022-04-10"], "y": [0.9275, 0.0105, 1.3380, -0.2905, 1.6141, 1.3169, 0.8419, 1.4552, -1.4754, 2.0689, 1.7644, 0.1575, 3.3959, -2.2594, 3.6226, 0.7037, -0.0966, 1.3131, 0.4008, -0.2267, 3.2041, 0.0204, 2.9156, 0.2648, 0.0879, 3.9804, -1.4135, 1.0850, -1.3311, 4.2444, -2.7417, 5.1503, 0.2909, 0.0184, -0.7418, 1.3180, 0.3581, 3.9337, -1.0533, -0.0841, 4.3164, 1.7329, -3.0435, 4.2194, -0.5880, 1.8392, 2.4574, -2.5456, 2.3113, 2.4073, 1.5374, -1.4622, 0.5437, 2.3993, 1.2310, 0.8505, 1.2448, 0.4244, 1.8224, 1.3893, 0.9807, -0.9244, 1.9374, 2.4662, -2.5280, 3.1982, 1.1732, 0.0344, 1.0363, 2.3930, 2.6000, -2.8978, 2.4432, -0.6210, 2.6275, 0.4826, 1.8237, 1.0654, -0.0930, 2.2449, 1.4359, -0.8454, 0.7032, 0.9531, 1.3600, 0.8368, 1.9321, -0.0241, 0.6473, 1.2921, 2.2090, -1.9911, 1.9628, 1.7194, -0.4645, 2.6325, -0.3225, 1.6876], "mode": "lines", "name": "一阶差分", "line": {"color": "#12b886"}}]}原始序列显示出明显的上升趋势,并且ADF检验未能拒绝非平稳性的零假设(p值较高)。应用一阶差分后,结果序列在零附近波动,显得更为平稳。差分序列上的ADF检验现在得到一个非常小的p值,有力地表明序列已平稳。高阶差分有时,仅一次差分步骤不足以达到目的。例如,具有二次趋势的数据可能需要进行两次差分才能实现平稳性。二阶差分就是一阶差分的差值:$$ \Delta^2 y_t = \Delta (\Delta y_t) = \Delta(y_t - y_{t-1}) = (y_t - y_{t-1}) - (y_{t-1} - y_{t-2}) $$实际上,通常不需要进行两次以上的差分(即 $d=1$ 或 $d=2$)。你可以通过两次调用 Pandas 中的 .diff() 方法来实现二阶差分:# 二阶差分 differenced_data_2 = data.diff().diff().dropna()在每次差分后,务必(通过视觉检查和使用ADF等检验)检查序列是否平稳。过度差分(差分的次数超过必要)可能会引入不必要的自相关并使建模复杂化。季节性差分如果你的数据表现出季节性,仅进行一阶差分可能不足以消除重复的季节性模式。季节性差分是指计算一个观测值与前一个季节(或周期)中对应观测值之间的差值。如果 $m$ 是季节性周期(例如,月度数据 $m=12$,季度数据 $m=4$,具有周度模式的日数据 $m=7$),则季节性差分为:$$ \Delta_m y_t = y_t - y_{t-m} $$此操作通过将当前值与一个周期前对应的值进行比较来消除季节性成分。在 Pandas 中,你可以使用 .diff() 方法中的 periods 参数执行季节性差分:# 例子:生成具有年度季节性(m=12)的数据 time_monthly = pd.date_range(start='2018-01-01', periods=48, freq='MS') seasonal_component = np.tile(np.sin(np.linspace(0, 2*np.pi, 12)), 4) * 5 trend_monthly = np.linspace(0, 10, 48) noise_monthly = np.random.normal(0, 1, 48) data_monthly = pd.Series(trend_monthly + seasonal_component + noise_monthly, index=time_monthly, name='Monthly Data') # 季节性差分 (m=12) seasonal_diff_data = data_monthly.diff(periods=12).dropna() # 有时需要同时进行普通差分和季节性差分 # 首先进行季节性差分,然后进行普通差分 combined_diff_data = data_monthly.diff(periods=12).diff(periods=1).dropna() # 接下来会进行绘图或ADF检验以确认平稳性...通常,同时具有趋势和季节性的数据可能需要同时进行非季节性的一阶差分($\Delta y_t$)和季节性差分($\Delta_m y_t$)。常用做法是先应用季节性差分,然后对结果应用非季节性差分。积分与ARIMA模型为了实现数据平稳性而进行差分的次数是时间序列建模中的一个重要参数。这个差分阶数在非季节性ARIMA(p, d, q)模型中由参数 'd' 表示,在季节性SARIMA(p, d, q)(P, D, Q)m 模型中由参数 'D' 表示。这些模型会根据这些参数在内部处理差分。当使用依赖差分数据的ARIMA或SARIMA等模型进行预测时,最终预测结果需要转换回原始尺度。这种反向操作称为积分(因此ARIMA中的'I'指代此意),它涉及累加差分。当从已拟合的ARIMA/SARIMA模型生成预测结果时,statsmodels 等库会自动处理这种积分。总之,差分是一种使时间序列数据平稳的基本方法,尤其用于消除趋势和季节性。通过应用一阶、季节性或偶尔二阶差分,你为依赖平稳性假设的模型准备好数据。记住在差分后,始终通过视觉检查和统计检验来确认平稳性。