背景
探讨非平稳时间序列在企业场景的可行性,这边重点展示电商场景的销量预测
用最近12-1月,预测2024.2.4-2.10的销量数据
数据处理
缺失值处理:由于企业销售数据少有缺失值,这里就省略缺失值的处理
极端值处理:采用平均值填充,一般量级较少,建议将前7天的或者14天销量平均值进行 手动填充
ARIMA 模型
非平稳时间序列的分析方法可以分为确定性因素分解的时序分析和随机时序分析两大类。
确定性因素分解的方法把所有序列的变化都归结为 4 个因素(长期趋势、季节变动、循环变动和随机波动)的综合影响,其中长期趋势和季节变动的规律性信息通常比较容易提取,而由随机因素导致的波动则非常难以确定和分析,对随机信息浪费严重会导致模型拟合精度不够理想。
随机时序分析法的发展就是为了弥补确定性因素分解方法的不足。根据时间
序列的不同特点,随机时序分析可以建立的模型有 ARIMA 模型、残差自回归模型、季节模型、异方差模型等。
本项目重点介绍 ARIMA 模型对非平稳时间序列进行建模。
ARIMA 模型(p,a,q),分别代表的意思是AR、差分、MA的阶数
原始数据
import pandas as pd
df1 = pd.read_excel('D:/预测.xlsx')
df3 = df1.loc[(df1['日期']>='2023-12-01')&(df1['日期']<='2024-02-3'),:]
df3 = df3['销 量'].groupby(df3['日期']).apply(lambda x:sum(x)).reset_index()
#查看原始数据岁时间变化的图表
import matplotlib.pyplot as plt
# 原始时间序列图
plt.figure(figsize=(12,5),dpi=100)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
df3['销 量'].plot(color='green', marker='o', linestyle='dashed', linewidth=1, markersize=2)
plt.ylabel('销量')
plt.title("销量量时间序列分析图")
确定数据是与时间是否存在强相关性的
如果自相关系数接近1或-1,则表示数据存在较强的时间依赖性。 自相关系数在0到1之间,表示正相关关系。自相关系数越接近1,表示滞后期的数据与当前期的数据之间相关性越强。 自相关系数在-1到0之间,表示负相关关系。自相关系数越接近-1,表示滞后期的数据与当前期的数据之间负相关性越强。 自相关系数接近0,表示滞后期的数据与当前期的数据之间没有相关性。
# 自相关系数,查看数据与时间的相关性
plot_acf(df3['销 量'])
plt.title("原始序列的自相关图")
plt.show()
所以数据与时间是存在强相关性的
检测平稳性
看P值 pvalue,p 值显著大于 0.05,则该序列判断为非平稳序列(非平稳序列一定不是白噪声序列)
#若P值小于0.05,则说明这已经是一个平稳序列,无需差分,直接白噪声检验
# 平稳性检测
print('原始序列的ADF检验结果为:', ADF(df3['销 量']))
# 返回值依次为adf、pvalue、usedlag、nobs、critical values、icbest、regresults、resstore
在这里数据是显示平稳的,但是后面训练模型的时候,平稳性变差,所以还是可以做差分,得到一个更加平均的序列
差分
D_data = df3['销 量'].diff(1).dropna() # diff(1),1表示一阶差分
D_data.columns = [u'销量差分']
D_data.plot(color='green', marker='o', linestyle='dashed', linewidth=1, markersize=6) # 时序图
plt.title("一阶差分之后序列的时序图")
plt.ylabel('销量')
plt.show()
plot_acf(D_data) # 自相关图
plt.title("一阶差分之后序列的自相关图")
plt.show()
print('差分序列的ADF检验结果为:', ADF(D_data)) # 平稳性检测
此处检验结果表示:序列仍是平稳的
白噪声检验
# 白噪声检验
print(u'差分序列的白噪声检验结果为:', acorr_ljungbox(D_data, lags=1)) # 返回统计量和p值
# 从纯随机性结果上看,p值均小于0.05,拒绝原假设,该时间序列为非白噪声序列。
plot_pacf(D_data) # 偏自相关图
plt.title("一阶差分后序列的偏自相关图")
plt.show()
一阶差分之后序列的时序图在均值附近比较平稳地波动、自相关图有很强的短期相关性、单位根检验 p 值小于 0.05,所以一阶差分之后的序列是平稳序列。
定阶
ARMA(p,q) 当 p 和 q 均小于等于 3 的所有组合的 BIC 信息量,取其中BIC 信息量达到最小的模型阶数。
列=q,行=p
# 定阶
# ARIMA模型接口的调用方式
from statsmodels.tsa.arima.model import ARIMA
data = df3['销 量'].astype(float)
pmax = int(len(D_data) / 10) # 一般阶数不超过length/10
qmax = int(len(D_data) / 10) # 一般阶数不超过length/10
bic_matrix = [] # BIC矩阵
for p in range(pmax + 1):
tmp = []
for q in range(qmax + 1):
try: # 存在部分报错,所以用try来跳过报错。
tmp.append(ARIMA(data, order=(p, 1, q)).fit().bic)
except:
tmp.append(None)
bic_matrix.append(tmp)
bic_matrix = pd.DataFrame(bic_matrix) # 从中可以找出最小值
print('BIC矩阵:')
print(bic_matrix)
tmp_data = bic_matrix.values
tmp_data = tmp_data.flatten()
s = pd.DataFrame(tmp_data, columns=['value'])
s = s.dropna()
print('BIC最小值:', s.min())
s.to_excel('tmp.xlsx')
p, q = bic_matrix.stack().idxmin() # 先用stack展平,然后用idxmin找出最小值位置。
print(u'BIC最小的p值和q值为:%s、%s' % (p, q))
参数检验
print('模型报告为:\n', model.summary())
残差检验
import statsmodels.api as sm
print('D-W检验的结果为:', sm.stats.durbin_watson(resid.values))
# 根据Durbin-Watson统计量的计算公式,其值介于0和4之间,该值越接近2意味着不存在自相关性的可能性越大。
# 模型预测的残差不存在自相关性性,这说明拟合的模型预测效果很好
#平稳性检验
print('原始序列一阶差分的ADF检验结果为:',ADF(resid.values))
#解读:第二个是P值,小于显著性水平α(0.05),拒绝原假设(非平稳序列),说明获得平稳序列。
print('残差序列的白噪声检验结果为:', acorr_ljungbox(resid, lags=1)) # 返回统计量、P值
# P>0.05.说明原假设成立,接受原假设(纯随机序列),说明获得非白噪声序列
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/909966be0d6d42f1bb4dfa7be0554fd1.png
D-W 检验的结果为:1.76。因此,模型预测的残差不存在自相关性性,这说明拟合的模型预测效果很好。
预测
import numpy as np
forecastdata = model.forecast(7)
forecast=pd.DataFrame(np.array(forecastdata),columns=["forecast"])
forecast
这边预测出来每一个值很接近,也正好验证,我这边选择的数据源一开始检测稳定性的时候就是稳定的。
同时,也说明目前生产环境较稳定,这周和上周的区别不会很大(暗示,如果直接粗糙的用前一周 或者前2周的平均销量来预测下一周的销量数据,差异可能也不会太大)
这个预测的结果是每一天的,但是我们实际生产环境中看的是一个周维度的总计值
实际值
df4 = df1.loc[(df1['日期']>='2024-02-4')&(df1['日期']<='2024-02-10'),:]
df5 = df4['销 量'].groupby(df4['日期']).apply(lambda x:sum(x)).reset_index()
# 设置Pandas以便在输出时不显示索引
pd.set_option('display.max_rows', len(df5))
df5['销 量']
平均误差在30,这个数值放到整个生产环境来说是 可以是接受的范围内,它对于整体的达成率,影响知道0.3%。
反思
部门全月达成数据预估
法1:大盘的销售数据预估可以用过去平均值,本月已过的实际销售平均值*本月天数,得到预估达成率,这个值是可以滚动,随着本月的结束,达成率肯定会越来越准确
法2:精确某一类型的产品,运营状态是类似的,可以尝试用到上面的arima时间序列。当数据之间的相似性越明显,对于模型训练的效果是更佳,如果不介意,也可以先把商品分好类,按类目一个个预测,最后汇总也能得到大盘的达成率。