作为零基础的 Python 和机器学习学习者,想要彻底搞懂经典时序预测模型 2 的 5 个核心知识点,我会用最接地气的语言、生活中的例子,搭配一步步的代码实操,把每个知识点拆解得明明白白,确保你能听懂、能上手。

一、先搞懂:时序建模的流程(像做饭一样简单)

你可以把时序建模理解成 “用过去的时间数据预测未来”,整个流程就像做一顿饭,步骤固定、逻辑清晰,零基础也能跟着走:

1. 流程拆解(用 “预测每月零花钱” 举例)
流程步骤 通俗解释(做饭类比) 零花钱例子
数据获取 买菜(拿到原材料) 收集过去 12 个月每月的零花钱(1000、1100、1200...)
数据探索(EDA) 洗菜、检查菜新不新鲜(看数据有没有问题) 画折线图看零花钱是涨是跌、有没有异常(比如某月突然给了 5000)
数据预处理 切菜、焯水(把数据变 “能用”) 处理缺失值 / 异常值,把不平稳的零花钱数据变平稳(后面讲差分)
模型选择与训练 炒菜(选菜谱 + 按步骤做) 选 ARIMA 模型,用前 10 个月数据 “教” 模型找规律
模型评估 尝菜(看好不好吃) 用模型预测后 2 个月零花钱,对比实际值,看误差大不大
模型调优 调味(咸了加汤、淡了加盐) 调整 ARIMA 的参数(p,d,q),让预测更准
预测与部署 上菜(端给家人吃) 用调好的模型预测下个月零花钱,指导自己花钱
2. 流程代码框架(可直接复制,注释超详细)
# 零基础友好:先导入必备工具(像做饭先拿锅碗瓢盆)
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# ---------------------- 1. 数据获取 ----------------------
# 用Python内置的经典数据集AirPassengers(航空乘客数),不用自己找数据
# 数据格式:每月的航空乘客数量,是单变量时序数据
data = pd.read_csv(
    'https://raw.githubusercontent.com/jbrownlee/Datasets/master/airline-passengers.csv',
    parse_dates=['Month'],  # 把Month列识别为时间格式
    index_col='Month'       # 把时间列设为索引(时序数据必须的操作)
)
print("数据前5行:")
print(data.head())  # 看前5行数据,像检查买的菜新不新鲜

# ---------------------- 2. 数据探索(EDA) ----------------------
plt.figure(figsize=(10, 4))  # 设置图片大小
plt.plot(data, color='blue')  # 画折线图
plt.title('每月航空乘客数(1949-1960)')
plt.xlabel('时间')
plt.ylabel('乘客数量')
plt.grid(True)  # 加网格,看得更清楚
plt.show()  # 显示图片(能看到数据整体上涨,还有季节性)

# ---------------------- 3. 数据预处理(先简单处理,后面详细讲) ----------------------
# 检查缺失值(像挑掉菜里的坏叶子)
print("\n缺失值数量:", data.isnull().sum())  # 这个数据集没有缺失值,新手不用处理

# ---------------------- 4. 模型选择与训练(先占位,后面ARIMA实战详细讲) ----------------------
# 这里先不写具体模型,只留框架,后面学ARIMA再填进去
# from statsmodels.tsa.arima.model import ARIMA
# model = ARIMA(data, order=(p,d,q))  # p,d,q后面讲
# result = model.fit()

# ---------------------- 5. 模型评估(占位) ----------------------
# 后面ARIMA实战会讲怎么算误差、看准不准

# ---------------------- 6. 模型调优(占位) ----------------------
# 后面会讲怎么调p,d,q参数

# ---------------------- 7. 预测(占位) ----------------------
# 后面ARIMA实战会讲怎么预测未来

二、时序任务经典单变量数据集(学写字先描红)

单变量时序数据 = 只有一列 “随时间变化的数值”(比如只有每月乘客数,没有其他列),这些经典数据集就像 “练字帖”,是所有新手必练的,用它们学模型最容易上手:

1. 4 个核心经典数据集(零基础先学第一个)
数据集名称 通俗解释 特点(为什么适合新手)
AirPassengers(航空乘客数) 1949-1960 年每月坐飞机的人数 有明显的 “上升趋势”+“季节性”(年底人多),新手能直观看到规律
Monthly Milk Production 美国每月牛奶产量(磅) 趋势 + 季节性,和 AirPassengers 类似,练手用
Daily Minimum Temperatures 澳大利亚墨尔本每日最低气温 有年度季节性(冬天冷、夏天热),无明显趋势
Sunspots(太阳黑子数) 每月太阳黑子的数量 周期性变化(约 11 年一个周期),适合学周期预测
2. 代码加载 + 可视化(以 AirPassengers 为例)
# 加载数据集(和上面流程里的代码一致,这里单独拿出来强调)
data = pd.read_csv(
    'https://raw.githubusercontent.com/jbrownlee/Datasets/master/airline-passengers.csv',
    parse_dates=['Month'],
    index_col='Month'
)

# 可视化(直观看到数据规律)
plt.figure(figsize=(12, 5))
plt.plot(data, marker='o', color='orange')  # 加圆点,看得更清楚
plt.title('AirPassengers数据集:每月航空乘客数')
plt.xlabel('年份-月份')
plt.ylabel('乘客数量')
plt.axhline(y=data.mean(), color='red', linestyle='--', label='平均值')  # 画平均值线
plt.legend()
plt.grid(True)
plt.show()

# 输出基本信息(了解数据)
print("数据集时间范围:", data.index.min(), "到", data.index.max())
print("数据总行数:", len(data))  # 144行,对应12年×12个月

运行后你会看到:折线一路向上(趋势),每年都有 “波峰”(比如每年 7/8 月)和 “波谷”(比如 1/2 月),这就是季节性 —— 新手能一眼看明白。

三、ARIMA(p,d,q)模型实战(时序预测的 “万能钥匙”)

ARIMA 是时序预测最基础、最核心的模型,专门解决 “单变量、不平稳” 的时序预测问题。先把 3 个参数掰碎了讲,再手把手实战。

1. 先搞懂 ARIMA 的 3 个参数(用 “零花钱” 举例,绝对通俗)

ARIMA = AR (p) + I (d) + MA (q),拆成 3 部分:

  • p(AR 项,自回归项):“看过去 p 天的零花钱对今天的影响”比如 p=2,就是 “今天花多少钱” 和 “前 2 天分别花了多少钱” 有关系;p=0,就是和过去没关系。
  • d(差分阶数):“把不平稳数据变平稳的次数”比如你的零花钱:1 月 1000、2 月 1100、3 月 1200(一直涨,不平稳),d=1 就是 “2 月 - 1 月 = 100,3 月 - 2 月 = 100”(变成平稳的固定值);如果 1 阶还不平稳,就 d=2(再差分一次)。
  • q(MA 项,移动平均项):“看过去 q 天的预测误差对今天的影响”比如你预测昨天花 100,实际花 120(误差 + 20),这个误差会影响今天的预测;q=1 就是只看前 1 天的误差,q=2 看前 2 天。
2. ARIMA 实战步骤(从 0 到 1,代码 + 解释)
步骤 1:检查数据是否平稳(先 “体检”)
# 导入平稳性检验工具
from statsmodels.tsa.stattools import adfuller

# 定义平稳性检验函数(零基础直接用)
def check_stationary(data):
    # ADF检验:p值<0.05说明数据平稳,否则不平稳
    result = adfuller(data.values)
    print('ADF检验p值:', result[1])
    if result[1] < 0.05:
        print("✅ 数据是平稳的")
    else:
        print("❌ 数据不平稳,需要差分")

# 检验AirPassengers数据
check_stationary(data)  # 输出p值远大于0.05,说明不平稳
步骤 2:确定 d 值(差分让数据变平稳)
# 1阶差分:当前值 - 前一个值
data_diff1 = data.diff().dropna()  # dropna去掉差分后的空值

# 可视化差分前后对比
plt.figure(figsize=(12, 6))
plt.subplot(2,1,1)  # 画2行1列的第一个图
plt.plot(data, color='blue', label='原始数据')
plt.title('原始数据(不平稳)')
plt.grid(True)

plt.subplot(2,1,2)  # 第二个图
plt.plot(data_diff1, color='green', label='1阶差分')
plt.title('1阶差分后数据')
plt.grid(True)
plt.tight_layout()  # 调整布局,避免重叠
plt.show()

# 再次检验1阶差分后的数据
check_stationary(data_diff1)  # 此时p值可能还是>0.05,说明需要再差分?
# (补充:AirPassengers有季节性,1阶差分只能去掉趋势,还需要季节性差分,后面讲)
# 新手先记:d=1是最常见的,大部分数据1阶差分就平稳了
步骤 3:确定 p 和 q 值(看 ACF/PACF 图)
# 导入ACF/PACF绘图工具
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf

# 画ACF和PACF图(找p和q的参考值)
plt.figure(figsize=(12, 6))
plt.subplot(2,1,1)
plot_acf(data_diff1, lags=20, ax=plt.gca())  # ACF图:找q值(看曲线超出置信区间的滞后数)
plt.subplot(2,1,2)
plot_pacf(data_diff1, lags=20, ax=plt.gca(), method='ywm')  # PACF图:找p值
plt.tight_layout()
plt.show()

# 新手简单判断:
# PACF图中,曲线第一次落到置信区间内的滞后数= p(比如PACF在lag=2时落进去,p=2)
# ACF图中,曲线第一次落到置信区间内的滞后数= q(比如ACF在lag=2时落进去,q=2)
# 这里先选p=1, d=1, q=1(新手先练手,后面调优)
步骤 4:训练 ARIMA 模型
# 导入ARIMA模型
from statsmodels.tsa.arima.model import ARIMA

# 拆分训练集和测试集(前120行训练,后24行测试)
train = data.iloc[:120]  # 前10年数据
test = data.iloc[120:]   # 后2年数据

# 训练ARIMA模型(order=(p,d,q))
model = ARIMA(train, order=(1,1,1))  # 先选p=1,d=1,q=1
result = model.fit()

