1.3 Python预测初步
前言:本篇文章通过一个简单的例子来说明用python进行预测的主要步骤。让大家了解到用python进行预测的一个基本过程。
- 本实例采用wineind数据集(葡萄酒销量预测),数据集表示从1980年1月到1994年8月,葡萄酒生产商销售的容量不到1L的澳大利亚葡萄酒的总量。数据大致如下图所示:
(1)首先我们要做的第一步就是数据预处理
- 基于wineind数据集,使用statsmodels.graphics.tsaplots模块下面的plot_acf函数查看wineind数据的自相关性。代码如下。
from statsmodels.graphics.tsaplots import plot_acf,plot_pacf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False
wineind=pd.read_csv("wineind.csv")
#concat函数可以将数据根据不同的轴作为简单的融合
#axis表示需要合并连链接的轴,0是行,1是列
wineind=pd.concat([wineind,wineind.月份.str.split('-',expand=True)],axis=1)
wineind=wineind.drop(columns=['月份']).rename(columns={
0:"年份",1:"月份"})
wineind['月份']=[int(x) for x in wineind.月份]
plot_acf(wineind.销量,lags=100,title="wineind autocorrection").show()
- 其中,上图中竖线表示对应近n期延迟数据的相关系数,阴影部分表示相关性不明显的部分。其中第一条竖线表示第0期。
- 从左到右,选择一些不在阴影范围内的数据来建立指标,作为预测基础数据。(1,4,6,8,12)期数据。
(2)观察并且确定wineind数据周期为一年的,并且将1980年到1993年每年按月份的曲线图画在一张图中。代码如下。
"""
我们可以将1980年到1994年每年按照月份的曲线画在一张图中
"""
plt.figure(figsize=(10,5))
for _year_ in pd.unique(wineind.月份):
df_row=wineind.loc[wineind.年份==_year_,]
plt.plot(df_row.月份.values,df_row.销量.values,'o--',label=_year_)
#对应年份范围(1980~1993)
# x=np.arange(int(wineind.shape[0]))
#将图例分成2行7列
plt.legend(ncol=7)
for i in range(int(wineind.shape[0]/12)):
plt.plot([x+1 for x in range(12)],wineind.销量[12*i:12*(i+1)],'o--',label=wineind.月份[12*i])
#添加辅助线(也就是图像中对角线)
plt.plot([x+1 for x in range(12)],[1500*x+17000 for x in range(12)],'b--',linewidth=3)
plt.xlabel("月份",fontsize=12)
plt.ylabel("销量",fontsize=12)
plt.title("葡萄酒按月销量变化",fontsize=14)
plt.show()
- 上图中,月份于销量的线性关系非常明显,应该考虑加入建模基础数据用于预测。因此,需要将wineind的原始数据处理成如下表所示的格式,并且输出建模基础数据。
字段名称 | 字段说明 |
---|---|
id | 唯一标识 |
month | 预测月份 |
value | 预测月销量 |
r1_value | 近1月销量 |
r4_value | 近4月销量 |
r6_value | 近6月销量 |
r8_value | 近8月销量 |
r12_value | 近12月销量 |
- 数据转换代码如下。
wineind['month']=[x for x in wineind.月份]
wineind['value']=wineind.销量
sales_list=[None]*12+wineind.value.tolist()
for loc in [1,4,6,8,12]:
wineind['近'+str(loc)+'月_销量']=sales_list[(12-loc):][0:len(wineind)]
wineind=wineind.dropna()
- 画出散点矩阵
#画出散点矩阵图
import seaborn as sns
sns.pairplot(wineind,diag_kind='kde')
plt.show()
我们注意看上面的图,可以发现:近12月_销量的子图中,拥有较明显的线性关系,但是图中明显存在杠杆点,如下图所示
- 建立 销量 ~ 近12月_销量的线性模型,通过cooks标准来计算每行记录对模型的影响程度
import statsmodels.api as sm
fig,ax=plt.subplots(figsize=(12,8))
#使用普通最小二乘法拟合一条线
lm=sm.OLS(wineind.value,sm.add_constant(wineind.近12月_销量)).fit()
sm.graphics.influence_plot(lm,alpha=0.05,ax=ax,criterion="cooks")
plt.show()
其中,上图中的点表示记录,横坐标表示杠杆影响,纵坐标表示学生化残差。由图可知,91号和135号这两个样本存在明显的异常,因此需要在“近12月_销量”对应的散点图中标记出来,代码如下:
"""
从图中可知 91号 和 135号 样本存在明显的异常
现将这两个点在 销量~近12月_销量 对应的散点图中标记出来
"""
abnormal_point=wineind.loc[[91,135],]
plt.figure(figsize=(8,5))
plt.plot(wineind.近12月_销量,wineind.销量,'o',c='black')
plt.scatter(abnormal_point.近12月_销量,abnormal_point.销量, marker='o', c='r', edgecolors='k', s=200)
plt.xlabel("近12月_销量")
plt.ylabel("销量")
plt.show()
由上图可知91号和135号的点正是我们通过散点矩阵图发现的杠杆点。现在需要将这两个点从wineind中去掉,代买如下:
"""
91号和135号的点正是我们通过散点矩阵图发现的杠杆点。现将这两个样本从 wineind 中去掉
"""
wineind=wineind.drop(index=[91,135])
(3)建立模型
根据上一步得到的基础数据 wineind,提取其前150行数据作为训练集,余下的部分作为测试集,进行分区
- 数据分区及建模代码如下:
"""
根据上一步得到的基础数据 wineind,提取其前150行数据作为训练集,余下的部分作为测试集,进行分区
"""
wineind=wineind.reset_index().drop(columns='index')
train_set=wineind.loc[0:149]
test_set=wineind.loc[149:,]
X=np.column_stack((train_set.月份,train_set.近1月_销量, train_set.近4月_销量,
train_set.近6月_销量, train_set.近8月_销量, train_set.近12月_销量))
X=sm.add_constant(X)
model=sm.OLS(train_set.销量,X).fit()
print(model.summary())
最小二乘法回归分析的结果如下表所示:
由上图可知,调整之后R的平方值达到了0.847,作为模型来讲基本可以使用。但是看了一下截距项的P值为0.17,不显著,还需要进一步调整,使得P值低于0.05或0.01为止。另外,变量x2和x4的P值都非常的大,明显不显著,可以将这两个变量移除。重构模型,代码如下:
"""
重新构建模型,代码如下
"""
X=np.column_stack((train_set.月份, train_set.近4月_销量, train_set.近8月_销量, train_set.近12月_销量))
X=sm.add_constant(X)
model=sm.OLS(train_set.销量,X).fit()
print(model.summary())
由上图,很明显P值仍然比较大,但是相比于0.17略有下降,可以尝试使用非线性的思路来进一步拟合模型,在模型中加入x2(近4月_销量) 对应的二次项、三次项,重新建模,代码如下:
"""
尝试使用非线性的思路来进一步拟合模型,在模型中加入 x2(近4月_销量) 对应的二次项、三次项,重新建模
"""
X=np.column_stack((train_set.月份, train_set.近4月_销量,
train_set.近4月_销量**2,
train_set.近4月_销量**3,
train_set.近8月_销量,
train_set.近12月_销量))
X=sm.add_constant(X)
model=sm.OLS(train_set.销量,X).fit()
print(model.summary())
由上图可知,截距的P值均低于0.01,可以将该模型用于预测。Model就是我们建立的用于实践序列预测的线性回归模型。
(4)预测及误差分析
对预测数据集test_set进行预测,代码如下:
"""
将Model作为预测模型,对预测数据集 test_set 进行预测
"""
X=np.column_stack((test_set.月份,
test_set.近4月_销量,
test_set.近4月_销量**2,
test_set.近4月_销量**3,
test_set.近8月_销量,
test_set.近12月_销量))
X=sm.add_constant(X)
y_pred=model.predict(X)
diff=np.abs(test_set.销量-y_pred)/test_set.销量
print(diff)
统计预测结果,代码如下:
print(diff.describe())
其中,预测数据总共13条记录进行预测,最小百分误差率1.2%,最大百分误差率是34.6%,平均百分误差率为11.1%。
(5)总结
预测的基本过程大致如下:
- 1、数据预处理
- 2、建立模型
- 3、预测以及误差分析
课外知识补充
pd.concat()函数简介
- concat函数是在pandas底下的方法,可以将数据根据不同的轴作简单的融合
pd.concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False,
keys=None, levels=None, names=None, verify_integrity=False)
其中:
- objs: series,dataframe或者是panel构成的序列lsit
- axis: 需要合并链接的轴,0是行,1是列
- join:连接的方式 inner,或者outer
转载:https://blog.csdn.net/weixin_43890515/article/details/116525898