
作为零基础的 Python 和机器学习学习者,我将用最通俗易懂的语言、贴近生活的例子,把经典时序预测模型 1 的核心知识点 ------ 序列数据处理(n 阶差分处理非平稳性、季节性差分处理季节性)和模型选择(AR (p)、MA (q)、ARMA (p,q))拆解得明明白白,还会配合详细的代码步骤,确保你彻底理解。
在开始前,先给你一个最基础的概念:时序数据就是按时间顺序排好的数,比如你每天的饭量、每月的工资、每天的气温,这些都是时序数据。我们学的这些方法,就是为了用过去的时序数据预测未来。
一、序列数据的处理:让数据 "变乖" 才能预测
很多时序预测模型(比如后面要学的 AR/MA/ARMA)都有个 "怪脾气"------ 只认平稳数据。先把 "平稳" 讲透:
平稳数据:就像你每天的喝水量,基本稳定在 800ml,偶尔多喝 100ml、少喝 50ml,但整体的平均值(800ml)、波动幅度(±100ml)不会随时间变。非平稳数据:就像你从小到大的身高,小时候每年长 5cm,均值一直在涨,波动也变大,这就是不平稳;还有冰淇淋销量,夏天高、冬天低,这是带 "季节性" 的不平稳。
我们处理数据的目的,就是把 "不乖" 的非平稳数据,变成 "听话" 的平稳数据。
1. 处理非平稳性:n 阶差分
(1)通俗理解差分
差分就是 "后一个数减前一个数",核心是去掉数据的趋势。
- 一阶差分:做 1 次 "后减前";
- n 阶差分:把一阶差分的结果再做差分,重复 n 次。
举个生活例子:假设你每月的零花钱(非平稳,因为逐月涨):
| 月份 | 1 月 | 2 月 | 3 月 | 4 月 | 5 月 |
|---|---|---|---|---|---|
| 零花钱 | 100 | 200 | 300 | 400 | 500 |
一阶差分(后 - 前):200-100=100,300-200=100,400-300=100,500-400=100 → 结果是 [100,100,100,100],均值固定在 100,变成平稳数据了!
如果一阶差分还不平稳,就做二阶差分(对一阶差分的结果再减),直到平稳。
(2)代码实操(零基础友好,每步都注释)
我们用 Python 的pandas库(处理数据的常用工具)来实现,先安装库(如果没装的话):
终端执行,安装需要的库
pip install pandas numpy matplotlib