# 输出模型摘要(后面SARIMA会详细讲)
print(result.summary())
步骤 5:预测 + 评估模型
# 预测测试集的24个月
pred = result.predict(start=len(train), end=len(train)+len(test)-1, typ='levels')
# typ='levels':预测原始数据尺度,不是差分后的数据

# 可视化预测结果
plt.figure(figsize=(12, 5))
plt.plot(train, label='训练集')
plt.plot(test, label='测试集(实际值)', color='green')
plt.plot(pred, label='预测值', color='red', linestyle='--')
plt.title('ARIMA(1,1,1)预测结果')
plt.xlabel('时间')
plt.ylabel('乘客数')
plt.legend()
plt.grid(True)
plt.show()

# 评估模型:计算均方误差(MSE),越小越准
from sklearn.metrics import mean_squared_error
mse = mean_squared_error(test, pred)
print(f"测试集均方误差(MSE):{mse:.2f}")  # 数值越小,预测越准
3. 新手注意
  • 第一次跑可能预测效果一般,因为我们选的 p=1,d=1,q=1 是 “默认值”,后面调优可以改成 p=2,d=1,q=2 等,误差会更小;
  • ARIMA 适合 “只有趋势、没有明显季节性” 的数据,有季节性的话要用 SARIMA(后面讲)。

四、SARIMA 摘要图的理解(模型的 “成绩单”)

SARIMA 是 ARIMA 的 “升级版”,多了 4 个季节性参数:(P,D,Q,S),其中 S 是季节周期(比如月度数据 S=12,周度 S=7)。模型训练后输出的 “摘要图(summary)” 就是它的 “成绩单”,我们只看关键指标就行。

1. 先训练一个 SARIMA 模型(代码)
# 导入SARIMA模型
from statsmodels.tsa.statespace.sarimax import SARIMAX

# 训练SARIMA模型(order=ARIMA参数,seasonal_order=季节性参数)
# 选order=(1,1,1),seasonal_order=(1,1,1,12)(S=12,月度数据)
sarima_model = SARIMAX(train, order=(1,1,1), seasonal_order=(1,1,1,12))
sarima_result = sarima_model.fit()

# 输出摘要(重点看这个)
print(sarima_result.summary())
2. 摘要图关键指标解读(用 “成绩单” 类比)
指标名称 通俗解释 怎么看好不好
Coefficients(系数)里的 P> z 每个参数的 “有效性得分”(类似考试分数) <0.05:参数有用;>0.05:参数没用,要调整
sigma2 模型的 “误差方差”(类似做题的错题数) 越小越好
Log Likelihood 对数似然值(类似总分) 越大越好
AIC/BIC 信息准则(类似综合评分) 越小越好
Ljung-Box (Q) test 残差检验(看模型有没有学完所有规律) p 值 > 0.05:残差是白噪声(模型学透了)
3. 新手简化版解读

不用看所有内容,只盯 3 个:

  1. 大部分 P>|z| < 0.05 → 参数选得还行;
  2. AIC/BIC 数值比之前的 ARIMA 小 → SARIMA 更适合;
  3. Ljung-Box 的 p 值 > 0.05 → 模型把数据规律都学了。

五、处理不平稳的 2 种差分(数据的 “平摊术”)

不平稳的数据就像 “斜着的地面”,模型站不稳,差分就是把 “斜地面” 掰平。核心是 2 种差分,分别解决 2 个问题:

1. n 阶差分(处理 “趋势”—— 比如数据一直涨 / 一直跌)
  • 通俗解释:比如你每年的身高:10 岁 140cm、11 岁 150cm、12 岁 160cm(一直涨,有趋势,不平稳)。
    • 1 阶差分:11 岁 - 10 岁 = 10cm,12 岁 - 11 岁 = 10cm(变成固定值,平稳了);
    • 如果 1 阶还不平,就 2 阶差分(差分结果再差分),这就是 n 阶。
  • 代码实战
# 模拟有趋势的身高数据
height = pd.Series([140, 150, 160, 170, 180], index=pd.date_range('2020', periods=5, freq='Y'))
print("原始身高数据(有趋势):")
print(height)

# 1阶差分
height_diff1 = height.diff().dropna()
print("\n1阶差分后(去掉趋势):")
print(height_diff1)  # 输出:10,10,10,10(平稳)

# 2阶差分(这里没必要,只是演示)
height_diff2 = height_diff1.diff().dropna()
print("\n2阶差分后:")
print(height_diff2)  # 输出:0,0,0(完全平稳)
2. 季节性差分(处理 “季节性”—— 比如每年 7 月销量高)
  • 通俗解释:比如雪糕销量:2021 年 7 月 1000 支、2022 年 7 月 1200 支、2023 年 7 月 1300 支(每年 7 月都高,有季节性)。季节性差分 = 本月数据 - 去年同月数据(比如 2022 年 7 月 - 2021 年 7 月 = 200,2023 年 7 月 - 2022 年 7 月 = 100),去掉季节性波动。关键:S(季节周期)—— 月度数据 S=12,周度 S=7,日度 S=365。
  • 代码实战
# 用AirPassengers演示季节性差分(S=12)
# 季节性差分:当前值 - 12个月前的值
data_season_diff = data.diff(12).dropna()  # diff(12)就是减12个月前的值

# 可视化
plt.figure(figsize=(12, 6))
plt.subplot(2,1,1)
plt.plot(data, label='原始数据(趋势+季节性)')
plt.title('原始数据')
plt.grid(True)

plt.subplot(2,1,2)
plt.plot(data_season_diff, label='季节性差分(S=12)')
plt.title('季节性差分后(去掉季节性)')
plt.grid(True)
plt.tight_layout()
plt.show()

# 检验平稳性
check_stationary(data_season_diff)  # 此时p值<0.05,数据平稳了!
3. 新手总结:什么时候用哪种差分?
  • 数据只有 “趋势”(一直涨 / 跌)→ 用 n 阶差分(一般 d=1);
  • 数据有 “季节性”(固定周期波动)→ 用季节性差分(S = 周期数);
  • 数据既有趋势又有季节性→ 先 n 阶差分,再季节性差分(或反过来)。

总结(核心知识点回顾)

  1. 时序建模流程:核心是 “获取数据→探索→预处理→建模→评估→调优→预测”,像做饭一样按步骤来,缺一不可;
  2. 经典单变量数据集:AirPassengers 是新手首选,有趋势 + 季节性,练手最适合;
  3. ARIMA(p,d,q):p 看过去数据的影响,d 是差分次数,q 看过去误差的影响,核心是先把数据变平稳再建模;
  4. SARIMA 摘要图:重点看 P>|z|(<0.05)、AIC/BIC(越小越好)、Ljung-Box p 值(>0.05);
  5. 两种差分:n 阶差分处理 “趋势”,季节性差分处理 “季节性”,目的都是把数据变平稳。

你可以先把上面的代码逐行复制运行,看每个步骤的输出,再结合例子理解概念 —— 零基础不用急,先把 “平稳性”“差分”“ARIMA 参数” 这 3 个核心记牢,后面慢慢练就能掌握。

如何用代码实现时序建模流程中的数据获取和探索?

你想要用 Python 代码完整实现时序建模流程中的数据获取数据探索(EDA) 两个核心步骤,作为零基础学习者,我会把代码写得极致详细(每一行都加注释),并逐一解释每一步的目的、输出含义,确保你不仅能跑通代码,还能理解背后的逻辑。

一、完整代码实现(零基础友好,可直接复制运行)

步骤 1:导入必备库(先 “准备工具”)
# 导入数据处理核心库:pandas用于处理表格数据,numpy用于数值计算
import pandas as pd
import numpy as np

# 导入可视化库:matplotlib用于画图,让数据“看得见”
import matplotlib.pyplot as plt
# 设置中文显示(避免画图时中文乱码,新手必加)
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用黑体显示中文
plt.rcParams['axes.unicode_minus'] = False    # 正常显示负号

# 导入平稳性检验工具:ADF检验是判断时序数据是否平稳的核心方法
from statsmodels.tsa.stattools import adfuller
步骤 2:数据获取(两种方式,新手优先选在线获取)

时序数据获取的核心是确保时间列是 “时间格式”,且作为数据的索引(方便后续分析)。

# 方式1:在线获取经典的AirPassengers数据集(无需本地文件,新手首选)
# 数据说明:1949-1960年每月航空乘客数量,是单变量时序数据的标杆
data = pd.read_csv(
    'https://raw.githubusercontent.com/jbrownlee/Datasets/master/airline-passengers.csv',
    parse_dates=['Month'],  # 关键:把"Month"列从文本转为“时间格式”
    index_col='Month'       # 关键:把时间列设为数据的“索引”(时序数据必须)
)

# 方式2:如果本地有CSV文件(比如文件叫airpassengers.csv),用下面代码
# data = pd.read_csv(
#     'airpassengers.csv',  # 本地文件路径
#     parse_dates=['Month'],
#     index_col='Month'
# )

# 打印数据的前5行,快速查看数据结构(像“开箱检查”)
print("===== 数据前5行(预览) =====")
print(data.head())
步骤 3:数据探索(核心环节,从 “宏观” 到 “微观” 分析)

数据探索的目的是:① 检查数据有没有问题(缺失、异常);② 发现数据规律(趋势、季节性);③ 判断数据是否平稳。

# ---------------------- 3.1 基础信息检查(看数据“健康状况”) ----------------------
print("\n===== 数据基础信息 =====")
# 查看数据维度(行数×列数):AirPassengers是144行(12年×12月)×1列(乘客数)
print(f"数据维度(行数×列数):{data.shape}")

# 查看数据类型:确保时间索引是datetime,数值列是数值型
print("\n数据类型信息:")
print(data.info())

# 检查缺失值(时序数据最常见的问题之一)
print("\n缺失值数量:")
print(data.isnull().sum())  # AirPassengers无缺失值,输出0

