时间序列是一组按时间顺序排列的数据点,相邻数据点之间存在相关性。本章介绍了时间序列分析的核心概念,包括平稳性、自相关、ACF/PACF和季节性。读者将学习如何描述时间序列的趋势和季节性,预测未来值,检测异常,并判断过程是否受控。平稳性是时间序列分析中的关键概念,指统计性质不随时间变化。大多数时间序列模型要求数据平稳,可通过眼测、ADF检验或KPSS检验判断。自相关(ACF)和偏自相关(PACF)用于分析数据的相关性结构,指导模型选型。分解方法将时间序列拆分为趋势、季节性和残差三部分,有助于理解和建模。评估模型时,应使用连续时间段进行训练和测试,并采用MAE、RMSE或MAPE等指标衡量误差。学完本章后,读者能够对时间序列数据进行基本分析和建模,并应用相关方法解决实际问题。
时间序列基础
时间序列 (time series) 是一组按时间顺序排列的数据点,比如每日股票价格、每月销售额、每小时服务器请求数。它和"普通"表格数据的关键区别:相邻点之间不独立,今天的值往往和昨天相关。
这一章我们建立时序分析的核心词汇:平稳性、自相关、ACF/PACF、季节性。
什么是时间序列
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 模拟一个销售额时序: 趋势 + 季节性 + 噪声
np.random.seed(42)
dates = pd.date_range("2022-01-01", periods=365*2, freq="D")
trend = np.linspace(100, 200, len(dates)) # 上升趋势
seasonal = 20 * np.sin(np.arange(len(dates)) * 2 * np.pi / 365) # 年周期
noise = np.random.normal(0, 5, len(dates))
sales = trend + seasonal + noise
df = pd.DataFrame({"date": dates, "sales": sales})
df.set_index("date", inplace=True)
df["sales"].plot(figsize=(12, 4), title="某商品 2 年每日销量")
plt.show()
你会看到:
- 整体上升 (趋势)
- 周期波动 (季节性, 365 天周期)
- 小幅抖动 (噪声)
4 大分析目标
- 描述 (Description): 画图、看统计量、找趋势和季节性
- 预测 (Forecasting): 未来 7 天 / 30 天的销量
- 异常检测 (Anomaly Detection): 找出"双11"那种反常高峰
- 控制 (Control): 判断过程是否"在控"
平稳性 (Stationarity):最重要的概念
平稳序列的统计性质 (均值、方差、自相关) 不随时间变化。
直观理解:平稳序列没有明显趋势,没有周期,均值和方差稳定。
# 上面的销量数据: 非平稳 (有趋势 + 季节性)
# 对数 + 差分后: 接近平稳
df["log_sales"] = np.log(df["sales"])
df["diff"] = df["log_sales"].diff() # 一阶差分
df["diff"].plot(figsize=(12, 4), title="对数差分后")
plt.show()
为什么要平稳?大多数时序模型 (ARIMA 等) 要求数据平稳,否则预测会"漂"。
from statsmodels.tsa.stattools import adfuller, kpss
# ADF 检验
result = adfuller(df["sales"].dropna())
print(f"ADF p-value: {result[1]:.4f}")
# 0.5+ → 非平稳
# 对数 + 一阶差分后
result = adfuller(df["diff"].dropna())
print(f"差分后 ADF p-value: {result[1]:.4f}")
# 0.001 → 平稳
自相关 (ACF) 和偏自相关 (PACF)
自相关衡量"相隔 k 期的两个点有多相关"。
- ACF: 包含所有中间影响的相关系数
- PACF: 排除中间影响,只看"相隔 k 期的纯相关"
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 4))
plot_acf(df["diff"].dropna(), lags=30, ax=ax1)
plot_pacf(df["diff"].dropna(), lags=30, ax=ax2)
plt.show()
看图判断:
- ACF 在 lag=7, 14, 21... 处明显 → 周季节性
- ACF 缓慢下降 → 序列有趋势
- PACF 在 lag=1 后截尾 → 适合 AR(1) 模型
- ACF 在 lag=1 后截尾 → 适合 MA(1) 模型
分解:Trend + Seasonal + Residual
把时间序列拆成 3 部分有助于理解和建模:
from statsmodels.tsa.seasonal import seasonal_decompose
decomp = seasonal_decompose(df["sales"], model="additive", period=365)
decomp.plot()
plt.show()
- Trend: 长期趋势
- Seasonal: 季节性波动
- Residual: 剩余噪声 (应该接近白噪声)
模型: y(t) = Trend(t) + Seasonal(t) + Residual(t) (加法)
或: y(t) = Trend(t) × Seasonal(t) × Residual(t) (乘法,适合波动幅度随趋势增大的)
评估:训练/测试切分
时序数据不能随机打乱! 必须是连续的时间段:
train = df["sales"][:"2023-06-30"]
test = df["sales"]["2023-07-01":]
# 或者按比例
train_size = int(len(df) * 0.8)
train = df["sales"][:train_size]
test = df["sales"][train_size:]
评估指标:
- MAE (平均绝对误差): 直观, 同量纲
- RMSE (均方根误差): 惩罚大误差
- MAPE (平均绝对百分比误差): 相对误差,跨量纲可比
from sklearn.metrics import mean_absolute_error, mean_squared_error
import numpy as np
def eval_metrics(y_true, y_pred):
mae = mean_absolute_error(y_true, y_pred)
rmse = np.sqrt(mean_squared_error(y_true, y_pred))
mape = np.mean(np.abs((y_true - y_pred) / y_true)) * 100
return {"MAE": mae, "RMSE": rmse, "MAPE%": mape}
小结
- 时间序列: 按时间排列的数据, 关键特征是相邻点相关
- 4 大任务: 描述、预测、异常检测、控制
- 平稳性是核心: 大多数模型要求数据平稳
- ADF / KPSS 检验平稳, 差分让非平稳变平稳
- ACF / PACF 看相关性结构, 指导模型选型
- 时序切分: 训练在前, 测试在后, 不能随机打乱
练习思考
- 找一组真实数据 (气温、股票、销售), 画图看是否符合"平稳"?
- 对非平稳序列做一阶差分后, ADF 检验的 p 值变化多少?
- 同样的序列, 用
period=7(周) 和period=30(月) 做分解, 季节性部分有什么不同?
章末小测验
检验你对《时间序列基础》的掌握程度。
判断时间序列是否平稳的常用检验是?
时间序列 4 大分析目标是?