ARIMA(p,d,q) 模型实战:Python statsmodels 库实现销售预测,RMSE 降低 15%

ARIMA(p,d,q) 模型实战:Python statsmodels 库实现销售预测,RMSE 降低 15%

当销售总监问"下个季度能卖多少"时,时间序列预测就是数据分析师的魔法水晶球。在众多预测工具中,ARIMA模型以其数学严谨性和业务解释性,成为销售预测的经典选择。本文将手把手带你用Python的statsmodels库,从数据清洗到模型部署,完整实现一个可落地的销售预测方案。

1. 环境准备与数据探索

工欲善其事,必先利其器。我们先配置好Python环境:

# 基础库 import numpy as np import pandas as pd import matplotlib.pyplot as plt %matplotlib inline # 时间序列专用 from statsmodels.tsa.stattools import adfuller # ADF检验 from statsmodels.graphics.tsaplots import plot_acf, plot_pacf # ACF/PACF from statsmodels.tsa.arima.model import ARIMA # ARIMA模型 from sklearn.metrics import mean_squared_error # 评估指标

假设我们有一份2018-2022年的月度销售数据sales.csv,字段包括:

  • date: 日期(YYYY-MM-DD)
  • sales: 销售额(万元)

先进行基础探索:

df = pd.read_csv('sales.csv', parse_dates=['date']) df.set_index('date', inplace=True) print(df.head()) # 绘制原始序列 plt.figure(figsize=(12,6)) plt.plot(df, label='Raw Data') plt.title('Monthly Sales Trend') plt.legend()

常见的数据问题及处理方法:

问题类型检测方法解决方案
缺失值df.isnull().sum()线性插值或前向填充
异常值箱线图观察Winsorize处理或中位数替换
周期性分解季节成分季节差分或外部变量
趋势性ADF检验常规差分或对数变换

2. 平稳化处理与参数确定

ARIMA建模的前提是序列平稳。我们先用ADF检验验证:

def adf_test(series): result = adfuller(series) print('ADF Statistic:', result[0]) print('p-value:', result[1]) print('Critical Values:', result[4]) adf_test(df['sales']) # 假设输出p值=0.8 > 0.05,不平稳

进行一阶差分并重新检验:

df['diff_1'] = df['sales'].diff(1).dropna() adf_test(df['diff_1']) # p值=0.02 < 0.05,平稳

此时d=1。接下来通过ACF/PACF图确定p和q:

fig, (ax1, ax2) = plt.subplots(2,1, figsize=(12,8)) plot_acf(df['diff_1'], lags=20, ax=ax1) plot_pacf(df['diff_1'], lags=20, ax=ax2) plt.show()

根据图形特征:

  • PACF在lag=2后截尾 → p=2
  • ACF在lag=1后截尾 → q=1

也可以使用网格搜索确定最优参数:

best_aic = np.inf best_order = None for p in range(0, 3): for d in range(0, 2): for q in range(0, 3): try: model = ARIMA(df['sales'], order=(p,d,q)) results = model.fit() if results.aic < best_aic: best_aic = results.aic best_order = (p,d,q) except: continue print(f'Best ARIMA{best_order} with AIC={best_aic:.2f}')

3. 模型训练与验证

拆分训练集和测试集(保留最后12个月验证):

train = df[:-12] test = df[-12:] model = ARIMA(train['sales'], order=(2,1,1)) model_fit = model.fit() print(model_fit.summary())

关键输出解读:

  • coef列:各参数权重及显著性(P>|z|应<0.05)
  • Ljung-Box:残差自相关检验(p值应>0.05)
  • Jarque-Bera:残差正态性检验

进行滚动预测验证:

history = train['sales'].tolist() predictions = [] for t in range(len(test)): model = ARIMA(history, order=(2,1,1)) model_fit = model.fit() yhat = model_fit.forecast()[0] predictions.append(yhat) history.append(test['sales'][t]) rmse = np.sqrt(mean_squared_error(test['sales'], predictions)) print(f'Test RMSE: {rmse:.2f}')

4. 预测优化与业务应用

通过以下技巧可进一步提升效果:

技巧1:Box-Cox变换处理异方差

from scipy.stats import boxcox train['trans'], lam = boxcox(train['sales']) # 预测后需用inv_boxcox还原

技巧2:引入外部变量

# 添加促销活动指标 model = ARIMA(train['sales'], exog=train['promo'], order=(2,1,1))

技巧3:残差诊断可视化

residuals = pd.DataFrame(model_fit.resid) residuals.plot(kind='kde') # 应接近正态分布

最终预测未来12个月销售:

final_model = ARIMA(df['sales'], order=(2,1,1)) final_fit = final_model.fit() forecast = final_fit.get_forecast(steps=12) # 获取置信区间 conf_int = forecast.conf_int() forecast_index = pd.date_range(df.index[-1], periods=13, freq='M')[1:] # 可视化 plt.figure(figsize=(12,6)) plt.plot(df.index, df['sales'], label='Historical') plt.plot(forecast_index, forecast.predicted_mean, color='r', label='Forecast') plt.fill_between(forecast_index, conf_int.iloc[:,0], conf_int.iloc[:,1], color='pink', alpha=0.3) plt.title('12-Month Sales Forecast with 95% CI') plt.legend()

将预测结果转化为业务建议:

  1. 库存准备:根据预测下限确保基础库存
  2. 营销预算:参考预测中值分配资源
  3. 风险预案:对置信区间上界做产能准备