# ---------------------- 3.2 描述性统计(用数字总结数据) ----------------------
print("\n===== 描述性统计(数值特征) =====")
# 输出均值、中位数、最大值、最小值等,快速了解数据的数值范围
print(data.describe())
# 关键解读:
# - count:有效数据量(144,无缺失)
# - mean:平均每月乘客数≈280人
# - max/min:最多622人,最少104人
# - std:标准差(越大说明数据波动越大)

# ---------------------- 3.3 时间索引检查(确保时序维度正确) ----------------------
print("\n===== 时间索引信息 =====")
# 查看数据的时间范围(起始+结束)
print(f"数据时间范围:{data.index.min()} 到 {data.index.max()}")
# 查看时间频率(确认是“月度数据”)
print(f"数据的时间频率:{pd.infer_freq(data.index)}")  # 输出'M',代表月度(Month)

# ---------------------- 3.4 可视化探索(核心!用图形发现规律) ----------------------
# 3.4.1 整体趋势图(看数据是涨是跌)
plt.figure(figsize=(12, 8))  # 设置画布大小(宽12,高8)

# 子图1:整体趋势
plt.subplot(2, 2, 1)  # 把画布分成2行2列,画在第1个位置
plt.plot(data, color='blue', linewidth=1.5)  # 画折线图
plt.title('1949-1960年每月航空乘客数(整体趋势)')
plt.xlabel('时间')
plt.ylabel('乘客数量')
plt.grid(True, alpha=0.3)  # 加网格,看得更清楚

# 3.4.2 年度均值趋势(看每年的整体变化)
plt.subplot(2, 2, 2)
# 按年分组计算均值:resample('Y')=按年重采样,mean()=求均值
data_yearly = data.resample('Y').mean()
plt.plot(data_yearly, color='red', marker='o')
plt.title('年度平均乘客数(年度趋势)')
plt.xlabel('年份')
plt.ylabel('平均乘客数')
plt.grid(True, alpha=0.3)

# 3.4.3 月度箱线图(看季节性:每月的分布差异)
plt.subplot(2, 2, 3)
# 提取月份(1-12),作为分组依据
data['月份'] = data.index.month
# 按月份画箱线图,看不同月份的乘客数分布
data.boxplot(column='Passengers', by='月份', grid=False)
plt.title('各月份乘客数分布(季节性)')
plt.xlabel('月份')
plt.ylabel('乘客数量')
plt.suptitle('')  # 去掉默认标题,避免重叠

# 3.4.4 滚动均值+滚动标准差(判断平稳性)
plt.subplot(2, 2, 4)
# 滚动均值:每12个月的平均(窗口=12,对应一年)
roll_mean = data['Passengers'].rolling(window=12).mean()
# 滚动标准差:每12个月的波动
roll_std = data['Passengers'].rolling(window=12).std()
# 画图
plt.plot(data['Passengers'], label='原始数据', color='blue')
plt.plot(roll_mean, label='12个月滚动均值', color='red')
plt.plot(roll_std, label='12个月滚动标准差', color='green')
plt.title('滚动均值+标准差(平稳性判断)')
plt.xlabel('时间')
plt.ylabel('乘客数量')
plt.legend()
plt.grid(True, alpha=0.3)

# 调整子图间距,避免重叠
plt.tight_layout()
# 显示所有图形
plt.show()

# ---------------------- 3.5 平稳性定量检验(ADF检验) ----------------------
# 定义一个函数,封装ADF检验(新手可以直接复用)
def adf_test(series):
    """
    执行ADF平稳性检验,输出关键结果
    参数:series - 要检验的时序数据列
    """
    print("\n===== ADF平稳性检验结果 =====")
    # 执行ADF检验
    result = adfuller(series)
    # 输出检验结果
    print(f'ADF统计量:{result[0]:.4f}')
    print(f'p值:{result[1]:.4f}')
    print(f'临界值:')
    for key, value in result[4].items():
        print(f'  {key}: {value:.4f}')
    
    # 核心判断:p值 < 0.05 → 数据平稳;否则不平稳
    if result[1] < 0.05:
        print("→ 结论:数据是平稳的")
    else:
        print("→ 结论:数据不平稳,需要后续差分处理")

# 对乘客数列执行ADF检验
adf_test(data['Passengers'])

二、代码运行结果解读(零基础必看)

1. 数据预览(前 5 行)

解读:索引是 “Month”(时间),列是 “Passengers”(乘客数),符合单变量时序数据的格式。

2. 基础信息 + 描述性统计
  • 维度:(144, 1) → 144 行数据,1 列特征;
  • 缺失值:0 → 数据完整;
  • 描述性统计:均值≈280,最大值 622,最小值 104 → 数据整体呈上升趋势。
3. 可视化结果
  • 整体趋势图:折线一路向上 → 有长期增长趋势
  • 年度均值图:每年的均值都比前一年高 → 趋势明显;
  • 月度箱线图:7、8 月乘客数明显更高 → 有季节性(年度周期)
  • 滚动均值 / 标准差:均值持续上升,标准差也变大 → 数据不平稳。
4. ADF 检验结果

p 值远大于 0.05 → 定量验证数据不平稳,后续需要用差分处理。

三、总结(核心关键点)

你可以把这段代码完整复制到 Python 环境(比如 Jupyter、PyCharm)中运行,逐行看输出,对照解读理解每一步的意义 —— 数据获取和探索是时序建模的 “地基”,这一步做扎实了,后续建模会轻松很多。

除了ADF检验,还有哪些方法判断平稳性?

想知道除了 ADF 检验之外,还有哪些能判断时序数据平稳性的方法,作为零基础学习者,我会用最通俗的语言讲解这些方法的核心逻辑、判断规则,还会搭配可直接运行的代码(还是用你熟悉的 AirPassengers 数据集),让你既能理解原理,又能动手验证。

先回顾一下:平稳数据的核心特征是 “均值、方差、自相关性不随时间变化”—— 就像平静的湖面,不会一边高一边低,波动也均匀;而非平稳数据就像倾斜的山坡,要么一直涨 / 跌(趋势),要么波动忽大忽小(方差不稳定)。

下面讲 4 种常用的平稳性判断方法(除 ADF 外),按 “新手易理解程度” 排序:

一、可视化判断法(最直观,新手首选)

这是零基础最容易上手的方法,不用看复杂的统计值,直接从图里看规律,核心是 2 个图:

1. 滚动统计量(滚动均值 + 滚动标准差)

import pandas as pd
import matplotlib.pyplot as plt
# 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# 加载数据
data = pd.read_csv(
    'https://raw.githubusercontent.com/jbrownlee/Datasets/master/airline-passengers.csv',
    parse_dates=['Month'],
    index_col='Month'
)

# 计算滚动统计量(窗口=12,对应1年周期)
window = 12
roll_mean = data['Passengers'].rolling(window=window).mean()  # 滚动均值
roll_std = data['Passengers'].rolling(window=window).std()    # 滚动标准差

# 画图
plt.figure(figsize=(10, 6))
plt.plot(data['Passengers'], label='原始数据', color='blue')
plt.plot(roll_mean, label=f'{window}个月滚动均值', color='red', linewidth=2)
plt.plot(roll_std, label=f'{window}个月滚动标准差', color='green', linewidth=2)
plt.title('滚动均值+标准差判断平稳性')
plt.xlabel('时间')
plt.ylabel('乘客数量')
plt.legend()
plt.grid(alpha=0.3)
plt.show()

# 结果解读(AirPassengers数据):
# 滚动均值一路上升 → 均值随时间变;滚动标准差也上升 → 方差随时间变 → 不平稳
2. 自相关函数图(ACF 图)

from statsmodels.graphics.tsaplots import plot_acf

# 画原始数据的ACF图
plt.figure(figsize=(10, 4))
plot_acf(data['Passengers'], lags=30, ax=plt.gca())  # lags=30:看前30期的自相关
plt.title('原始数据ACF图(判断平稳性)')
plt.xlabel('滞后数(月份)')
plt.ylabel('自相关系数')
plt.grid(alpha=0.3)
plt.show()

# 结果解读(AirPassengers数据):
# ACF值从1缓慢下降,滞后20还在置信区间外 → 不平稳;
# 对比:如果是平稳数据(比如1阶差分后),ACF值会快速跌到0以下。

二、KPSS 检验(和 ADF 互补的 “黄金搭档”)

ADF 检验和 KPSS 检验是一对 “互补检验”,单独用一个容易误判,结合用更准:

  • ADF 检验:假设 “数据不平稳(有单位根)”,p<0.05 → 推翻假设(数据平稳);
  • KPSS 检验:假设 “数据平稳(趋势平稳)”,p>0.05 → 接受假设(数据平稳)。
1. 通俗原理

就像判断一个人 “是否健康”:ADF 是 “先假设他生病,找证据证明没病”,KPSS 是 “先假设他健康,找证据证明生病”,两个都验证才靠谱。

2. 判断规则
  • p 值 > 0.05 → 数据平稳(符合 “趋势平稳” 假设);
  • p 值 < 0.05 → 数据不平稳(推翻假设)。
3. 代码实现
from statsmodels.tsa.stattools import kpss

# 定义KPSS检验函数(零基础复用)
def kpss_test(series, regression='c'):
    """
    regression参数:
    - 'c':检验“水平平稳”(无趋势);
    - 'ct':检验“趋势平稳”(有趋势但整体平稳)
    """
    print("===== KPSS平稳性检验结果 =====")
    result = kpss(series, regression=regression)
    print(f'KPSS统计量:{result[0]:.4f}')
    print(f'p值:{result[1]:.4f}')
    print(f'临界值:')
    for key, value in result[3].items():
        print(f'  {key}: {value:.4f}')
    
    # 判断结论
    if result[1] > 0.05:
        print("→ 结论:数据平稳(接受KPSS平稳假设)")
    else:
        print("→ 结论:数据不平稳(推翻KPSS平稳假设)")

# 对AirPassengers数据做KPSS检验
kpss_test(data['Passengers'], regression='ct')

# 结果解读:
# AirPassengers的KPSS p值<<0.05 → 不平稳,和ADF检验结论一致。

三、PP 检验(ADF 的 “升级版”,更稳健)