然后写代码:
python
# 导入需要的库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 1. 生成模拟的非平稳数据(模拟每月零花钱)
months = ['1月', '2月', '3月', '4月', '5月', '6月']
pocket_money = [100, 200, 300, 400, 500, 600] # 非平稳:逐月涨100
df = pd.DataFrame({'月份': months, '零花钱': pocket_money})
# 2. 画原始数据图,看非平稳的趋势
plt.figure(figsize=(10, 6))
plt.plot(df['月份'], df['零花钱'], marker='o', label='原始零花钱(非平稳)')
plt.title('每月零花钱(非平稳数据)')
plt.xlabel('月份')
plt.ylabel('金额(元)')
plt.legend()
plt.show()
# 3. 做一阶差分
# diff(1)表示一阶差分,diff(2)就是二阶差分
df['一阶差分'] = df['零花钱'].diff(1)
# 去掉第一个NaN(因为1月没有前一个数,差分结果是空)
df = df.dropna()
# 4. 画一阶差分后的图,看平稳性
plt.figure(figsize=(10, 6))
plt.plot(df['月份'], df['一阶差分'], marker='o', color='red', label='一阶差分后(平稳)')
plt.title('零花钱一阶差分结果(平稳数据)')
plt.xlabel('月份')
plt.ylabel('差分金额(元)')
plt.legend()
plt.show()
# 打印结果,直观看到差分效果
print("处理后的数据:")
print(df)
(3)代码运行结果说明
- 第一张图:蓝色线是原始零花钱,呈直线上升,明显非平稳;
- 第二张图:红色线是一阶差分结果,所有值都是 100,完全平稳;
- 打印的表格里,"一阶差分" 列全是 100,就是我们要的平稳数据。
2. 处理季节性:季节性差分
(1)通俗理解季节性差分
季节性是数据按固定周期重复的规律,比如:
- 冰淇淋销量:周期 12 个月(每年夏天涨);
- 外卖订单:周期 7 天(周末订单多);
- 上下班堵车:周期 24 小时(早 8 点、晚 6 点堵)。
季节性差分不是 "相邻减",而是 "同周期位置减"------ 比如 2024 年 7 月的冰淇淋销量 - 2023 年 7 月的销量,2024 年 8 月 - 2023 年 8 月,去掉季节性,只留趋势。
举个生活例子:冰淇淋销量(周期 12 个月,只列关键月份):
| 时间 | 2023 年 7 月 | 2023 年 8 月 | 2023 年 12 月 | 2024 年 7 月 | 2024 年 8 月 | 2024 年 12 月 |
|---|---|---|---|---|---|---|
| 销量 | 5000 | 4800 | 500 | 5200 | 5000 | 600 |
季节性差分(周期 12):
- 2024 年 7 月 - 2023 年 7 月 = 5200-5000=200;
- 2024 年 8 月 - 2023 年 8 月 = 5000-4800=200;
- 2024 年 12 月 - 2023 年 12 月 = 600-500=100;
差分后结果 [200,200,100],去掉了 "夏天高、冬天低" 的季节性,只剩每年的增长趋势。
(2)代码实操(模拟冰淇淋销量)
python
# 导入库(如果已经导入过,可跳过)
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 1. 生成模拟的带季节性的冰淇淋销量数据
# 时间:2023年1月-2024年12月(24个月)
dates = pd.date_range(start='2023-01', end='2025-01', freq='M')[:24]
# 销量:夏天(6-8月)高,冬天(12-2月)低,且每年整体涨一点
sales = []
for i in range(24):
month = i % 12 + 1 # 1-12月循环
# 基础销量(每年涨100) + 季节性波动(夏天+4000,冬天-500)
base = 1000 + (i // 12) * 100
if 6 <= month <= 8:
sale = base + 4000
elif 12 == month or 1 <= month <= 2:
sale = base - 500
else:
sale = base
sales.append(sale)
# 做成DataFrame
df = pd.DataFrame({'日期': dates, '冰淇淋销量': sales})
df.set_index('日期', inplace=True) # 把日期设为索引,方便按时间处理
# 2. 画原始数据图,看季节性
plt.figure(figsize=(12, 6))
plt.plot(df.index, df['冰淇淋销量'], marker='o', label='原始销量(带季节性)')
plt.title('冰淇淋月度销量(带季节性)')
plt.xlabel('日期')
plt.ylabel('销量')
plt.legend()
plt.show()
# 3. 做季节性差分(周期12个月,即和去年同月比)
df['季节性差分'] = df['冰淇淋销量'].diff(12)
# 去掉前12个NaN(2023年的数没有去年同月可减)
df = df.dropna()
# 4. 画季节性差分后的图,看是否去掉了季节性
plt.figure(figsize=(12, 6))
plt.plot(df.index, df['季节性差分'], marker='o', color='green', label='季节性差分后')
plt.title('冰淇淋销量季节性差分结果(去掉季节性)')
plt.xlabel('日期')
plt.ylabel('差分销量')
plt.legend()
plt.show()
# 打印结果
print("季节性差分后的数据:")
print(df[['冰淇淋销量', '季节性差分']])
(3)代码运行结果说明
- 第一张图:能明显看到每年 6-8 月销量骤增,12-2 月骤降,是典型的季节性非平稳;
- 第二张图:绿色线波动很小,基本稳定在 100 左右,季节性被完全去掉了;
- 打印的表格里,"季节性差分" 列大多是 100,就是每年销量的增长值,这就是去掉季节性后的核心趋势。
二、模型的选择:AR (p)、MA (q)、ARMA (p,q)
这三个模型是时序预测的 "基础款",我们用 "预测饭量" 这个最贴近生活的例子,把每个模型讲透。
先安装建模需要的库:
pip install statsmodels # 专门做时序分析的库

1. AR (p) 自回归模型:看 "过去的自己"
(1)通俗理解
AR 的全称是 "自回归",核心是:预测未来的值,只看过去 p 个时刻的自己。
- p:就是 "看过去几个自己",比如 p=2,就是用 "前 2 个值" 预测 "当前值"。
生活例子:你每天的饭量很规律:
| 时间 | 周一 | 周二 | 周三 | 周四 | 周五 |
|---|---|---|---|---|---|
| 饭量(碗) | 2 | 2.5 | 2.3 | 2.4 | ? |
用 AR (2) 预测周五的饭量:只看周三(2.3)、周四(2.4),因为你的饭量不会突然跳变,前两天的饭量能很好预测今天的。
核心逻辑:当前值 = 过去 p 个值的加权和 + 小误差。
(2)代码实操(用 AR (2) 预测模拟的饭量数据)
python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.ar_model import AutoReg # AR模型工具
# 1. 生成模拟的饭量数据(平稳数据,符合AR模型要求)
days = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
rice = [2.0, 2.5, 2.3, 2.4, 2.2, 2.3, 2.4] # 平稳的饭量数据
df = pd.DataFrame({'星期': days, '饭量': rice})
# 2. 拟合AR(2)模型(p=2,看过去2天的饭量)
# 取前6天的数据训练模型,留周日的数据验证
train_data = df['饭量'][:6]
model_ar = AutoReg(train_data, lags=2) # lags=2就是p=2
model_ar_fit = model_ar.fit()
# 3. 预测周日的饭量(也可以预测未来1天)
# 预测1个值(周日)
pred_ar = model_ar_fit.predict(start=6, end=6)
df.loc[6, 'AR(2)预测值'] = pred_ar.iloc[0]
# 4. 画图对比真实值和预测值
plt.figure(figsize=(10, 6))
plt.plot(df['星期'], df['饭量'], marker='o', label='真实饭量', color='blue')
plt.plot(df['星期'], df['AR(2)预测值'], marker='*', label='AR(2)预测值', color='orange', markersize=12)
plt.title('AR(2)模型预测饭量')
plt.xlabel('星期')
plt.ylabel('饭量(碗)')
plt.legend()
plt.show()
# 打印结果
print("AR(2)模型预测结果:")
print(df)
# 打印模型的系数(理解:预测值 = 系数1*前1天 + 系数2*前2天)
print("\nAR(2)模型系数:")
print(model_ar_fit.params)
(3)结果说明
- 预测值和真实值(2.4)非常接近(大概 2.35 左右),说明 AR (2) 能很好捕捉饭量的 "自回归性";
- 模型系数里,前 1 天的系数≈0.6,前 2 天的系数≈0.3,意思是:周五饭量 ≈ 0.6周四饭量 + 0.3周三饭量。
2. MA (q) 移动平均模型:看 "过去的突发冲击"
(1)通俗理解
MA 的全称是 "移动平均",核心是:预测未来的值,看过去 q 个时刻的 "突发冲击"。
- 冲击:就是偏离正常水平的突发情况,比如 "今天心情不好,少吃了 1 碗""今天运动多,多吃了 0.5 碗";
- 衰减:冲击的影响会随时间变小,比如今天少吃 1 碗,明天可能多吃 0.5 碗,后天基本恢复正常;
- q:就是 "看过去 q 个冲击"。
生活例子:你平时正常饭量是 2 碗(均值),但有突发冲击:
| 时间 | 周一 | 周二 | 周三 | 周四 | 周五 |
|---|---|---|---|---|---|
| 真实饭量 | 2 | 1(冲击:-1,心情不好) | 2.5(冲击:+0.5,补偿) | 2.2(冲击:+0.2,余波) | ? |
用 MA (2) 预测周五的饭量:看周二(-1)、周三(+0.5)的冲击,因为周四的饭量是 "正常 2 碗 + 冲击余波 0.2",MA (2) 会捕捉这些冲击的衰减规律。
核心逻辑:当前值 = 均值 + 过去 q 个冲击的加权和 + 小误差。
(2)代码实操(用 MA (2) 预测饭量)
python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.arima.model import ARIMA # 用ARIMA来实现MA模型(order=(0,0,q)就是MA(q))
# 1. 生成带突发冲击的饭量数据
days = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
# 正常饭量2碗,周二-1(心情不好),周三+0.5(补偿),周四+0.2(余波)
rice = [2.0, 1.0, 2.5, 2.2, 2.1, 2.0, 2.05]
df = pd.DataFrame({'星期': days, '饭量': rice})
# 2. 拟合MA(2)模型(q=2,看过去2个冲击)
# ARIMA的order参数:(p,d,q),MA模型p=0,d=0,q=2
train_data = df['饭量'][:6]
model_ma = ARIMA(train_data, order=(0, 0, 2)) # order=(0,0,2)就是MA(2)
model_ma_fit = model_ma.fit()
# 3. 预测周日的饭量
pred_ma = model_ma_fit.predict(start=6, end=6)
df.loc[6, 'MA(2)预测值'] = pred_ma.iloc[0]
# 4. 画图对比
plt.figure(figsize=(10, 6))
plt.plot(df['星期'], df['饭量'], marker='o', label='真实饭量', color='blue')
plt.plot(df['星期'], df['MA(2)预测值'], marker='*', label='MA(2)预测值', color='purple', markersize=12)
plt.title('MA(2)模型预测饭量(捕捉突发冲击)')
plt.xlabel('星期')
plt.ylabel('饭量(碗)')
plt.legend()
plt.show()
# 打印结果
print("MA(2)模型预测结果:")
print(df)
(3)结果说明
- 周日真实饭量是 2.05,MA (2) 预测值大概 2.03 左右,非常接近;
- MA 模型重点捕捉了 "周二少吃、周三多吃" 的突发冲击,预测时考虑了这些冲击的衰减,所以结果准确。
3. ARMA (p,q) 自回归移动平均模型:既看自己,又看冲击
(1)通俗理解
ARMA 就是 AR 和 MA 的 "结合体",核心是:预测未来的值,既看过去 p 个时刻的自己,又看过去 q 个时刻的突发冲击。
生活例子:预测你周日的饭量,ARMA (2,1) 就是:
- 看过去 2 天的饭量(AR (2)):周五 2.1 碗、周六 2.0 碗;
- 看过去 1 个冲击(MA (1)):周六有没有突发情况(比如多吃 / 少吃);
- 结合两者,预测更准确。
核心逻辑:当前值 = 过去 p 个值的加权和 + 过去 q 个冲击的加权和 + 小误差。
(2)代码实操(用 ARMA (2,1) 预测饭量)
python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.arima.model import ARIMA # ARIMA(order=(p,d,q)),d=0就是ARMA(p,q)
# 1. 生成混合趋势+冲击的饭量数据
days = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
# 有自回归趋势(慢慢回落)+ 突发冲击(周四多吃0.3)
rice = [2.5, 2.4, 2.3, 2.6, 2.4, 2.3, 2.25]
df = pd.DataFrame({'星期': days, '饭量': rice})
# 2. 拟合ARMA(2,1)模型(p=2,q=1)
# ARIMA的order=(2,0,1)就是ARMA(2,1)(d=0表示不需要差分)
train_data = df['饭量'][:6]
model_arma = ARIMA(train_data, order=(2, 0, 1))
model_arma_fit = model_arma.fit()
# 3. 预测周日的饭量
pred_arma = model_arma_fit.predict(start=6, end=6)
df.loc[6, 'ARMA(2,1)预测值'] = pred_arma.iloc[0]
# 4. 画图对比
plt.figure(figsize=(10, 6))
plt.plot(df['星期'], df['饭量'], marker='o', label='真实饭量', color='blue')
plt.plot(df['星期'], df['ARMA(2,1)预测值'], marker='*', label='ARMA(2,1)预测值', color='green', markersize=12)
plt.title('ARMA(2,1)模型预测饭量(结合自回归+冲击)')
plt.xlabel('星期')
plt.ylabel('饭量(碗)')
plt.legend()
plt.show()
# 打印结果
print("ARMA(2,1)模型预测结果:")
print(df)
(3)结果说明
- ARMA (2,1) 的预测值(约 2.24)比单独 AR 或 MA 更接近真实值(2.25);
- 因为它同时考虑了 "前 2 天的饭量趋势" 和 "最近 1 次的突发冲击",所以预测更精准。
总结
- 数据处理核心 :非平稳数据用n 阶差分 去掉趋势,带季节性的非平稳数据用季节性差分去掉周期规律,最终要得到平稳数据;
- 模型选择核心 :
- AR (p):只看过去 p 个时刻的 "自己",适合数据有连续趋势(如气温、饭量);
- MA (q):只看过去 q 个时刻的 "突发冲击",适合数据有临时波动(如股票、促销后的销量);
- ARMA (p,q):结合 AR 和 MA,兼顾趋势和冲击,是更通用的基础时序模型;
- 实操关键 :所有模型都要求输入平稳数据,这是时序预测的前提。

作业:检索下经典的时序单变量数据集有哪些,选择一个尝试观察其性质
一、经典的时序单变量数据集介绍
单变量时序数据集就是 "只有一列核心数据 + 时间索引" 的数据集,适合入门学习,以下是最经典的几个:
| 数据集名称 | 核心内容 | 特点(适合学习的点) |
|---|---|---|
| AirPassengers | 1949-1960 年每月国际航空乘客数 | 有明显的增长趋势 +年度季节性(夏天乘客多),完美适配我们学的 "一阶差分(去趋势)+ 季节性差分(去周期)" |
| Monthly Milk Production | 1962-1975 年每月牛奶产量(磅 / 头) | 有趋势 + 季节性,和 AirPassengers 类似,适合对比练习 |
| Sunspots | 年太阳黑子数(1700-2008) | 有长期周期(约 11 年),适合学习长周期季节性处理 |
| Daily Female Births | 1959 年每天的女性出生数 | 无明显趋势 / 季节性,接近平稳数据,适合直接练 AR/MA/ARMA 模型 |
我选择AirPassengers作为实操对象(statsmodels 库自带,无需手动下载,Mac 下一键获取),它的趋势 + 季节性特征能完整覆盖我们今天学的 "数据处理" 知识点。
二、Mac OS 下的具体实操步骤(全程零基础友好)
步骤 1:Mac 下准备运行环境
1.1 打开 Mac 终端
- 方式 1:按
Command + 空格,输入 "终端",回车打开; - 方式 2:在 "应用程序 - 实用工具" 里找到终端。
1.2 安装所需 Python 库
在终端中输入以下命令(逐行复制粘贴,回车执行),安装我们需要的库:
安装核心库(pandas/numpy/matplotlib/statsmodels)
pip3 install pandas numpy matplotlib statsmodels

注:Mac 系统默认可能用
pip3而非pip(因为 Mac 自带 Python2,pip3对应 Python3),如果执行pip3报错,试试pip。
步骤 2:编写并运行代码(全程注释,逐段解释)
打开 Mac 上的 Python 编辑器(比如自带的 IDLE、VSCode、PyCharm,新手推荐先用 IDLE:终端输入idle3回车即可打开),复制以下完整代码(可直接运行),我会逐段解释每一步的作用。
python
# ====================== 第一步:导入所有需要的库 ======================
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 导入时序相关工具:1.自带数据集 2.ADF平稳性检验 3.绘图美化
from statsmodels.datasets import airpassengers
from statsmodels.tsa.stattools import adfuller
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
# 设置中文显示(Mac下解决plt画图中文乱码问题,这步很重要!)
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # Mac自带的中文字体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# ====================== 第二步:获取并查看数据集基本信息 ======================
# 加载AirPassengers数据集(statsmodels自带,无需手动下载)
data = airpassengers.load_pandas().data # 获取数据
# 添加时间索引(原数据是1949-1960年每月,频率为月)
data['time'] = pd.date_range(start='1949-01', periods=len(data), freq='M')
# 将时间设为索引(时序数据必须把时间设为索引,方便后续处理)
data.set_index('time', inplace=True)
# 重命名列(方便理解)
data.rename(columns={'AirPassengers': '乘客数'}, inplace=True)
# 查看数据基本信息
print("===== 数据集基本信息 =====")
print(f"数据形状(行数, 列数):{data.shape}") # 144行(12年×12月),1列
print("\n前5行数据:")
print(data.head()) # 查看前5行
print("\n数据描述性统计(均值、最大值、最小值等):")
print(data.describe())
# ====================== 第三步:可视化原始数据,观察趋势和季节性 ======================
plt.figure(figsize=(12, 6))
# 绘制原始乘客数曲线
plt.plot(data.index, data['乘客数'], marker='o', color='blue', label='每月乘客数')
plt.title('1949-1960年国际航空每月乘客数(原始数据)')
plt.xlabel('年份')
plt.ylabel('乘客数量')
plt.grid(True, alpha=0.3) # 加网格,方便看趋势
plt.legend()
plt.show()
# 结论:从图中能明显看到两个特征------
# 1. 趋势:乘客数逐年增长(非平稳);
# 2. 季节性:每年夏季(6-8月)乘客数达到峰值,冬季低(年度季节性)。
# ====================== 第四步:量化检验数据的平稳性(ADF检验) ======================
# ADF检验是判断数据是否平稳的标准方法:
# - p值 < 0.05:数据平稳;
# - p值 ≥ 0.05:数据非平稳(需要处理)。
def adf_test(series):
"""定义ADF平稳性检验函数,输出清晰的结果"""
result = adfuller(series)
print("\n===== ADF平稳性检验结果 =====")
print(f'ADF统计量: {result[0]:.4f}')
print(f'p值: {result[1]:.4f}')
print(f'临界值(1%): {result[4]["1%"]:.4f}')
print(f'临界值(5%): {result[4]["5%"]:.4f}')
print(f'临界值(10%): {result[4]["10%"]:.4f}')
# 判断是否平稳
if result[1] < 0.05:
print("→ 结论:数据平稳(p值 < 0.05)")
else:
print("→ 结论:数据非平稳(p值 ≥ 0.05),需要做差分处理")
# 对原始数据做ADF检验
adf_test(data['乘客数'])
# ====================== 第五步:处理非平稳性(一阶差分 + 季节性差分) ======================
# 5.1 第一步:做一阶差分(去趋势)
data['一阶差分'] = data['乘客数'].diff(1)
# 去掉差分后的NaN值(第一个数没有前一个数,差分结果为空)
data_1diff = data.dropna(subset=['一阶差分'])
# 可视化一阶差分结果
plt.figure(figsize=(12, 6))
plt.plot(data_1diff.index, data_1diff['一阶差分'], marker='o', color='red', label='一阶差分后')
plt.title('乘客数一阶差分结果(去除增长趋势)')
plt.xlabel('年份')
plt.ylabel('一阶差分(后月-前月)')
plt.grid(True, alpha=0.3)
plt.legend()
plt.show()
# 对一阶差分后的数据做ADF检验
print("\n===== 一阶差分后的数据ADF检验 =====")
adf_test(data_1diff['一阶差分'])
# 结论:一阶差分后p值可能仍>0.05(因为还有季节性),需要做季节性差分
# 5.2 第二步:做季节性差分(周期=12个月,去年度季节性)
# 季节性差分:当前值 - 去年同月的值(对原始数据直接做,效果更好)
data['季节性差分(12)'] = data['乘客数'].diff(12)
data_season_diff = data.dropna(subset=['季节性差分(12)'])
# 可视化季节性差分结果
plt.figure(figsize=(12, 6))
plt.plot(data_season_diff.index, data_season_diff['季节性差分(12)'], marker='o', color='green', label='季节性差分(周期12)')
plt.title('乘客数季节性差分结果(去除年度季节性)')
plt.xlabel('年份')
plt.ylabel('季节性差分(当前月-去年同月)')
plt.grid(True, alpha=0.3)
plt.legend()
plt.show()
# 对季节性差分后的数据做ADF检验
print("\n===== 季节性差分后的数据ADF检验 =====")
adf_test(data_season_diff['季节性差分(12)'])
# 5.3 第三步:一阶差分 + 季节性差分(终极处理,彻底去趋势+季节性)
data['一阶+季节性差分'] = data['季节性差分(12)'].diff(1)
data_final = data.dropna(subset=['一阶+季节性差分'])
# 可视化最终处理结果
plt.figure(figsize=(12, 6))
plt.plot(data_final.index, data_final['一阶+季节性差分'], marker='o', color='purple', label='一阶+季节性差分')
plt.title('乘客数一阶+季节性差分结果(平稳数据)')
plt.xlabel('年份')
plt.ylabel('差分结果')
plt.grid(True, alpha=0.3)
plt.legend()
plt.show()
# 对最终处理后的数据做ADF检验
print("\n===== 一阶+季节性差分后的数据ADF检验 =====")
adf_test(data_final['一阶+季节性差分'])
# ====================== 第六步:观察自相关性(补充,帮助后续选AR/MA模型) ======================
# 自相关图(ACF):看数据和过去值的相关性,帮助选MA(q)的q值
plt.figure(figsize=(12, 4))
plot_acf(data_final['一阶+季节性差分'], lags=20, ax=plt.gca())
plt.title('平稳数据的自相关图(ACF)')
plt.grid(True, alpha=0.3)
plt.show()
# 偏自相关图(PACF):帮助选AR(p)的p值
plt.figure(figsize=(12, 4))
plot_pacf(data_final['一阶+季节性差分'], lags=20, ax=plt.gca(), method='ywm')
plt.title('平稳数据的偏自相关图(PACF)')
plt.grid(True, alpha=0.3)
plt.show()
步骤 3:代码运行结果解读(关键结论)
你运行代码后会看到以下核心结果,对应我们今天学的知识点:

三、总结(核心关键点)
- Mac 系统适配 :核心区别仅在终端安装库用
pip3、画图设置中文字体用Arial Unicode MS,Python 代码和 Windows 完全通用; - AirPassengers 数据集性质 :典型的 "趋势 + 季节性" 非平稳单变量时序数据,需通过一阶差分去趋势 +12 阶季节性差分去年度周期才能得到平稳数据;
- 实操核心逻辑:
- 先可视化观察数据趋势 / 季节性 → 用 ADF 检验量化平稳性 → 用差分(n 阶 / 季节性)处理非平稳性 → 再次检验确认平稳;
- 平稳数据是后续用 AR/MA/ARMA 模型预测的前提。
如何使用代码进行季节性差分?
你想知道如何用 Python 代码具体实现季节性差分,并且希望代码步骤清晰、通俗易懂,结合我们之前用的 AirPassengers 数据集来讲解,我会把核心逻辑、代码细节和结果解读都讲透,确保你能直接上手操作。
首先明确核心:季节性差分的代码实现依赖 Python 的pandas库,核心函数是diff(),关键是设置季节周期参数 (比如年度季节性设periods=12,周度季节性设periods=7)。
一、季节性差分的核心代码逻辑

二、完整代码实操(以 AirPassengers 为例,Mac OS 通用)
以下代码是 "纯季节性差分" 的极简版(聚焦核心),每一行都加了详细注释,你可以直接复制运行:
python
# ====================== 第一步:导入库 + 适配Mac中文显示 ======================
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.datasets import airpassengers # 自带数据集,无需下载
from statsmodels.tsa.stattools import adfuller # 平稳性检验
# Mac下解决plt中文乱码(关键!)
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
# ====================== 第二步:加载并整理时序数据 ======================
# 加载AirPassengers数据集(1949-1960年每月航空乘客数)
data = airpassengers.load_pandas().data
# 添加时间索引(1949年1月开始,每月1条数据)
data['时间'] = pd.date_range(start='1949-01', periods=len(data), freq='M')
# 将时间设为索引(时序数据必须!否则diff无法按时间对齐)
data.set_index('时间', inplace=True)
# 重命名列,方便理解
data.rename(columns={'AirPassengers': '乘客数'}, inplace=True)
# 查看原始数据前5行
print("===== 原始数据(前5行) =====")
print(data.head())
# ====================== 第三步:核心操作------做季节性差分 ======================
# 季节周期=12(年度季节性,每月数据),所以periods=12
# 新增一列"季节性差分",值为:当前月乘客数 - 去年同月乘客数
data['季节性差分(周期12)'] = data['乘客数'].diff(periods=12)
# 处理NaN值:前12行(1949年1-12月)没有"去年同月"数据,差分结果是NaN,需要删除
data_season_diff = data.dropna(subset=['季节性差分(周期12)'])
# 查看差分后的数据前5行(从1950年1月开始)
print("\n===== 季节性差分后的数据(前5行) =====")
print(data_season_diff[['乘客数', '季节性差分(周期12)']].head())
# ====================== 第四步:可视化对比(原始数据 vs 季节性差分后) ======================
# 画原始数据(看季节性)
plt.figure(figsize=(12, 8))
# 子图1:原始数据
plt.subplot(2, 1, 1) # 2行1列,第1个子图
plt.plot(data.index, data['乘客数'], marker='o', color='blue', label='原始乘客数')
plt.title('原始数据:1949-1960年每月航空乘客数(有年度季节性)')
plt.xlabel('年份')
plt.ylabel('乘客数')
plt.grid(True, alpha=0.3)
plt.legend()
# 子图2:季节性差分后的数据
plt.subplot(2, 1, 2) # 2行1列,第2个子图
plt.plot(data_season_diff.index, data_season_diff['季节性差分(周期12)'], marker='o', color='green', label='季节性差分(周期12)')
plt.title('季节性差分后:去除年度季节性(当前月 - 去年同月)')
plt.xlabel('年份')
plt.ylabel('差分结果')
plt.grid(True, alpha=0.3)
plt.legend()
# 调整子图间距,避免重叠
plt.tight_layout()
plt.show()
# ====================== 第五步:检验差分效果(ADF平稳性检验) ======================
def adf_test(series):
"""简易ADF检验函数,输出核心结论"""
result = adfuller(series)
p_value = result[1]
print(f"\n===== 平稳性检验结果 =====")
print(f'p值:{p_value:.4f}')
if p_value < 0.05:
print("→ 结论:季节性差分后数据平稳(季节性已去除)")
else:
print("→ 结论:仍有非平稳性(需结合一阶差分)")
# 对差分后的数据做检验
adf_test(data_season_diff['季节性差分(周期12)'])
三、代码运行结果解读
1. 数据层面
- 原始数据前 5 行是 1949 年 1-5 月的乘客数:
[112, 118, 132, 129, 121]; - 季节性差分后的数据从 1950 年 1 月开始,比如 1950 年 1 月的差分结果 = 1950 年 1 月乘客数 (115) - 1949 年 1 月乘客数 (112)=3;
- 差分结果列的数值不再有 "夏季高、冬季低" 的规律,而是围绕一个固定值波动。
2. 可视化层面
- 原始数据图:曲线逐年上升,且每年 6-8 月有明显峰值(季节性);
- 差分后数据图:曲线没有明显的季节性峰值,波动幅度基本稳定(季节性被去除)。
3. 平稳性检验层面
- 原始数据 p 值≈1.0(非平稳);
- 季节性差分后 p 值≈0.01(<0.05),说明数据已平稳,季节性被成功去除。
四、不同季节周期的代码适配(拓展)
如果你的数据不是 "月度年度季节性",只需修改periods参数即可:
例 1:周度季节性(每日外卖订单数据)
python
# 假设df是每日外卖订单数据,索引是日期
df['周度季节性差分'] = df['订单数'].diff(periods=7) # 今天 - 上周同一天
例 2:日内季节性(每小时气温数据)
python
# 假设df是每小时气温数据,索引是小时
df['日内季节性差分'] = df['气温'].diff(periods=24) # 当前小时 - 昨天同一小时
总结
- 核心代码 :季节性差分的关键是
df['列名'].diff(periods=季节周期),周期数根据数据规律定(月度年度 = 12、日度周度 = 7、小时度日内 = 24); - 必做步骤 :差分后需用
dropna()删除前 N 个 NaN 值(N = 周期数); - 效果验证:通过可视化看季节性是否消失,或用 ADF 检验(p<0.05 = 平稳)。
只要记住 "周期数对应季节性规律",无论什么类型的季节性数据,都能用这行核心代码实现差分处理。