尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

时间序列与灰色预测:小样本场景下的Python实战与选型指南

时间序列与灰色预测:小样本场景下的Python实战与选型指南 1. 从直觉到算法为什么我们需要时间序列与灰色预测做数据分析或者业务预测的朋友经常会遇到一个头疼的问题手头只有一小撮历史数据比如过去几个月的销量、几周的日活用户数甚至只有寥寥几个数据点但老板或者项目却要求你对未来做出判断。用传统的回归模型吧数据量太少模型根本学不出个所以然强行拟合的结果往往惨不忍睹。这时候时间序列分析和灰色预测模型就成了我们工具箱里两件非常趁手的兵器。时间序列预测说白了就是基于事物过去随时间变化的规律去推测它未来的走势。它的核心假设是“未来是过去的延续”历史数据中蕴含着趋势、周期和季节性。而灰色预测特别是经典的GM(1,1)模型它的思想更“哲学”一些它承认我们面对的系统信息不完全所以叫“灰色”但它认为尽管表象数据可能杂乱无章其背后必然存在某种内在规律。GM(1,1)通过累加生成操作弱化原始数据的随机性挖掘其潜在的指数增长趋势特别擅长处理“小样本、贫信息”的不确定性问题。简单来说当你数据量充足、变化规律相对明显时ARIMA、Prophet这类时间序列模型是主力军。而当你数据稀缺、又必须做出预测时灰色预测往往能给你一个意想不到的、逻辑上说得通的参考结果。接下来我就结合Python把这两块内容的原理、区别和实际代码实现掰开揉碎了讲清楚。2. 时间序列预测的核心分解、理解与建模时间序列分析不是简单地画条趋势线。一个完整的时间序列通常可以分解为几个核心成分理解了它们建模才能有的放矢。2.1 时间序列的“五脏六腑”趋势、季节与残差任何一个时间序列数据记作 $Y_t$我们都可以尝试将其拆解为以下部分趋势 (Trend, T_t)数据在较长时期内呈现的持续向上或向下的基本方向。比如一款成功产品的用户数总体在增长。季节性 (Seasonality, S_t)在固定周期如一年、一月、一周、一天内重复出现的规律性波动。比如冰淇淋销量夏天高冬天低电商网站的流量周末高周中低。周期性 (Cycle, C_t)非固定频率的波动通常与经济周期等长期因素相关波动周期长于一年。在实际分析中有时与趋势合并考虑。残差/不规则波动 (Residual/ Irregular, I_t)剔除趋势、季节和周期后剩下的无法解释的随机噪声。一个好的模型应该能尽量捕捉前三种成分让残差看起来像白噪声随机且无规律。一种经典的分解方法是STL (Seasonal and Trend decomposition using Loess)。它使用局部加权回归 (Loess) 来迭代地提取季节和趋势成分对异常值不敏感且季节成分可以随时间变化非常强大。2.2 经典模型ARIMA如何用数学语言描述时间依赖ARIMA模型是时间序列预测的基石它的全称是自回归积分滑动平均模型。听起来复杂其实理解三个部分就行AR (自回归, p)当前值 $Y_t$ 与它过去 $p$ 个时期的值线性相关。公式为$Y_t c \phi_1 Y_{t-1} \phi_2 Y_{t-2} ... \phi_p Y_{t-p} \epsilon_t$。这好比说“今天的温度很大程度上取决于昨天和前天的温度”。I (差分, d)为了让非平稳序列均值或方差随时间变化变得平稳需要对原始数据进行 $d$ 阶差分。一阶差分就是 $Yt Y_t - Y{t-1}$。这是很多时间序列建模的前提步骤。MA (滑动平均, q)当前值 $Y_t$ 与过去 $q$ 个时期的随机扰动误差项线性相关。公式为$Y_t \mu \epsilon_t \theta_1 \epsilon_{t-1} ... \theta_q \epsilon_{t-q}$。这反映了外部冲击对当前值的持续影响。ARIMA(p,d,q)就是把这三者结合起来。确定p, d, q的过程就是模型定阶通常会借助自相关图(ACF)和偏自相关图(PACF)来辅助判断。2.3 实战用Python进行时间序列分解与ARIMA建模我们用一个模拟的、带有趋势和季节性的销售数据来演示全过程。这里会用到statsmodels和pandas。import numpy as np import pandas as pd import matplotlib.pyplot as plt from statsmodels.tsa.seasonal import STL from statsmodels.tsa.stattools import adfuller from statsmodels.graphics.tsaplots import plot_acf, plot_pacf from statsmodels.tsa.arima.model import ARIMA import warnings warnings.filterwarnings(ignore) # 1. 生成模拟数据趋势 季节性 噪声 np.random.seed(42) time_index pd.date_range(start2020-01-01, periods120, freqM) # 10年月度数据 trend np.linspace(100, 300, 120) # 线性上升趋势 seasonality 50 * np.sin(2 * np.pi * np.arange(120) / 12) # 年度季节性12个月周期 noise np.random.normal(0, 15, 120) # 随机噪声 sales trend seasonality noise ts_data pd.Series(sales, indextime_index) ts_data.name Monthly Sales # 2. STL分解 stl STL(ts_data, period12) # 明确周期为12个月 result stl.fit() fig result.plot() plt.show()这段代码会生成一张图清晰地展示原始序列、趋势项、季节项和残差项。通过观察残差是否随机可以初步判断分解效果。注意STL分解要求指定period周期。对于月度数据年度周期是12对于日数据周周期可能是7。这是第一个容易踩的坑周期设错分解结果会完全失真。# 3. 平稳性检验 (ADF检验) adf_test adfuller(ts_data) print(fADF Statistic: {adf_test[0]:.4f}) print(fp-value: {adf_test[1]:.4f}) if adf_test[1] 0.05: print(序列非平稳需要差分。) else: print(序列平稳。) # 通常p值大于0.05则认为非平稳。对于非平稳序列我们需要差分。 ts_data_diff ts_data.diff().dropna() # 一阶差分 adf_test_diff adfuller(ts_data_diff) print(f差分后序列 p-value: {adf_test_diff[1]:.4f})# 4. 观察ACF和PACF图为ARIMA定阶 fig, axes plt.subplots(1, 2, figsize(12,4)) plot_acf(ts_data_diff, lags40, axaxes[0]) # 观察ACF截尾或拖尾 plot_pacf(ts_data_diff, lags40, axaxes[1]) # 观察PACF截尾或拖尾 plt.show()ACF图如果自相关系数缓慢衰减拖尾可能提示需要MA项。如果在滞后q阶后突然截断截尾可能提示MA的阶数q。PACF图如果在滞后p阶后突然截断可能提示AR的阶数p。在实际操作中完全依赖看图定阶非常主观且困难。更通用的做法是使用网格搜索Grid Search配合信息准则如AIC, BIC来选择最优的(p,d,q)组合。AIC/BIC值越小模型在拟合优度和复杂度之间平衡得越好。# 5. 拟合ARIMA模型 (这里以(1,1,1)为例实际应用应网格搜索) model ARIMA(ts_data, order(1,1,1)) model_fit model.fit() print(model_fit.summary()) # 6. 预测未来12个月 forecast_steps 12 forecast_result model_fit.get_forecast(stepsforecast_steps) forecast_mean forecast_result.predicted_mean forecast_ci forecast_result.conf_int() # 置信区间 # 7. 可视化 plt.figure(figsize(10,6)) plt.plot(ts_data, labelHistorical Data) plt.plot(forecast_mean, labelForecast, colorred) plt.fill_between(forecast_ci.index, forecast_ci.iloc[:, 0], forecast_ci.iloc[:, 1], colorpink, alpha0.3, label95% Confidence Interval) plt.legend() plt.title(ARIMA Model Forecast) plt.show()实操心得对于ARIMA最大的坑往往在“差分”和“定阶”。差分过度会导致信息损失差分不足模型不平稳。我的经验是先用ADF检验判断差分到平稳为止通常1-2阶就够了。定阶不要死磕ACF/PACF图对于初学者可以尝试用pmdarima库的auto_arima函数它能自动进行差分检测和参数搜索结果通常不错可以作为基准参考。3. 灰色预测GM(1,1)小样本情况下的“数据放大器”当你的数据少到让时间序列模型“哑火”时就该灰色预测登场了。GM(1,1)是其中最常用、最基础的模型它只用一个变量的一阶微分方程来建模。3.1 GM(1,1)模型原理四步理解其魔法假设我们有原始非负序列 $X^{(0)} (x^{(0)}(1), x^{(0)}(2), ..., x^{(0)}(n))$第一步累加生成AGO这是灰色预测的精髓。通过累加将原本可能杂乱无章的原始序列转化为具有明显指数增长趋势的新序列。 $X^{(1)} (x^{(1)}(1), x^{(1)}(2), ..., x^{(1)}(n))$ 其中$x^{(1)}(k) \sum_{i1}^{k} x^{(0)}(i), \quad k1,2,...,n$第二步构建灰微分方程GM(1,1)模型对应的灰微分方程基本形式为 $x^{(0)}(k) a z^{(1)}(k) b$ 这里$a$ 称为发展系数反映序列 $X^{(1)}$ 的发展态势$b$ 称为灰色作用量可以理解为背景值。$z^{(1)}(k)$ 是 $x^{(1)}(k)$ 的紧邻均值生成序列 $z^{(1)}(k) 0.5 (x^{(1)}(k) x^{(1)}(k-1)), \quad k2,3,...,n$第三步利用最小二乘法求解参数a, b将k从2到n代入方程可以得到一个方程组写成矩阵形式 $Y B \cdot [a, b]^T$ 其中 $Y [x^{(0)}(2), x^{(0)}(3), ..., x^{(0)}(n)]^T$ $B \begin{bmatrix} -z^{(1)}(2) 1 \ -z^{(1)}(3) 1 \ \vdots \vdots \ -z^{(1)}(n) 1 \end{bmatrix}$ 利用最小二乘法求得参数估计值 $[\hat{a}, \hat{b}]^T (B^T B)^{-1} B^T Y$第四步得到预测公式并还原求解出参数后对应的白化微分方程反映累加序列 $X^{(1)}$ 的连续变化规律为 $\frac{dx^{(1)}}{dt} a x^{(1)} b$ 其时间响应式即累加序列的预测公式为 $\hat{x}^{(1)}(k1) (x^{(0)}(1) - \frac{b}{a}) e^{-a k} \frac{b}{a}, \quad k0,1,2,...$ 最后通过累减生成IAGO还原得到原始序列的预测值 $\hat{x}^{(0)}(k1) \hat{x}^{(1)}(k1) - \hat{x}^{(1)}(k), \quad k1,2,...$ 特别地$\hat{x}^{(0)}(1) x^{(0)}(1)$。3.2 模型检验预测不能“一本正经地胡说八道”灰色预测的结果必须经过检验常用的有残差检验计算绝对残差 $\epsilon(k) x^{(0)}(k) - \hat{x}^{(0)}(k)$ 和相对残差 $\Delta_k \frac{|\epsilon(k)|}{x^{(0)}(k)}$。通常要求平均相对残差低于20%根据场景可调整最大相对残差不超过允许范围。后验差检验计算原始序列 $X^{(0)}$ 的均值 $\bar{X}$ 和标准差 $S_1$。计算残差序列 $\epsilon$ 的均值 $\bar{\epsilon}$ 和标准差 $S_2$。计算后验差比值$C S_2 / S_1$。C值越小说明预测误差的波动相对于原始数据波动越小模型越好。一般C0.35认为合格0.5可接受。计算小误差概率$P P(|\epsilon(k) - \bar{\epsilon}| 0.6745 S_1)$。P值越大越好通常P0.95优秀0.8合格。3.3 实战手写Python实现GM(1,1)并进行检验我们不依赖第三方库从头实现一遍GM(1,1)以加深理解。import numpy as np import pandas as pd import matplotlib.pyplot as plt class GM11: 手动实现的GM(1,1)灰色预测模型 def __init__(self): self.a None # 发展系数 self.b None # 灰色作用量 self.x0 None # 原始序列 self.x1 None # 一次累加序列 self.z1 None # 紧邻均值序列 self.fit_fitted None # 拟合值 self.fit_residual None # 残差 def fit(self, data): 拟合模型 self.x0 np.array(data, dtypenp.float64) n len(self.x0) # 1. 累加生成 self.x1 np.cumsum(self.x0) # 2. 计算紧邻均值序列 self.z1 (self.x1[:-1] self.x1[1:]) / 2.0 # 3. 构造矩阵B, Y B np.column_stack((-self.z1, np.ones(n-1))) Y self.x0[1:].reshape(-1, 1) # 4. 最小二乘法求解参数 BTB_inv np.linalg.inv(np.dot(B.T, B)) theta np.dot(np.dot(BTB_inv, B.T), Y) self.a, self.b theta.flatten() # 5. 计算拟合值 self.fit_fitted self._predict_values(n) self.fit_residual self.x0 - self.fit_fitted return self def _predict_values(self, steps): 预测指定步数的值包括拟合和未来预测 n_orig len(self.x0) pred_x1 np.zeros(steps) # 累加序列预测公式 pred_x1[0] (self.x0[0] - self.b/self.a) * np.exp(-self.a * 0) self.b/self.a for k in range(1, steps): pred_x1[k] (self.x0[0] - self.b/self.a) * np.exp(-self.a * k) self.b/self.a # 累减还原为原始序列预测值 pred_x0 np.zeros(steps) pred_x0[0] pred_x1[0] for k in range(1, steps): pred_x0[k] pred_x1[k] - pred_x1[k-1] # 对于拟合部分第一个值就是原始值 pred_x0[0] self.x0[0] return pred_x0 def predict(self, steps1): 预测未来steps个值 n_orig len(self.x0) all_pred self._predict_values(n_orig steps) return all_pred[n_orig:] # 返回未来预测部分 def fit_predict(self, data, future_steps1): 拟合并预测的便捷方法 self.fit(data) return self.predict(future_steps) def evaluate(self): 模型后验差检验 if self.fit_residual is None: raise ValueError(请先调用fit方法拟合模型。) n len(self.x0) # 原始序列均值标准差 S1 np.std(self.x0, ddof1) # 样本标准差 # 残差序列均值标准差 residual self.fit_residual S2 np.std(residual, ddof1) # 后验差比值C C S2 / S1 # 小误差概率P mean_residual np.mean(residual) count np.sum(np.abs(residual - mean_residual) 0.6745 * S1) P count / n # 相对残差 relative_error np.abs(self.fit_residual / self.x0) * 100 avg_relative_error np.mean(relative_error[1:]) # 通常忽略第一个点 evaluation { C: C, P: P, avg_relative_error(%): avg_relative_error, max_relative_error(%): np.max(relative_error[1:]), development_coefficient(a): self.a, grey_input(b): self.b } return evaluation # 使用示例假设我们有某产品过去6个月的销量小样本 original_data np.array([120, 135, 150, 142, 160, 175]) # 仅6个数据点 print(原始数据:, original_data) model GM11() model.fit(original_data) future_pred model.predict(steps3) # 预测未来3期 print(未来3期预测值:, future_pred) # 模型检验 eval_result model.evaluate() print(\n 模型检验结果 ) for key, value in eval_result.items(): print(f{key}: {value:.4f} if isinstance(value, float) else f{key}: {value}) # 判断模型精度等级参考 C, P eval_result[C], eval_result[P] if C 0.35 and P 0.95: grade 优秀 (Good) elif C 0.5 and P 0.8: grade 合格 (Qualified) elif C 0.65 and P 0.7: grade 勉强合格 (Barely Qualified) else: grade 不合格 (Unqualified) print(f模型精度等级: {grade}) # 可视化 fitted_values model.fit_fitted x_historical np.arange(len(original_data)) x_future np.arange(len(original_data), len(original_data)3) plt.figure(figsize(10,6)) plt.plot(x_historical, original_data, bo-, labelOriginal Data, markersize8) plt.plot(x_historical, fitted_values, rs--, labelFitted Values, markersize6) plt.plot(x_future, future_pred, g^--, labelForecast, markersize10) plt.axvline(xlen(original_data)-0.5, colorgray, linestyle:, alpha0.7) plt.text(len(original_data)/2, max(original_data)*1.05, Fitting Period, hacenter) plt.text(len(original_data)1, max(original_data)*1.05, Forecast Period, hacenter) plt.xlabel(Time Step) plt.ylabel(Value) plt.title(GM(1,1) Model Fitting and Forecasting) plt.legend() plt.grid(True, alpha0.3) plt.show()踩坑实录灰色预测对数据有要求。第一数据必须是非负的如果有负数需要先做平移处理所有数据加上一个常数使其为正。第二数据需要具有指数趋势。如果原始数据是纯随机波动GM(1,1)强行拟合的结果后验差检验C值会很大P值很小预测结果不可信。所以务必进行模型检验不要拿到预测值就直接用。第三预测期不宜过长。GM(1,1)本质是指数外推对于中长期预测误差会迅速放大一般只建议做短期预测如未来1-3期。4. 模型对比与选型指南何时用谁学完了两种方法最关键的问题是我该用哪个下面这个表格从多个维度进行了对比。特性维度时间序列模型 (如ARIMA)灰色预测模型 (GM(1,1))数据需求需要较多数据点通常50以识别稳定的统计规律。核心优势只需少量数据通常≥4个点即可建模。数据特性要求序列平稳或可差分平稳能处理包含趋势、季节、周期的复杂模式。要求数据非负适合具有单调趋势如近似指数增长/衰减的序列。对波动大的随机序列效果差。模型原理基于随机过程理论挖掘数据自身的自相关和移动平均结构。基于灰色系统理论通过累加生成挖掘数据内在的指数规律处理“贫信息”不确定性。预测范围短、中、长期预测均可但长期预测不确定性会增大。通常只适合短期预测。中长期外推因是指数形式可能偏离实际。结果输出提供点预测和概率性置信区间对预测不确定性有量化。通常只提供点预测值。可通过构建多个灰微分方程如GM(1,1)包络模型来估计范围但非标准置信区间。计算复杂度相对较高涉及平稳性检验、模型定阶、参数估计等。计算简单快捷核心是最小二乘法求解两个参数。适用场景数据量充足序列模式相对清晰需要进行稳健统计预测的场景。如经济指标预测、销量预测有多年历史数据。数据稀缺但业务又急需一个预测参考。如新产品初期销量预估、突发事件的短期影响预测、设备在少量监测数据下的故障趋势判断。选型决策流看数据量如果数据点少于10个优先考虑灰色预测。如果数据充足比如几十上百个进入下一步。看序列模式绘制时序图。如果序列有明显的季节性或周期性波动ARIMA或其变体如SARIMA是更自然的选择。如果序列呈现单调的指数型趋势且缺乏明显周期两者都可尝试但灰色预测可能更简洁。看业务需求如果需要量化预测的不确定性即“我有多少把握”ARIMA提供的置信区间更有价值。如果只是需要一个快速的、方向性的参考灰色预测更便捷。最终验证无论如何在历史数据上做样本外预测回测用平均绝对百分比误差MAPE、均方根误差RMSE等指标客观比较不同模型的拟合效果。5. 进阶思考与常见问题排雷在实际项目中单纯套用标准模型往往不够需要根据情况调整和优化。5.1 时间序列模型的季节性处理与自动化对于有强季节性的数据如月度、季度数据需要使用SARIMA模型它在ARIMA的基础上增加了季节性参数(P,D,Q,s)。手动定阶非常复杂。如前所述使用pmdarima的auto_arima函数可以极大简化流程。# 使用auto_arima自动寻找最优SARIMA参数示例 from pmdarima import auto_arima import pmdarima as pm # 忽略警告 import warnings warnings.filterwarnings(ignore) model_auto auto_arima(ts_data, # 你的时间序列数据 start_p0, start_q0, max_p5, max_q5, seasonalTrue, # 启用季节性 m12, # 季节性周期月度数据为12 start_P0, start_Q0, max_P2, max_Q2, dNone, # 自动检测差分阶数 DNone, # 自动检测季节性差分阶数 traceTrue, # 打印搜索过程 error_actionignore, suppress_warningsTrue, stepwiseTrue) # 使用逐步搜索更快 print(model_auto.summary()) # 最佳模型参数会显示在summary中如 SARIMAX(1,1,1)(1,1,1,12)5.2 灰色预测的优化与变体标准的GM(1,1)有时精度不够可以考虑以下优化方向背景值优化标准模型用紧邻均值 $z^{(1)}(k)0.5(x^{(1)}(k)x^{(1)}(k-1))$ 作为背景值。可以尝试引入权重因子 $\alpha$改为 $z^{(1)}(k)\alpha x^{(1)}(k) (1-\alpha)x^{(1)}(k-1)$通过优化算法寻找最优的 $\alpha$通常在0到1之间以更好地匹配序列特性。初始条件修正标准模型时间响应式基于 $x^{(1)}(1)x^{(0)}(1)$。有研究认为使用 $x^{(1)}(1)$ 的模拟值或序列中其他点作为初始条件可能更好。模型组合例如先用其他方法如移动平均对原始数据平滑去除部分噪声后再用GM(1,1)预测。或者建立多个GM(1,1)模型如用不同长度的数据子序列然后对预测结果进行加权平均。使用GM(1,1)包络模型分别对原始序列的上包络序列和下包络序列建立GM(1,1)模型得到一个预测区间而非单一值从而描述预测的不确定性。5.3 实战中避不开的坑与解决思路ARIMA模型预测结果是一条直线可能原因1序列差分后已无自相关性。检查ACF/PACF图如果差分后序列的自相关系数全部快速落入置信区间内说明序列已接近白噪声无规律可循ARIMA预测未来值就会趋向于均值或最后一个值看起来像直线。可能原因2模型阶数选择不当。特别是MA(q)项的系数 $\theta$ 为零或接近零时模型退化为随机游走预测值等于最后一个观测值。解决重新审视数据是否真的具有可预测的模式。尝试使用包含外生变量的模型如ARIMAX或者换用其他更适合的模型如指数平滑。GM(1,1)预测值出现负数原因虽然原始数据非负但GM(1,1)的预测公式是指数形式在发展系数 $a0$ 时预测的累加序列 $\hat{x}^{(1)}$ 是衰减的还原后的 $\hat{x}^{(0)}$ 可能为负。解决这通常意味着原始序列并不适合用GM(1,1)建模可能不是指数增长趋势。可以尝试对原始数据做平移变换所有值加上一个正数常数C使序列整体抬高用平移后的数据建模预测结果再减去常数C还原。但这种方法需谨慎会改变序列的统计特性。数据中有异常值/缺失值怎么办时间序列对异常值敏感。可以使用滚动中位数、3-sigma原则等进行检测和修正。对于缺失值可用前向填充、线性插值或更复杂的时间序列插值方法如pandas的interpolate(methodtime)。灰色预测对异常值相对不敏感因为累加生成有一定平滑作用。但严重的异常值仍会影响背景值计算。建议在建模前先进行简单的数据清洗。如何将预测结果应用到实际业务永远不要只依赖一个模型的输出。尤其是重要决策应该建立模型组合或预测融合。例如同时运行ARIMA、指数平滑和灰色预测将它们的预测结果进行加权平均如根据历史回测的误差倒数赋予权重。理解预测的局限性所有统计预测模型都是基于“历史模式在未来延续”的假设。对于突发性事件如政策变化、黑天鹅事件模型是无法捕捉的。预测结果必须结合业务专家的定性判断进行修正。持续监控与更新模型不是一劳永逸的。随着时间的推移需要将新的实际数据加入重新训练或调整模型参数这是一个持续迭代的过程。我在处理一个新产品上市初期的销量预估时就遇到了数据极少只有前5周的周销量的问题。直接上ARIMA完全失效。我用GM(1,1)做了一个短期预测未来3周后验差检验C0.28P0.92精度等级为“优秀”。这个预测给了市场团队一个量化的参考虽然最终实际销量因为一次意外的社交媒体传播而高于预测但模型给出的增长趋势和量级范围是合理的为初期备货提供了关键依据。后来数据积累到3个月后我们迅速切换到了季节性ARIMA模型预测精度得到了进一步提升。这个案例让我深刻体会到没有最好的模型只有最适合当前数据阶段和业务场景的模型。
返回列表