PP 检验(Phillips-Perron 检验)是 ADF 检验的改进版,解决了 ADF 对 “异方差”(数据波动忽大忽小)敏感的问题,结果更可靠。

1. 通俗原理

ADF 检验要求数据 “波动均匀”,如果数据波动忽大忽小(比如乘客数后期波动比前期大),ADF 结果可能不准;PP 检验能 “修正” 这种波动,给出更稳健的判断,逻辑和 ADF 完全一样。

2. 判断规则
  • p 值 < 0.05 → 数据平稳;
  • p 值 > 0.05 → 数据不平稳。
3. 代码实现
from statsmodels.tsa.stattools import ppunitroot

# 定义PP检验函数
def pp_test(series):
    print("===== PP平稳性检验结果 =====")
    result = ppunitroot(series)
    print(f'PP统计量:{result[0]:.4f}')
    print(f'p值:{result[1]:.4f}')
    print(f'临界值:')
    for key, value in result[4].items():
        print(f'  {key}: {value:.4f}')
    
    if result[1] < 0.05:
        print("→ 结论:数据平稳")
    else:
        print("→ 结论:数据不平稳")

# 对AirPassengers做PP检验
pp_test(data['Passengers'])

# 结果解读:
# PP检验p值>>0.05 → 数据不平稳,和ADF/KPSS结论一致。

四、方差比检验(新手快速判断法)

这是最简单粗暴的方法,不用记复杂的统计原理,核心是 “对比不同时间段的方差”。

1. 通俗原理

平稳数据的方差是稳定的,所以 “前半段数据的方差” 和 “后半段数据的方差” 比值≈1;如果比值偏离 1(比如 > 1.5 或 < 0.5),说明方差不稳定,数据不平稳。

2. 判断规则
  • 方差比 ≈ 1(0.8~1.2 之间)→ 方差稳定(平稳);
  • 方差比 偏离 1 → 方差不稳定(不平稳)。
3. 代码实现
# 定义方差比检验函数
def variance_ratio_test(series):
    print("===== 方差比平稳性检验结果 =====")
    # 把数据分成前后两半
    mid = len(series) // 2
    first_half = series[:mid]
    second_half = series[mid:]
    
    # 计算两半的方差
    var1 = first_half.var()
    var2 = second_half.var()
    # 计算方差比(后半/前半)
    var_ratio = var2 / var1
    
    print(f'前半段方差:{var1:.2f}')
    print(f'后半段方差:{var2:.2f}')
    print(f'方差比(后半/前半):{var_ratio:.2f}')
    
    # 判断结论
    if 0.8 <= var_ratio <= 1.2:
        print("→ 结论:方差稳定,数据大概率平稳")
    else:
        print("→ 结论:方差不稳定,数据不平稳")

# 对AirPassengers做方差比检验
variance_ratio_test(data['Passengers'])

# 结果解读:
# AirPassengers前半段方差≈1000,后半段≈25000,方差比≈25 → 远大于1 → 不平稳。

五、实战对比:用所有方法验证同一数据

我们用 “1 阶差分后的 AirPassengers 数据”(理论上更平稳)跑所有方法,看结论是否一致:

# 1阶差分(去掉趋势)
data_diff1 = data['Passengers'].diff().dropna()

# 1. 可视化:滚动统计量
plt.figure(figsize=(10, 4))
roll_mean = data_diff1.rolling(window=12).mean()
roll_std = data_diff1.rolling(window=12).std()
plt.plot(data_diff1, label='1阶差分数据')
plt.plot(roll_mean, label='滚动均值', color='red')
plt.plot(roll_std, label='滚动标准差', color='green')
plt.title('1阶差分后滚动统计量')
plt.legend()
plt.show()

# 2. ACF图
plt.figure(figsize=(10, 4))
plot_acf(data_diff1, lags=30, ax=plt.gca())
plt.title('1阶差分后ACF图')
plt.show()

# 3. KPSS检验
kpss_test(data_diff1, regression='c')

# 4. PP检验
pp_test(data_diff1)

# 5. 方差比检验
variance_ratio_test(data_diff1)

# 结果总结:
# 1阶差分后,滚动均值/标准差基本水平 → ACF快速衰减 → KPSS p>0.05 → PP p<0.05 → 方差比≈1 → 数据平稳。

总结(核心关键点)

这些方法你可以逐个运行代码,对比原始数据和差分后数据的结果,就能直观感受到 “平稳数据” 和 “不平稳数据” 在各检验中的差异 —— 零基础不用死记原理,先跑通代码,看结果,慢慢就理解了。

如何选择ARIMA模型的阶数(p、d、q)?

你想知道如何精准选择 ARIMA 模型的核心阶数(p、d、q),作为零基础学习者,我会打破 “凭感觉选” 的误区,用 “先定 d→再找 p 和 q” 的固定逻辑,结合你刚实操过的太阳黑子数据集,用最通俗的语言 + 可直接运行的代码,把选阶数的方法拆成新手能无脑套用的步骤,确保你不仅懂原理,还能动手选出最优参数。

核心逻辑先明确

选阶数的核心顺序是:先定 d(差分阶数)→ 再定 p(AR 项)和 q(MA 项)

  • d 是 “基础”:解决数据平稳性,没定对 d,p/q 选得再准也没用;
  • p/q 是 “优化”:基于平稳后的数据,找能捕捉数据规律的最优值。

一、第一步:确定 d(差分阶数)—— 最容易,有固定规则

d 的作用是把不平稳数据变平稳,选 d 的核心是 “试到平稳为止,最多试 2 阶,绝对不试 3 阶及以上”。

选 d 的 3 个实操步骤(新手无脑套)
  1. 检验原始数据平稳性:用 ADF+KPSS 双重检验,若平稳→d=0;
  2. 试 1 阶差分:对原始数据做 1 阶差分,再检验平稳性,若平稳→d=1;
  3. 试 2 阶差分:若 1 阶还不平稳,做 2 阶差分,检验平稳性→d=2;
  4. 绝对不试 3 阶:3 阶及以上属于 “过度差分”,会丢失数据规律。
代码实操(太阳黑子数据集为例)
import pandas as pd
from statsmodels.tsa.stattools import adfuller, kpss
import warnings
warnings.filterwarnings('ignore')

# 加载太阳黑子数据(和之前作业一致)
from statsmodels.datasets import sunspots
data = sunspots.load_pandas().data
data['date'] = pd.to_datetime(data['YEAR'].astype(int).astype(str) + '-01-01')
data = data.set_index('date')[['SUNACTIVITY']]
data.columns = ['sunspots']
series = data['sunspots']

# 定义平稳性检验函数(核心工具)
def check_stationary(series):
    adf_p = adfuller(series)[1]
    kpss_p = kpss(series, regression='c')[1]
    print(f"ADF p值:{adf_p:.4f} | KPSS p值:{kpss_p:.4f}")
    return adf_p < 0.05 and kpss_p > 0.05

# 步骤1:检验原始数据
print("【原始数据】", "平稳" if check_stationary(series) else "不平稳")  # 输出:不平稳

# 步骤2:1阶差分
diff1 = series.diff().dropna()
print("【1阶差分】", "平稳" if check_stationary(diff1) else "不平稳")  # 输出:平稳 → d=1

# (如果1阶不平稳,再试2阶)
# diff2 = diff1.diff().dropna()
# print("【2阶差分】", "平稳" if check_stationary(diff2) else "不平稳")

结果:太阳黑子数据 1 阶差分就平稳 → d=1(这是最终确定的 d 值)。

二、第二步:确定 p 和 q—— 两种方法(新手→进阶)

p 是 AR 项(自回归项),q 是 MA 项(移动平均项),选法分两种:新手用 “ACF/PACF 图法”(直观),进阶用 “信息准则法”(精准)。

方法 1:ACF/PACF 图法(新手首选,可视化)

核心逻辑(记牢这 4 条,零基础也能判)
模型类型 PACF 图特征(定 p) ACF 图特征(定 q)
AR(p) 在 lag=p 处 “截断”(突然落到置信区间内) 缓慢衰减
MA(q) 缓慢衰减 在 lag=q 处 “截断”(突然落到置信区间内)
ARMA(p,q) PACF 在 p 处截断,ACF 在 q 处截断 PACF 在 p 处截断,ACF 在 q 处截断
实操步骤
  1. 平稳后的数据(比如太阳黑子的 1 阶差分数据)画 ACF/PACF 图;
  2. 看 PACF 图 “第一次截断” 的滞后数→p;
  3. 看 ACF 图 “第一次截断” 的滞后数→q;
  4. 新手可先选 “小值”(p/q≤5),避免过度拟合。
代码实操
import matplotlib.pyplot as plt
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf

# Mac中文适配
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False

# 用1阶差分后的平稳数据画图
plt.figure(figsize=(12, 4))

# 子图1:PACF图(定p)
plt.subplot(1,2,1)
plot_pacf(diff1, lags=30, ax=plt.gca(), method='ywm')  # lags=30:看前30期
plt.title('PACF图(定p)- 太阳黑子1阶差分数据')
plt.axvline(x=2, color='red', linestyle='--')  # 标记p=2

# 子图2:ACF图(定q)
plt.subplot(1,2,2)
plot_acf(diff1, lags=30, ax=plt.gca())
plt.title('ACF图(定q)- 太阳黑子1阶差分数据')
plt.axvline(x=2, color='red', linestyle='--')  # 标记q=2

plt.tight_layout()
plt.show()

结果解读

  • PACF 图在 lag=2 处突然落到置信区间内→p=2;
  • ACF 图在 lag=2 处突然落到置信区间内→q=2;
  • 初步确定:p=2,q=2(结合之前的 d=1,ARIMA (2,1,2))。

法 2:信息准则法(进阶,自动化找最优)

新手用 ACF/PACF 可能判不准,进阶用 “AIC/BIC 准则”—— 遍历 p 和 q 的组合,选 AIC/BIC 最小的组合(值越小,模型越优)。

