模块三:时间序列预测 (Time Series Forecasting)。
在美赛C题中,预测题通常占了一半的江山(例如:预测未来的能源消耗、预测药物的传播趋势)。
核心逻辑 :时间序列不仅仅是 x x x 和 y y y 的关系,而是 y t y_t yt 与 过去的时间 y t − 1 , y t − 2 . . . y_{t-1}, y_{t-2}... yt−1,yt−2... 的关系。
我将根据数据量的多少 和数据的特征 ,把你手中的武器分为三档。请务必记住每种武器的适用条件 和判别方法。
🔍 判别:我该用哪个模型?(决策流程图)
拿到带时间标签的数据(如:1990-2020年的数据),按以下逻辑判断:
- 看数据量 :
- 极少 (< 15个点) :比如只给了过去10年的数据。
- 👉 灰色预测 GM(1,1) (唯一解,强行用机器学习会过拟合)。
- 中等 (> 20个点) 且有明显周期/节假日 :
- 👉 Prophet (Facebook开源库,处理周期性极强)。
- 中等 (> 30个点) 且追求统计学严谨性 :
- 👉 ARIMA (经典,论文里公式好看)。
- 海量 (> 1000个点) 且非线性强 :
- 👉 LSTM / RNN (深度学习,慎用,除非你显卡好且时间多)。
- 极少 (< 15个点) :比如只给了过去10年的数据。
🛠️ 武器一:灰色预测 GM(1,1) (Grey Prediction) ------ 贫信息救星
这是美赛和国内赛最喜欢的模型,专门解决"数据少、信息不全"的问题。
- 适用情况 :
- 数据样本极少(4个以上即可)。
- 数据呈现指数增长 或指数衰减的趋势(如果是波动的,效果不好)。
- 判别方法(级比检验) :
- 计算相邻两个数据的比值 λ k = x ( k − 1 ) / x ( k ) \lambda_k = x_{(k-1)}/x_{(k)} λk=x(k−1)/x(k)。
- 如果所有比值都落在 ( e − 2 / ( n + 1 ) , e 2 / ( n + 1 ) ) (e^{-2/(n+1)}, e^{2/(n+1)}) (e−2/(n+1),e2/(n+1)) 区间内,说明可以建模。
- 注意事项 :
- 只能预测短期(往后推2-3年),推太远误差会指数级爆炸。
- 如果数据是波动的,不要用 GM(1,1),或者先做平滑处理。
💻 Python 代码模板 (手写实现,sklearn没有):
python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
def gm11(x, n_pred=5):
"""
x: 输入的原始数据序列 (1D array or list)
n_pred: 往后预测多少期
"""
x0 = np.array(x)
n = len(x0)
# 1. 级比检验 (可选,论文里要写这一步通过了)
# lambda_k = x0[:-1] / x0[1:]
# range_min, range_max = np.exp(-2/(n+1)), np.exp(2/(n+1))
# 2. 累加生成 (AGO)
x1 = np.cumsum(x0)
# 3. 构造矩阵 B 和 Y
z1 = (x1[:-1] + x1[1:]) / 2.0
B = np.vstack([-z1, np.ones(n-1)]).T
Y = x0[1:].reshape(-1, 1)
# 4. 求解参数 a, u
u_a = np.linalg.inv(B.T @ B) @ B.T @ Y
a, u = u_a[0][0], u_a[1][0]
# 5. 预测模型
def predict_func(k):
return (x0[0] - u/a) * np.exp(-a * k) + u/a
# 还原 (累减)
pred_x1 = np.array([predict_func(k) for k in range(n + n_pred)])
pred_x0 = np.concatenate(([x0[0]], np.diff(pred_x1)))
return pred_x0
# 使用示例
# history_data = [100, 110, 125, 140, 165]
# result = gm11(history_data, n_pred=3)
🛠️ 武器二:Prophet (先知) ------ 周期性数据神器
由 Facebook 开发,专门针对商业和具有周期性(Weekly, Yearly)的数据。
- 适用情况 :
- 数据有明显的季节性 (夏天高冬天低)或节假日效应。
- 数据中有缺失值 或异常值(Prophet 鲁棒性很强)。
- 注意事项 :
- 需要安装:
pip install prophet(安装可能有点麻烦,建议提前装好)。 - 输入数据的列名必须严格是
ds(时间) 和y(数值)。
- 需要安装:
💻 Python 代码模板:
python
from prophet import Prophet
import pandas as pd
# 1. 准备数据 (必须是 ds 和 y 两列)
df_prophet = pd.DataFrame({
'ds': pd.to_datetime(['2023-01-01', '2023-01-02', '...']),
'y': [100, 112, ...]
})
# 2. 建模
model = Prophet(yearly_seasonality=True, weekly_seasonality=True)
model.fit(df_prophet)
# 3. 预测未来
future = model.make_future_dataframe(periods=30) # 往后推30天/年
forecast = model.predict(future)
# 4. 可视化 (Prophet自带画图,非常好用)
fig1 = model.plot(forecast) # 画预测图 (带置信区间阴影)
fig2 = model.plot_components(forecast) # 画趋势、周效应、年效应分解图 (论文素材!)
🛠️ 武器三:ARIMA 模型 ------ 统计学正统
Autoregressive Integrated Moving Average。最经典的统计模型。
- 适用情况 :
- 数据相对平稳(没有剧烈的趋势变化,或者差分后平稳)。
- 适合做纯粹的数值推演,不涉及外部变量(如温度影响销量)。
- 判别方法(平稳性检验 / ADF Test) :
- 如果在 p < 0.05 p < 0.05 p<0.05 (显著性水平),说明数据平稳,可以直接用。
- 如果不平稳,需要进行差分 (Differencing) ,即 y t − y t − 1 y_t - y_{t-1} yt−yt−1,直到平稳为止。
- 注意事项 :
- 参数 ( p , d , q ) (p, d, q) (p,d,q) 很难定。建议使用
pmdarima库的auto_arima自动寻找最优参数,别自己在比赛中手调 ACF/PACF 图,太慢了。
- 参数 ( p , d , q ) (p, d, q) (p,d,q) 很难定。建议使用
💻 Python 代码模板 (使用 auto_arima):
python
import pmdarima as pm
from pmdarima.arima import auto_arima
# pip install pmdarima
# 1. 自动寻找最优参数 (Auto-ARIMA)
model = auto_arima(train_data,
start_p=1, start_q=1, max_p=3, max_q=3, d=None, # d=None表示自动检测差分阶数
seasonal=False, # 如果有季节性,设为True
trace=True, # 打印过程
error_action='ignore',
suppress_warnings=True)
print(model.summary()) # 打印统计表 (直接截图放论文附录)
# 2. 预测
preds, conf_int = model.predict(n_periods=10, return_conf_int=True)
# 3. 画图 (带置信区间)
# conf_int 包含了预测值的上下界,画出来就是那个漂亮的阴影带
⚠️ 模块四总结:避坑指南
在做时间序列题目时,评委最讨厌看到以下几种错误,请务必避免:
-
拿着非平稳数据直接跑模型:
- 比如数据明明是一直上涨的(非平稳),你却没做差分,直接丢进 ARIMA。
- 解决 :论文里一定要提一句 "Stationarity Test (ADF Test)",或者说明用了
auto_arima自动处理了差分。
-
没有置信区间 (Confidence Interval):
- 预测未来是不确定的。如果你只画一条线,说明你不严谨。
- 解决:画图时,一定要用浅色阴影画出 95% 的置信区间(Prophet 和 auto_arima 都会自动给出来)。这会让你的图表瞬间专业化。
-
预测得太远:
- 你有10年的数据,却预测未来50年。
- 解决:通常预测长度不要超过历史数据的 1/3 或 1/2。如果在 GM(1,1) 中预测太远,要在论文中讨论"长期预测误差可能会增大"。
-
模型评价不当:
- 时间序列不能用随机打乱的交叉验证(K-Fold),因为时间是有顺序的!不能用未来的数据训练去预测过去。
- 解决 :使用时间序列交叉验证 (Time Series Split),或者简单点,直接切分最后 10% 做测试集。
🎯 顾问建议
作为编程手,针对时间序列模块,你需要准备好:
- GM(1,1) 的 Python 函数(因为这一块现成的库不好找,必须手存)。
- Prophet 的环境(确保能 import prophet 不报错)。
- ADF 检验的代码(用来证明你检查了数据平稳性)。
python
# 附赠:ADF 检验代码
from statsmodels.tsa.stattools import adfuller
def test_stationarity(timeseries):
dftest = adfuller(timeseries, autolag='AIC')
print('p-value:', dftest[1])
if dftest[1] < 0.05:
print("数据是平稳的 (Reject H0)")
else:
print("数据不平稳 (Fail to reject H0),需要差分")