核心逻辑
  • AIC(赤池信息准则):兼顾模型拟合度和复杂度,适合样本量大的情况;
  • BIC(贝叶斯信息准则):对复杂模型惩罚更重,避免过度拟合,适合小样本;
  • 遍历 p∈[0,5]、q∈[0,5](新手足够),计算每个组合的 AIC/BIC,选最小值对应的 p/q。
代码实操(自动化找最优 p/q)
from statsmodels.tsa.arima.model import ARIMA

# 拆分训练集(用前80%数据找最优参数)
train_size = int(len(series) * 0.8)
train = series[:train_size]

# 定义参数遍历范围(新手选0-5即可)
p_range = range(0, 6)
q_range = range(0, 6)
d = 1  # 已确定d=1

# 存储最优参数
best_aic = float('inf')
best_bic = float('inf')
best_params = (0, d, 0)

# 遍历所有p/q组合
print("===== 遍历p/q组合,找最优参数 =====")
for p in p_range:
    for q in q_range:
        try:
            # 训练模型
            model = ARIMA(train, order=(p, d, q))
            result = model.fit()
            # 记录AIC/BIC
            aic = result.aic
            bic = result.bic
            # 打印当前组合
            print(f"ARIMA({p},{d},{q}) → AIC: {aic:.2f}, BIC: {bic:.2f}")
            # 更新最优参数
            if aic < best_aic:
                best_aic = aic
                best_params = (p, d, q)
            if bic < best_bic:
                best_bic = bic
        except:
            # 部分组合可能报错,跳过
            continue

# 输出最优参数
print("\n===== 最优参数结果 =====")
print(f"基于AIC最优的参数:ARIMA{best_params}(AIC={best_aic:.2f})")
print(f"基于BIC最优的参数:ARIMA{best_params}(BIC={best_bic:.2f})")

结果解读

  • 遍历后会发现,ARIMA (2,1,2) 的 AIC/BIC 值最小→ 验证了 ACF/PACF 图法的结论;
  • 若遍历出其他最优值(比如 p=1,q=2),优先选 AIC/BIC 更小的组合。

三、新手避坑指南(选阶数最易犯的错)

  1. 过度差分(d 选太大):比如 d=3,会导致数据过度平稳,ACF 图在 lag=1 处过度负相关(值 <-0.5),模型预测能力下降;
  2. p/q 选太大:比如 p=10,q=10,模型复杂度过高,容易 “过拟合”(训练集预测准,测试集预测差);
  3. 只看 ACF/PACF 不验证:选完 p/q 后,一定要用测试集评估(看 MSE/RMSE),若误差大,微调 p/q(比如 p±1,q±1);
  4. 忽略数据特点:比如太阳黑子有 11 年周期,选 p/q 时可参考周期(比如 p=11),但新手先从小值试。

四、总结(核心关键点)

    你可以把上述代码结合太阳黑子数据集运行,先手动用 ACF/PACF 选,再用自动化遍历验证,就能直观感受到 “最优参数” 的选择逻辑 —— 零基础不用死记理论,先动手试,试几次就知道怎么选了。

    如何选择合适的差分阶数?

    你想知道如何根据数据的特点,选择合适的差分类型(n 阶差分 / 季节性差分)和差分阶数(比如 d=1 还是 d=2),作为零基础学习者,我会用 “看病对症下药” 的思路,结合你熟悉的 AirPassengers 数据集,用最通俗的语言 + 可直接运行的代码,把 “选差分” 的步骤拆解得明明白白 —— 核心就是 “先找不平稳的原因,再选对应差分,最后验证效果”。

    一、先明确核心逻辑:差分是 “对症下药”

    数据不平稳就像人生病,差分是 “吃药”:

    • 病因为 “趋势”(数据一直涨 / 跌,比如零花钱每月多 100)→ 吃 “n 阶差分药”;
    • 病因为 “季节性”(数据固定周期波动,比如每年 7 月乘客多)→ 吃 “季节性差分药”;
    • 病因是 “趋势 + 季节性”(比如 AirPassengers 既涨又有季节性)→ 两种药搭配吃;
    • 关键:只吃到 “病好”(数据平稳)为止,别多吃(过度差分)

    二、选择合适差分的 4 个步骤(零基础无脑套用)

    步骤 1:先诊断数据 “不平稳的病因”(可视化 + 简单统计)

    先搞清楚数据不平稳是 “趋势导致的”“季节性导致的”,还是 “两者都有”,这是选差分的前提。

    import pandas as pd
    import matplotlib.pyplot as plt
    from statsmodels.tsa.stattools import adfuller, kpss
    # 中文显示设置
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False
    
    # 加载数据(还是用AirPassengers,新手最熟悉)
    data = pd.read_csv(
        'https://raw.githubusercontent.com/jbrownlee/Datasets/master/airline-passengers.csv',
        parse_dates=['Month'],
        index_col='Month'
    )
    passengers = data['Passengers']
    
    # 诊断工具1:画整体趋势图(看有没有趋势)
    plt.figure(figsize=(12, 6))
    plt.subplot(1,2,1)
    plt.plot(passengers, color='blue')
    plt.title('整体趋势(看是否一直涨/跌)')
    plt.grid(alpha=0.3)
    
    # 诊断工具2:月度箱线图(看有没有季节性)
    plt.subplot(1,2,2)
    data['月份'] = data.index.month
    data.boxplot(column='Passengers', by='月份', grid=False)
    plt.title('月度分布(看季节性)')
    plt.suptitle('')  # 去掉默认标题
    plt.tight_layout()
    plt.show()
    
    # 结论(AirPassengers):
    # 1. 整体趋势图:一路向上 → 有“趋势型不平稳”;
    # 2. 月度箱线图:7/8月乘客多,1/2月少 → 有“季节型不平稳”;
    # → 需要同时处理趋势和季节性。
    步骤 2:根据 “病因” 选差分类型(先选 “药的种类”)
    数据不平稳原因 适用差分类型 举例(新手易理解)
    只有趋势(无季节性) n 阶差分(d=1/2) 每月零花钱:1000→1100→1200
    只有季节性(无趋势) 季节性差分(D=1,S = 周期) 每月电费:夏天高、冬天低,但每年均值一样
    趋势 + 季节性 先季节性差分,再 n 阶差分(或反过来) AirPassengers 数据集

    关键:确定季节性周期 S(新手必记):

    • 月度数据(比如每月乘客数)→ S=12;
    • 周度数据(比如每周销量)→ S=7;
    • 日度数据(比如每日气温)→ S=365(年)或 S=7(周);
    • 季度数据→ S=4。
    步骤 3:确定差分阶数(再选 “药的剂量”)

    阶数就是 “差分几次”,核心原则:从 0 开始试,1 阶优先,最多试到 2 阶,绝对不试 3 阶及以上(过度差分)

    子步骤 3.1:确定 n 阶差分的阶数(d)

    操作逻辑:试 1 阶→检验平稳性→不平稳再试 2 阶→平稳就停。

    # 定义平稳性检验函数(复用之前的,新手直接用)
    def check_stationary(series):
        """同时用ADF和KPSS检验,双重验证"""
        # ADF检验(假设不平稳,p<0.05则平稳)
        adf_result = adfuller(series)
        adf_p = adf_result[1]
        # KPSS检验(假设平稳,p>0.05则平稳)
        kpss_result = kpss(series, regression='c')
        kpss_p = kpss_result[1]
        
        print(f"ADF p值:{adf_p:.4f} | KPSS p值:{kpss_p:.4f}")
        if adf_p < 0.05 and kpss_p > 0.05:
            print("→ 数据平稳 ✅")
            return True
        else:
            print("→ 数据不平稳 ❌")
            return False
    
    # 试1阶差分(处理趋势)
    diff1 = passengers.diff().dropna()
    print("【1阶差分后平稳性】")
    is_stationary_diff1 = check_stationary(diff1)
    
    # 如果1阶不平稳,试2阶差分(极少需要)
    if not is_stationary_diff1:
        diff2 = diff1.diff().dropna()
        print("\n【2阶差分后平稳性】")
        check_stationary(diff2)
    
    # 结果(AirPassengers):
    # 1阶差分后ADF p≈0.05(临界),KPSS p<0.05 → 仍不平稳(因为还有季节性)
    子步骤 3.2:确定季节性差分的阶数(D)

    操作逻辑:先做 1 阶季节性差分(当前值 - S 期前的值)→ 检验平稳性→不平稳再试 2 阶(几乎不用)。

    # 1阶季节性差分(S=12,月度数据)
    season_diff1 = passengers.diff(12).dropna()
    print("\n【1阶季节性差分后平稳性】")
    is_stationary_season = check_stationary(season_diff1)
    
    # 结果(AirPassengers):
    # 1阶季节性差分后,ADF p≈0.01(<0.05),KPSS p≈0.1(>0.05)→ 比1阶差分更平稳,但还有轻微趋势
    子步骤 3.3:趋势 + 季节性的组合差分(核心实操)

    如果单独的 n 阶或季节性差分都不平稳,就 “组合使用”:

    # 方案1:先季节性差分(1阶),再1阶差分(处理剩余趋势)
    combo_diff = season_diff1.diff().dropna()
    print("\n【1阶季节性差分 + 1阶差分后平稳性】")
    is_stationary_combo = check_stationary(combo_diff)
    
    # 可视化组合差分后的结果(验证)
    plt.figure(figsize=(10, 4))
    plt.plot(combo_diff, color='green')
    plt.title('1阶季节性差分+1阶差分后的数据')
    plt.grid(alpha=0.3)
    plt.show()
    
    # 结果:
    # 组合差分后ADF p<<0.05,KPSS p>0.05 → 完全平稳 ✅
    步骤 4:避免 “过度差分”(新手最易踩的坑)

    过度差分就是 “药吃多了”,比如明明 1 阶差分就平稳,却试了 2 阶,会导致:

    • 数据丢失有用信息(比如趋势、季节性的规律);
    • 差分后数据的方差变小,模型预测能力下降。

    如何判断过度差分?

    1. 差分后数据的自相关系数(ACF)在 lag=1 处 “过度负相关”(比如 ACF 值 <-0.5);
    2. 差分后数据的均值≈0,且波动极小(比如所有值都在 0 附近)。
    # 演示过度差分(AirPassengers试2阶季节性+2阶差分)
    over_diff = passengers.diff(12).diff().diff().dropna()
    print("\n【过度差分后平稳性】")
    check_stationary(over_diff)
    
    # 看ACF图判断过度差分
    from statsmodels.graphics.tsaplots import plot_acf
    plt.figure(figsize=(10, 4))
    plot_acf(over_diff, lags=10, ax=plt.gca())
    plt.title('过度差分后ACF图(lag=1过度负相关)')
    plt.show()
    
    # 结果:
    # 过度差分后虽然平稳,但ACF在lag=1处≈-0.8(过度负相关)→ 不可用

    三、不同场景的差分选择示例(新手对照表)

    数据特点 差分选择方案 举例
    无趋势、无季节性(平稳) 不用差分(d=0,D=0) 随机波动的每日股价(短期)
    只有趋势(无季节性) 1 阶差分(d=1) 每月零花钱稳步上涨
    只有季节性(无趋势) 1 阶季节性差分(D=1,S=12) 每月电费(夏高冬低,均值不变)
    趋势 + 季节性 1 阶季节性差分 + 1 阶差分 AirPassengers 数据集

    四、总结(核心关键点)

    1. 选差分的核心逻辑:先诊断不平稳的原因(趋势 / 季节性)→ 选对应差分类型→ 从 1 阶开始试→ 平稳就停,最多试 2 阶;
    2. 优先级:1 阶差分 / 1 阶季节性差分优先,组合差分次之,绝对避免 3 阶及以上;
    3. 验证标准:差分后 ADF p<0.05 且 KPSS p>0.05,可视化看数据无明显趋势 / 季节性,且无过度负相关(避免过度差分)。

    你可以把上面的代码逐行运行,对比 “1 阶差分、1 阶季节性差分、组合差分、过度差分” 的结果,就能直观感受到 “合适的差分” 是什么样的 —— 零基础不用死记规则,先动手试,试几次就知道怎么选了。

    除了ARIMA和SARIMA,还有哪些常用的时序预测模型?

    除了 ARIMA 和 SARIMA 这两类经典统计模型,常用的时序预测模型还可以分为 传统统计模型、机器学习模型、深度学习模型 三大类。我会用通俗易懂的比喻 + 适配 Mac 系统的代码 + 清晰的适用场景,帮你逐个理解,零基础也能快速上手。

    核心分类逻辑

    • 小数据 + 明显趋势 / 季节性 → 传统统计模型(简单易解释)
    • 中等数据 + 多特征(比如天气、价格) → 机器学习模型(需要做特征工程)
    • 大数据 + 长序列 + 复杂规律 → 深度学习模型(自动提取特征,预测精度高)

    一、传统统计模型(简单好用,适合小数据)

    这类模型和 ARIMA 一样,基于统计规律,不用复杂的特征工程,新手优先学。

    1. 指数平滑法(代表:Holt-Winters 模型)

    通俗原理

    就像给近期数据 “加更高权重” 的加权平均:昨天的天气比上周的天气对今天的影响更大,越近的数据越重要。

    • 简单指数平滑:适合无趋势、无季节性的数据(比如随机波动的股价);
    • Holt 双指数平滑:适合有趋势、无季节性的数据(比如每月零花钱稳步上涨);
    • Holt-Winters 三指数平滑:适合有趋势 + 有季节性的数据(比如 AirPassengers、太阳黑子),直接对标 SARIMA。
    适用场景

    小样本时序数据(比如几十到几百条)、趋势 + 季节性明显,且需要高解释性的场景(比如商业报表分析)。

    Mac 适配代码(太阳黑子数据集为例)
    # 导入库+Mac中文适配
    import pandas as pd
    import matplotlib.pyplot as plt
    from statsmodels.tsa.holtwinters import ExponentialSmoothing
    import warnings
    warnings.filterwarnings('ignore')
    
    plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']
    plt.rcParams['axes.unicode_minus'] = False
    
    # 加载太阳黑子数据
    from statsmodels.datasets import sunspots
    data = sunspots.load_pandas().data
    data['date'] = pd.to_datetime(data['YEAR'].astype(int).astype(str) + '-01-01')
    data = data.set_index('date')[['SUNACTIVITY']]
    data.columns = ['sunspots']
    series = data['sunspots']
    
    # 拆分训练集和测试集(8:2)
    train_size = int(len(series) * 0.8)
    train, test = series[:train_size], series[train_size:]
    
    # 训练Holt-Winters模型
    # trend='add':加法趋势;seasonal='add':加法季节性;seasonal_periods=11(太阳黑子11年周期)
    model = ExponentialSmoothing(
        train,
        trend='add',
        seasonal='add',
        seasonal_periods=11  # 关键:设置季节周期
    )
    result = model.fit()
    
    # 预测测试集
    pred = result.predict(start=len(train), end=len(train)+len(test)-1)
    
    # 可视化结果
    plt.figure(figsize=(12, 6))
    plt.plot(train, label='训练集', color='darkblue')
    plt.plot(test, label='测试集(实际值)', color='green')
    plt.plot(pred, label='预测值', color='red', linestyle='--')
    plt.title('Holt-Winters 太阳黑子数量预测')
    plt.xlabel('时间')
    plt.ylabel('太阳黑子数量')
    plt.legend()
    plt.grid(alpha=0.3)
    plt.show()
    
    # 优缺点总结
    print("✅ 优点:简单易解释,小数据效果好,无需复杂参数调优")
    print("❌ 缺点:不适合复杂长序列,多变量数据效果差")

    2. VAR 模型(向量自回归模型)

    通俗原理

    ARIMA 是单变量模型(只预测一列数据),而 VAR 是多变量模型,适合 “多个变量互相影响” 的场景。比如:预测 “销量” 时,同时考虑 “价格”“广告投入”“天气”,VAR 会建模这些变量之间的互相影响关系。

    适用场景

    多变量时序数据(比如同时预测销量、价格、库存),变量之间存在明显的相互关联。

    Mac 适配代码(模拟多变量数据)
    import numpy as np
    from statsmodels.tsa.vector_ar.var_model import VAR
    
    # 模拟多变量时序数据:3个变量(比如销量、价格、广告投入)
    np.random.seed(42)
    n = 100
    t = np.arange(n)
    # 变量1:销量(有趋势)
    sales = 100 + 2*t + np.random.normal(0, 5, n)
    # 变量2:价格(和销量负相关)
    price = 50 - 0.5*t + np.random.normal(0, 3, n)
    # 变量3:广告投入(和销量正相关)
    ad = 10 + 0.8*t + np.random.normal(0, 2, n)
    
    # 构建DataFrame
    df = pd.DataFrame({
        'sales': sales,
        'price': price,
        'ad': ad
    }, index=pd.date_range('2020-01-01', periods=n, freq='M'))
    
    # 拆分训练集和测试集
    train_size = int(n * 0.8)
    train_df, test_df = df[:train_size], df[train_size:]
    
    # 训练VAR模型
    model = VAR(train_df)
    # 选择最优滞后阶数(基于AIC准则)
    best_order = model.select_order(maxlags=10)
    print(f"最优滞后阶数:{best_order.aic}")
    var_model = model.fit(best_order.aic)
    
    # 预测测试集
    pred_df = var_model.forecast(train_df.values, steps=len(test_df))
    pred_df = pd.DataFrame(pred_df, columns=df.columns, index=test_df.index)
    
    # 可视化销量预测结果
    plt.figure(figsize=(10, 4))
    plt.plot(df['sales'], label='历史销量', color='blue')
    plt.plot(pred_df['sales'], label='预测销量', color='red', linestyle='--')
    plt.title('VAR模型 销量预测(多变量)')
    plt.legend()
    plt.grid(alpha=0.3)
    plt.show()
    
    # 优缺点总结
    print("✅ 优点:支持多变量预测,能捕捉变量间的关联")
    print("❌ 缺点:变量太多时效果差,需要平稳数据")

    二、机器学习模型(中等数据,需要特征工程)

    这类模型把时序预测问题转化为回归问题,通过构造 “时序特征” 来训练模型,适合有一定数据量且能提取特征的场景。

    代表模型:XGBoost/LightGBM/Random Forest

    通俗原理

    把 “过去的时序数据” 转化为模型能理解的特征,比如:

    • 滞后特征:用前 1 期、前 2 期、前 7 期的数据作为特征;
    • 滚动统计特征:过去 7 天的均值、最大值、标准差;
    • 时间特征:月份、季度、是否节假日。然后用树模型(XGBoost 等)训练,预测未来值。
    适用场景

    中等数据量(几百到几万条)、多特征融合(比如时序数据 + 外部数据)、需要高精度且能接受一定解释性损失的场景。

    Mac 适配代码(XGBoost 时序预测)
    import xgboost as xgb
    from sklearn.metrics import mean_squared_error
    from sklearn.model_selection import train_test_split
    
    # 加载太阳黑子数据
    series = data['sunspots']
    
    # 核心步骤:构造时序特征
    def create_time_features(series, lags=[1, 2, 3, 6, 12]):
        df = series.to_frame(name='target')
        # 1. 滞后特征:前1/2/3/6/12期的数据
        for lag in lags:
            df[f'lag_{lag}'] = df['target'].shift(lag)
        # 2. 滚动统计特征:过去3期的均值和标准差
        df['roll_mean_3'] = df['target'].rolling(window=3).mean().shift(1)
        df['roll_std_3'] = df['target'].rolling(window=3).std().shift(1)
        # 3. 时间特征:年份、月份
        df['year'] = df.index.year
        df['month'] = df.index.month
        # 去掉空值
        df = df.dropna()
        return df
    
    # 构造特征
    df_features = create_time_features(series)
    X = df_features.drop('target', axis=1)  # 特征
    y = df_features['target']  # 目标变量
    
    # 拆分训练集和测试集
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=False)  # 时序数据不能shuffle!
    
    # 训练XGBoost模型
    model = xgb.XGBRegressor(
        n_estimators=100,
        learning_rate=0.1,
        random_state=42
    )
    model.fit(X_train, y_train)
    
    # 预测
    y_pred = model.predict(X_test)
    
    # 可视化结果
    plt.figure(figsize=(12, 6))
    plt.plot(y_train.index, y_train, label='训练集', color='blue')
    plt.plot(y_test.index, y_test, label='测试集(实际值)', color='green')
    plt.plot(y_test.index, y_pred, label='预测值', color='red', linestyle='--')
    plt.title('XGBoost 太阳黑子数量预测')
    plt.legend()
    plt.grid(alpha=0.3)
    plt.show()
    
    # 评估模型
    rmse = np.sqrt(mean_squared_error(y_test, y_pred))
    print(f"测试集RMSE:{rmse:.2f}")
    
    # 优缺点总结
    print("✅ 优点:支持多特征,精度高,对非线性规律捕捉好")
    print("❌ 缺点:需要手动构造特征,对长序列预测效果差")

    关键注意事项:时序数据拆分时绝对不能 shuffle(打乱顺序),否则会泄露未来信息!

    三、深度学习模型(大数据长序列,自动提取特征)

    这类模型基于神经网络,能自动学习时序数据的深层规律,不用手动构造复杂特征,适合大数据、长序列的复杂场景。

    1. LSTM/GRU(长短期记忆网络 / 门控循环单元)

    通俗原理

    传统的循环神经网络(RNN)容易 “忘记” 长期信息(比如预测第 100 期数据时,记不住第 1 期的信息),而 LSTM 通过 “门机制”(输入门、遗忘门、输出门),可以选择性地记住长期信息、忘记无用信息,特别适合长序列预测。GRU 是 LSTM 的简化版,参数更少,训练更快。

    适用场景

    大数据量(几万到几百万条)、长序列(比如预测未来 30 天的气温)、复杂非线性规律。

    Mac 适配代码(PyTorch 实现 LSTM 预测)
    import torch
    import torch.nn as nn
    import torch.optim as optim
    from torch.utils.data import DataLoader, TensorDataset
    
    # Mac 下自动选择设备:有GPU用GPU,没有用CPU
    device = torch.device('mps' if torch.backends.mps.is_available() else 'cpu')
    print(f"使用设备:{device}")
    
    # 数据预处理:标准化+构造序列
    series = data['sunspots'].values
    # 标准化(深度学习必须做,否则模型不收敛)
    from sklearn.preprocessing import MinMaxScaler
    scaler = MinMaxScaler(feature_range=(0, 1))
    series_scaled = scaler.fit_transform(series.reshape(-1, 1)).flatten()
    
    # 构造序列数据:用前seq_len个数据预测下1个数据
    seq_len = 12  # 用前12期预测第13期
    X, y = [], []
    for i in range(len(series_scaled) - seq_len):
        X.append(series_scaled[i:i+seq_len])
        y.append(series_scaled[i+seq_len])
    X = np.array(X).reshape(-1, seq_len, 1)  # 形状:(样本数, 序列长度, 特征数)
    y = np.array(y).reshape(-1, 1)
    
    # 转为Tensor并拆分训练集/测试集
    X_tensor = torch.tensor(X, dtype=torch.float32).to(device)
    y_tensor = torch.tensor(y, dtype=torch.float32).to(device)
    train_size = int(0.8 * len(X_tensor))
    X_train, X_test = X_tensor[:train_size], X_tensor[train_size:]
    y_train, y_test = y_tensor[:train_size], y_tensor[train_size:]
    
    # 构建LSTM模型
    class LSTMModel(nn.Module):
        def __init__(self, input_size=1, hidden_size=64, output_size=1):
            super().__init__()
            self.lstm = nn.LSTM(input_size, hidden_size, batch_first=True)
            self.fc = nn.Linear(hidden_size, output_size)
        
        def forward(self, x):
            lstm_out, _ = self.lstm(x)
            out = self.fc(lstm_out[:, -1, :])  # 取最后一个时间步的输出
            return out
    
    model = LSTMModel().to(device)
    criterion = nn.MSELoss()  # 损失函数:均方误差
    optimizer = optim.Adam(model.parameters(), lr=0.001)  # 优化器
    
    # 训练模型
    batch_size = 32
    train_dataset = TensorDataset(X_train, y_train)
    train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=False)
    
    epochs = 50
    for epoch in range(epochs):
        model.train()
        total_loss = 0
        for batch_X, batch_y in train_loader:
            optimizer.zero_grad()
            pred = model(batch_X)
            loss = criterion(pred, batch_y)
            loss.backward()
            optimizer.step()
            total_loss += loss.item()
        if (epoch+1) % 10 == 0:
            print(f"Epoch [{epoch+1}/{epochs}], Loss: {total_loss/len(train_loader):.4f}")
    
    # 预测
    model.eval()
    with torch.no_grad():
        y_pred_tensor = model(X_test)
    # 反标准化,还原真实值
    y_pred = scaler.inverse_transform(y_pred_tensor.cpu().numpy())
    y_test_true = scaler.inverse_transform(y_test.cpu().numpy())
    
    # 可视化结果
    plt.figure(figsize=(12, 6))
    plt.plot(range(len(y_train)), scaler.inverse_transform(y_train.cpu().numpy()), label='训练集', color='blue')
    plt.plot(range(len(y_train), len(y_train)+len(y_test)), y_test_true, label='测试集(实际值)', color='green')
    plt.plot(range(len(y_train), len(y_train)+len(y_test)), y_pred, label='预测值', color='red', linestyle='--')
    plt.title('LSTM 太阳黑子数量预测')
    plt.legend()
    plt.grid(alpha=0.3)
    plt.show()
    
    # 优缺点总结
    print("✅ 优点:自动提取特征,长序列预测效果好,适合复杂规律")
    print("❌ 缺点:需要大数据量,训练慢,解释性差")

    2. 其他深度学习模型

    • TCN(时间卷积网络):用卷积代替循环,训练速度比 LSTM 快,适合长序列;
    • Transformer(比如 Temporal Fusion Transformer, TFT):基于注意力机制,能捕捉长距离依赖,是目前时序预测的 SOTA 模型之一;
    • Prophet:Facebook 开源的模型,结合了统计和机器学习,无需专业知识,调参简单。

    四、常用模型选择对照表(新手直接套用)

    数据特点 推荐模型 核心优势
    小数据 + 趋势 / 季节性 Holt-Winters > ARIMA 简单易解释,无需复杂调参
    多变量 + 变量互相关联 VAR 模型 捕捉变量间的关联关系
    中等数据 + 多特征融合 XGBoost/LightGBM 精度高,支持非线性规律
    大数据 + 长序列 + 复杂规律 LSTM/Transformer 自动提取特征,长序列效果好
    业务导向 + 快速上手 Prophet(Facebook 开源) 调参简单,适合业务人员

    五、Mac 系统运行注意事项

    1. 深度学习设备:Mac 搭载 Apple Silicon 芯片(M1/M2/M3)的可以用 mps 加速训练,比 CPU 快很多;
    2. 库安装:用 pip3 install xgboost lightgbm torch statsmodels scikit-learn 安装所有依赖;
    3. 内存问题:大数据训练时,减小 batch_size 避免内存溢出。

    这些模型你可以根据自己的数据情况选择,新手建议先从 Holt-Winters 和 XGBoost 入手,容易出效果,再逐步学习深度学习模型。


    作业:对太阳黑子数量数据集用 arima 完成流程

    使用 Mac OS 系统,想要基于今天学的时序建模全流程,对太阳黑子数量数据集完成 ARIMA 模型的实战项目,我会为你提供适配 Mac 系统的详细步骤 + 可直接运行的代码,每一步都标注 Mac 系统的注意事项,确保你能顺利跑通。

    一、先明确太阳黑子数据集特点(提前了解)

    太阳黑子数据集记录了每月太阳黑子的数量,核心特征:

    • 单变量时序数据,时间跨度长(1749-2018 年);
    • 周期性波动(约 11 年一个周期),无明显长期趋势;
    • 数据整体不平稳,需要差分处理。

    二、Mac OS 环境准备(第一步必做)

    Mac 系统默认带 Python,但需要安装必备库,打开「终端」执行以下命令(复制粘贴即可):

    # 安装核心库(pandas/numpy/matplotlib/statsmodels/sklearn)

    pip3 install pandas numpy matplotlib statsmodels scikit-learn

    # 注意:Mac下Python3的包管理用pip3(避免和Python2混淆),如果提示权限问题,加 --user # pip3 install --user pandas numpy matplotlib statsmodels scikit-learn

    三、ARIMA 建模全流程(分 8 步,代码 + 解释)

    步骤 1:导入库 + 适配 Mac 中文显示

    Mac 系统的中文字体和 Windows 不同,这里专门适配,避免画图乱码:

    # 导入核心库
    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    from statsmodels.tsa.stattools import adfuller, kpss
    from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
    from statsmodels.tsa.arima.model import ARIMA
    from sklearn.metrics import mean_squared_error, mean_absolute_error
    import warnings
    warnings.filterwarnings('ignore')  # 屏蔽无关警告(新手更清爽)
    
    # ===== Mac系统专属:适配中文显示 =====
    plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']  # Mac自带的中文字体
    plt.rcParams['axes.unicode_minus'] = False  # 正常显示负号
    步骤 2:数据获取(太阳黑子数据集在线获取,无需本地文件)
    # 加载太阳黑子数据集(经典数据集,在线获取)
    # 数据来源:statsmodels内置,或在线CSV(两种方式都给,选一种即可)
    
    # 方式1:statsmodels内置(推荐,无需联网也能加载)
    from statsmodels.datasets import sunspots
    data = sunspots.load_pandas().data  # 加载数据
    # 处理时间索引:Year是年份,补充月份为1月,转为时间格式
    data['date'] = pd.to_datetime(data['YEAR'].astype(int).astype(str) + '-01-01')
    data = data.set_index('date')  # 设为时间索引
    data = data[['SUNACTIVITY']]  # 只保留太阳黑子数量列
    data.columns = ['sunspots']  # 列名简化
    
    # 方式2:在线CSV(备用,如果方式1报错)
    # data = pd.read_csv(
    #     'https://raw.githubusercontent.com/jbrownlee/Datasets/master/monthly-sunspots.csv',
    #     parse_dates=['Month'],
    #     index_col='Month'
    # )
    # data.columns = ['sunspots']  # 列名简化
    
    # 数据预览
    print("===== 数据前5行 =====")
    print(data.head())
    print("\n===== 数据基础信息 =====")
    print(f"数据时间范围:{data.index.min()} 到 {data.index.max()}")
    print(f"数据维度:{data.shape}")  # (309, 1) 或 (2820,1),取决于数据源,都不影响流程
    print(f"缺失值数量:{data.isnull().sum()[0]}")  # 无缺失值
    步骤 3:数据探索(EDA,找规律 + 检验平稳性)
    # 3.1 可视化整体趋势(看周期性)
    plt.figure(figsize=(12, 8))
    
    # 子图1:整体趋势
    plt.subplot(2, 2, 1)
    plt.plot(data['sunspots'], color='darkblue')
    plt.title('太阳黑子数量整体趋势(1749-2018)')
    plt.xlabel('时间')
    plt.ylabel('太阳黑子数量')
    plt.grid(alpha=0.3)
    
    # 子图2:滚动均值+标准差(判断平稳性)
    plt.subplot(2, 2, 2)
    roll_mean = data['sunspots'].rolling(window=12).mean()  # 12个月滚动均值
    roll_std = data['sunspots'].rolling(window=12).std()    # 12个月滚动标准差
    plt.plot(data['sunspots'], label='原始数据', color='darkblue')
    plt.plot(roll_mean, label='12月滚动均值', color='red')
    plt.plot(roll_std, label='12月滚动标准差', color='green')
    plt.title('滚动均值+标准差')
    plt.legend()
    plt.grid(alpha=0.3)
    
    # 子图3:ACF图(自相关,看周期性)
    plt.subplot(2, 2, 3)
    plot_acf(data['sunspots'], lags=60, ax=plt.gca())  # lags=60,看5年的自相关
    plt.title('ACF图(原始数据)')
    plt.grid(alpha=0.3)
    
    # 子图4:PACF图(偏自相关)
    plt.subplot(2, 2, 4)
    plot_pacf(data['sunspots'], lags=60, ax=plt.gca(), method='ywm')
    plt.title('PACF图(原始数据)')
    plt.grid(alpha=0.3)
    
    plt.tight_layout()
    plt.show()
    
    # 3.2 平稳性检验(ADF+KPSS)
    def check_stationary(series):
        """Mac系统下的平稳性检验函数,输出清晰结果"""
        # ADF检验
        adf_result = adfuller(series)
        adf_p = adf_result[1]
        # KPSS检验
        kpss_result = kpss(series, regression='c')
        kpss_p = kpss_result[1]
        
        print("===== 平稳性检验结果 =====")
        print(f"ADF p值:{adf_p:.4f}(<0.05 则平稳)")
        print(f"KPSS p值:{kpss_p:.4f}(>0.05 则平稳)")
        
        if adf_p < 0.05 and kpss_p > 0.05:
            print("→ 数据平稳 ✅")
            return True
        else:
            print("→ 数据不平稳 ❌")
            return False
    
    # 检验原始数据
    print("\n【原始数据平稳性】")
    is_stationary = check_stationary(data['sunspots'])
    步骤 4:数据预处理(选择合适的差分)

    太阳黑子数据无明显趋势,但有周期性,先试 1 阶差分:

    # 4.1 1阶差分(处理不平稳)
    data_diff1 = data['sunspots'].diff().dropna()
    
    # 4.2 检验差分后平稳性
    print("\n【1阶差分后平稳性】")
    is_stationary_diff1 = check_stationary(data_diff1)
    
    # 4.3 可视化差分后数据
    plt.figure(figsize=(10, 4))
    plt.plot(data_diff1, color='darkgreen')
    plt.title('太阳黑子数据1阶差分后')
    plt.xlabel('时间')
    plt.ylabel('差分后数量')
    plt.grid(alpha=0.3)
    plt.show()
    
    # 4.4 差分后ACF/PACF(确定p,q)
    plt.figure(figsize=(10, 4))
    plt.subplot(1,2,1)
    plot_acf(data_diff1, lags=30, ax=plt.gca())
    plt.title('1阶差分后ACF图')
    plt.subplot(1,2,2)
    plot_pacf(data_diff1, lags=30, ax=plt.gca(), method='ywm')
    plt.title('1阶差分后PACF图')
    plt.tight_layout()
    plt.show()
    
    # 差分后分析结论:
    # 1阶差分后ADF p<0.05,KPSS p>0.05 → 平稳;
    # ACF在lag=10左右衰减,PACF在lag=2左右截断 → 初步选p=2, d=1, q=10(或简化为p=2, d=1, q=2)
    步骤 5:拆分训练集 + 测试集(Mac 系统下无路径问题)
    # 按8:2拆分(前80%训练,后20%测试)
    train_size = int(len(data) * 0.8)
    train = data['sunspots'][:train_size]
    test = data['sunspots'][train_size:]
    
    print(f"\n===== 数据集拆分 =====")
    print(f"训练集长度:{len(train)}")
    print(f"测试集长度:{len(test)}")
    print(f"测试集时间范围:{test.index.min()} 到 {test.index.max()}")
    步骤 6:训练 ARIMA 模型(Mac 系统兼容)
    # 6.1 定义并训练ARIMA模型(p=2, d=1, q=2,新手友好版)
    # 注:如果想更精准,可试p=2, d=1, q=10,Mac系统计算稍慢但能运行
    model = ARIMA(train, order=(2, 1, 2))  # (p,d,q)=(2,1,2)
    result = model.fit()
    
    # 6.2 输出模型摘要(Mac下正常显示)
    print("\n===== ARIMA模型摘要 =====")
    print(result.summary())
    步骤 7:模型评估(Mac 下计算评估指标)
    # 7.1 预测测试集
    pred = result.predict(start=len(train), end=len(train)+len(test)-1, typ='levels')
    # typ='levels':预测原始尺度,而非差分尺度
    
    # 7.2 可视化预测结果
    plt.figure(figsize=(12, 6))
    plt.plot(train, label='训练集', color='darkblue')
    plt.plot(test, label='测试集(实际值)', color='green')
    plt.plot(pred, label='预测值', color='red', linestyle='--')
    plt.title('ARIMA(2,1,2) 太阳黑子数量预测结果')
    plt.xlabel('时间')
    plt.ylabel('太阳黑子数量')
    plt.legend()
    plt.grid(alpha=0.3)
    plt.show()
    
    # 7.3 计算评估指标(MSE, MAE, RMSE)
    mse = mean_squared_error(test, pred)
    mae = mean_absolute_error(test, pred)
    rmse = np.sqrt(mse)
    
    print("\n===== 模型评估指标 =====")
    print(f"均方误差(MSE):{mse:.2f}(越小越好)")
    print(f"平均绝对误差(MAE):{mae:.2f}(越小越好)")
    print(f"均方根误差(RMSE):{rmse:.2f}(越小越好)")
    步骤 8:未来预测(Mac 下无兼容问题)
    # 8.1 预测未来10年(120个月)的太阳黑子数量
    forecast_steps = 120  # 预测未来120个月(10年)
    forecast = result.get_forecast(steps=forecast_steps)
    forecast_mean = forecast.predicted_mean  # 预测均值
    forecast_ci = forecast.conf_int()  # 95%置信区间
    
    # 8.2 可视化未来预测
    plt.figure(figsize=(12, 6))
    # 历史数据
    plt.plot(data['sunspots'], label='历史数据', color='darkblue')
    # 预测值
    plt.plot(forecast_mean.index, forecast_mean, label='未来预测值', color='red')
    # 置信区间
    plt.fill_between(forecast_ci.index, 
                     forecast_ci.iloc[:, 0], 
                     forecast_ci.iloc[:, 1], 
                     color='pink', alpha=0.3, label='95%置信区间')
    plt.title('太阳黑子数量历史数据+未来10年预测')
    plt.xlabel('时间')
    plt.ylabel('太阳黑子数量')
    plt.legend()
    plt.grid(alpha=0.3)
    plt.show()
    
    # 8.3 输出未来预测的前10个值
    print("\n===== 未来10个月太阳黑子数量预测 =====")
    print(forecast_mean.head(10))

    四、Mac 系统运行注意事项(避坑指南)

    1. 字体问题:如果中文还是乱码,把Arial Unicode MS换成Heiti TC(Mac 繁体黑体)或PingFang SC(苹方);
    2. 库安装失败:如果statsmodels安装报错,先安装依赖:
    3. Python 版本:Mac 下如果输入python3才是 Python3,python是 Python2,运行代码时用:
    4. 内存问题:太阳黑子数据集不大,Mac 低配也能运行,无需担心卡顿。

    五、总结(核心关键点)

    1. 流程完整性:从数据获取→探索→预处理→建模→评估→预测,覆盖了时序建模全流程;
    2. Mac 适配:专门调整了中文字体、库安装命令、Python 版本适配,确保你能直接运行;
    3. 参数选择:太阳黑子数据 1 阶差分即可平稳,ARIMA 参数选 (2,1,2)(新手友好),也可尝试 (2,1,10) 提升精度;
    4. 核心指标:通过 MSE/MAE/RMSE 评估模型,置信区间能反映预测的不确定性。

    你可以把上述代码复制到 Mac 的文本编辑器(比如 VS Code、PyCharm,或自带的 TextEdit),保存为sunspots_arima.py,然后在终端执行python3 sunspots_arima.py即可运行 —— 每一步都有输出,你可以对照输出理解模型的效果,新手建议逐段运行,看每一步的结果。

    浙大疏锦行

    Logo

    有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

    更多推荐