前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一个完整的销售预测数据挖掘实战案例

一个完整的销售预测数据挖掘实战案例

作者头像
数据STUDIO
发布2024-07-24 09:41:28
750
发布2024-07-24 09:41:28
举报
文章被收录于专栏:数据STUDIO

0. 项目案例描述

沃尔玛是美国领先的零售商之一,他们希望能够准确预测销售和需求,因为一些事件和节假日可能会影响每天的销售额。目前,他们有 45 家商店的销售数据,但由于机器学习算法的不适用,他们面临着不可预见的需求和库存短缺的挑战。理想情况下,一种合适的机器学习算法应该能够准确预测需求,并考虑到包括 CPI、失业指数等在内的经济状况因素。

沃尔玛全年都会举办几次促销减价活动。这些减价活动都是在重要节假日之前进行的,其中最大的四个节假日是超级碗、劳动节、感恩节和圣诞节。包括这些节假日在内的几周在评估中的权重是非节假日周的五倍。在缺乏完整/理想历史数据的情况下,对这些节假日周的降价影响进行建模,是此次竞争所面临的部分挑战。我们提供了位于不同地区的 45 家沃尔玛商店的历史销售数据。

数据集信息 这是 2010-02-05 至 2012-11-01 期间的历史销售数据,数据文件直接在@公众号:数据STUDIO 后台回复 240707 免费获取。在该文件中,可以找到以下字段:

  • Store: 商店 - 商店编号
  • Date: 日期 - 销售的星期
  • Weekly_Sales - 指定商店的销售额
  • Holiday_Flag - 本周是否为特殊假日周 1 - 假日周 0 - 非假日周
  • Temperature: 温度 - 销售当天的温度
  • Fuel_Price: 燃料价格 - 该地区的燃料成本
  • CPI - 现行消费价格指数
  • Unemployment: 失业率 - 当时的失业率
  • Holiday Events: 假日活动 Super Bowl: 超级碗 12-Feb-10, 11-Feb-11, 10-Feb-12, 8-Feb-13 Labour Day: 劳动节 10-Sep-10, 9-Sep-11, 7-Sep-12, 6-Sep-13 Thanksgiving: 感恩节 26-Nov-10, 25-Nov-11, 23-Nov-12, 29-Nov-13 Christmas: 圣诞节 31-Dec-10, 30-Dec-11, 28-Dec-12, 27-Dec-13

项目流程

  • 了解数据集并进行清理(如果需要)。
  • 建立回归模型,利用单一和多重特征预测销售额。
  • 同时评估模型并比较各自的得分,如 R2、RMSE 等。

我们的目标是通过制定行动计划来解决问题陈述,以下是一些必要的步骤:

  1. 数据探索
  2. 探索性数据分析(EDA)
  3. 数据预处理
  4. 数据处理
  5. 特征选择/提取
  6. 预测建模
  7. 项目成果与结论

1. 数据探索

本项目所用python库如下

代码语言:javascript
复制
import os
import math
import numpy as np
import pandas as pd
import seaborn as sns
from IPython.display import display

from statsmodels.formula import api
from sklearn.feature_selection import RFE
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from statsmodels.stats.outliers_influence import variance_inflation_factor

from sklearn.decomposition import PCA
from sklearn.linear_model import Ridge
from sklearn.linear_model import Lasso
from sklearn.linear_model import ElasticNet
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import PolynomialFeatures
from sklearn.metrics import r2_score, mean_absolute_error, mean_squared_error

import matplotlib.pyplot as plt
plt.rcParams['figure.figsize'] = [10,6]

import warnings 
warnings.filterwarnings('ignore')

导入数据集

代码语言:javascript
复制
df = pd.read_csv('Walmart.csv') # 完整数据集和代码,在@公众号:数据STUDIO 后台回复 240707
display(df.head())

该数据包含 8 个特征 & 6435 个样本.

重构 columns

代码语言:javascript
复制
df.Date=pd.to_datetime(df.Date)

df['weekday'] = df.Date.dt.weekday
df['month'] = df.Date.dt.month
df['year'] = df.Date.dt.year

df.drop(['Date'], axis=1, inplace=True)#,'month'
target = 'Weekly_Sales'
features = [i for i in df.columns if i not in [target]]
original_df = df.copy(deep=True)

df.head()

检查所有列信息

代码语言:javascript
复制
df.info()
代码语言:javascript
复制
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6435 entries, 0 to 6434
Data columns (total 10 columns):
 #   Column        Non-Null Count  Dtype  
---  ------        --------------  -----  
 0   Store         6435 non-null   int64  
 1   Weekly_Sales  6435 non-null   float64
 2   Holiday_Flag  6435 non-null   int64  
 3   Temperature   6435 non-null   float64
 4   Fuel_Price    6435 non-null   float64
 5   CPI           6435 non-null   float64
 6   Unemployment  6435 non-null   float64
 7   weekday       6435 non-null   int64  
 8   month         6435 non-null   int64  
 9   year          6435 non-null   int64  
dtypes: float64(5), int64(5)
memory usage: 502.9 KB

检查每个特征的唯一值个数

代码语言:javascript
复制
df.nunique().sort_values()
代码语言:javascript
复制
Holiday_Flag       2
year               3
weekday            7
month             12
Store             45
Unemployment     349
Fuel_Price       892
CPI             2145
Temperature     3528
Weekly_Sales    6435
dtype: int64

检查每个特征中唯一的行数

代码语言:javascript
复制
nu = df[features].nunique().sort_values()
nf = []; cf = []; nnf = 0; ncf = 0; 

for i in range(df[features].shape[1]):
    if nu.values[i]<=45:cf.append(nu.index[i])
    else: nf.append(nu.index[i])

该数据集包含 4 个数值型 & 5 个类别型特征。

检查所有列的统计量信息

代码语言:javascript
复制
display(df.describe())

统计数据似乎没有问题,我们对数据集做进一步分析。

2. 探索性数据分析 (EDA)

目标变量可视化

我们首先分析一下目标变量的分布情况。(由于篇幅限制,可视化部分直接贴图,其余展示主要代码,若需要完整代码,在@公众号:数据STUDIO 后台回复 240707 获取即可~)

目标变量似乎呈正态分布,平均约为 20 个单位。

类别型特征可视化

数值型特征可视化

似乎有一些异常值,接下来我们需要解决这些问题...

特征之间的关系

我们可以发现一些特征具有线性关系,接下来需要进一步分析检测多重共线性。

3. 据预处理

删除重复值(如果存在)

代码语言:javascript
复制
df.drop_duplicates(inplace=True)

删除重复值数量---> 482

检查缺失值

代码语言:javascript
复制
nvc = pd.DataFrame(df.isnull().sum().sort_values(), columns=['Total Null Values'])
nvc['Percentage'] = round(nvc['Total Null Values']/df.shape[0],3)*100
print(nvc)
代码语言:javascript
复制
              Total Null Values  Percentage
Store                         0         0.0
Weekly_Sales                  0         0.0
Holiday_Flag                  0         0.0
Temperature                   0         0.0
Fuel_Price                    0         0.0
CPI                           0         0.0
Unemployment                  0         0.0
weekday                       0         0.0
month                         0         0.0
year                          0         0.0

数据集没有任何不一致的值。

类别型特征转换

代码语言:javascript
复制
for i in fcc:
    if df[i].nunique()==2:
        d[i]=pd.get_dummies(df[i], drop_first=True, prefix=str(i))
    if (df[i].nunique()>2):
        df = pd.concat([df.drop([i], axis=1), pd.DataFrame(pd.get_dummies(df3[i], drop_first=True, prefix=str(i)))],axis=1)
代码语言:javascript
复制
经 One-Hot 编码的特征: 
Holiday_Flag

经哑变量编码的特征:
year
weekday
month
Store

去除离群值

代码语言:javascript
复制
features1 = nf
for i in features1:
    Q1 = df1[i].quantile(0.25)
    Q3 = df1[i].quantile(0.75)
    IQR = Q3 - Q1
    df1 = df1[df1[i] <= (Q3+(1.5*IQR))]
    df1 = df1[df1[i] >= (Q1-(1.5*IQR))]
    df1 = df1.reset_index(drop=True)
display(df1.head())
代码语言:javascript
复制
删除离群值前, 数据集有 6435 个样本。
删除离群值后,数据集有 5953 个样本。

预处理后的最终数据集大小:数据清洗后, 482个样本被抛弃, 占总数据量的 7.49%

4. 数据处理

数据划分为训练集和测试集

代码语言:javascript
复制
X = df.drop([target],axis=1)
Y = df[target]
Train_X, Test_X, Train_Y, Test_Y = train_test_split(X, Y, train_size=0.8, test_size=0.2, random_state=100)
代码语言:javascript
复制
原始数据集 --->  (5953, 68) (5953,) 
训练数据集  --->  (4762, 68) (4762,) 
测试数据集   --->  (1191, 68)  (1191,)

特征归一化

代码语言:javascript
复制
std = StandardScaler()
Train_X_std = std.fit_transform(Train_X)
Test_X_std = std.transform(Test_X)

5. 特征工程

首先通过相关性矩阵查看各个特征间的相关性。

特征之间似乎存在很强的多重相关性。现在需要尝试解决这些问题...

用统计模型测试线性回归模型

代码语言:javascript
复制
Train_xy = pd.concat([Train_X_std,Train_Y.reset_index(drop=True)],axis=1)
a = Train_xy.columns.values

API = api.ols(formula='{} ~ {}'.format(target,' + '.join(i for i in Train_X.columns)), data=Train_xy).fit()
API.summary()
方法:

我们可以通过两种技术来解决多重共线性问题:

  1. 手动方法--方差膨胀因子(VIF)
  2. 自动方法--递归特征消除法(RFE)
  3. 使用 PCA 分解法消除特征

手动方法--方差膨胀因子(VIF)

代码语言:javascript
复制
from sklearn.preprocessing import PolynomialFeatures
DROP=[]
for i in range(len(Train_X_std.columns)):
    vif = pd.DataFrame()
    X = Train_X_std.drop(DROP,axis=1)
    vif['Features'] = X.columns
    vif['VIF'] = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])]
    vif['VIF'] = round(vif['VIF'], 2)
    vif = vif.sort_values(by = "VIF", ascending = False)
    if vif.loc[0][1]>1:
        DROP.append(vif.loc[0][0])
        # 建模
        LR = LinearRegression()
        LR.fit(Train_X_std.drop(DROP,axis=1), Train_Y)
        pred1 = LR.predict(Train_X_std.drop(DROP,axis=1))
代码语言:javascript
复制
Dropped Features -->  ['CPI', 'Unemployment', 'Fuel_Price', 'weekday_4', 'month_7', 'Store_7', 'Temperature', 'month_12', 'Store_43', 'year_2012', 'Store_30', 'month_2', 'month_11', 'Store_16', 'month_5', 'Store_25', 'Store_29', 'month_10', 'Store_17', 'Holiday_Flag', 'Store_18', 'year_2011', 'Store_19', 'month_9', 'Store_20', 'Store_8', 'Store_34', 'Store_15', 'Store_22', 'month_6', 'Store_21', 'Store_35', 'Store_14', 'Store_13', 'Store_45', 'Store_27', 'month_3', 'weekday_1', 'Store_23', 'Store_44', 'Store_42', 'Store_11', 'weekday_5', 'Store_39', 'weekday_2', 'weekday_3', 'Store_24', 'Store_41', 'Store_40', 'Store_10', 'Store_36', 'Store_9', 'month_4', 'Store_2', 'Store_3', 'Store_6']

自动方法--递归特征消除法(RFE)

代码语言:javascript
复制
from sklearn.preprocessing import PolynomialFeatures
Trr=[]; Tss=[]; n=3
m=df.shape[1]-2
for i in range(m):
    lm = LinearRegression()
    rfe = RFE(lm,n_features_to_select=Train_X_std.shape[1]-i)
    rfe = rfe.fit(Train_X_std, Train_Y)
  # 建模
    LR = LinearRegression()
    LR.fit(Train_X_std.loc[:,rfe.support_], Train_Y)
    pred1 = LR.predict(Train_X_std.loc[:,rfe.support_])

使用 PCA 分解法消除特征

代码语言:javascript
复制
from sklea.decomposition import PCA

pca = PCA().fit(Train_X_std)
pca.explained_variance_ratio_ # 解释方差
np.cumsum(pca.explained_variance_ratio_ # 累计解释方差
pca.n_components_ #主成分
代码语言:javascript
复制
from sklearn.decomposition import PCA
from sklearn.preprocessing import PolynomialFeatures
Trr=[]; Tss=[]; n=3
order=['ord-'+str(i) for i in range(2,n)]
Trd = pd.DataFrame(np.zeros((10,n-2)), columns=order)
Tsd = pd.DataFrame(np.zeros((10,n-2)), columns=order)
m=df.shape[1]-1

for i in range(m):
    pca = PCA(n_components=Train_X_std.shape[1]-i)
    Train_X_std_pca = pca.fit_transform(Train_X_std)
    Test_X_std_pca = pca.fit_transform(Test_X_std)
    # 建模
    LR = LinearRegression()
    LR.fit(Train_X_std_pca, Train_Y)
    pred1 = LR.predict(Train_X_std_pca)

可以看出,在未使用 VIF、RFE 和 PCA 技术放弃特征的情况下,模型的性能相当。比较 RMSE 图,发现使用手动 RFE 技术丢弃大多数特征的最佳值。但由于高级多重线性算法可以解决多重共线性问题,因此我们暂且跳过这些问题。

筛选出所选特征(RFE )

代码语言:javascript
复制
lm = LinearRegression()
rfe = RFE(lm,n_features_to_select=Train_X_std.shape[1]-28) 
rfe = rfe.fit(Train_X_std, Train_Y)

LR = LinearRegression()
LR.fit(Train_X_std.loc[:,rfe.support_], Train_Y)
pred1 = LR.predict(Train_X_std.loc[:,rfe.support_])
pred2 = LR.predict(Test_X_std.loc[:,rfe.support_])

print("训练集MSE:", np.sqrt(mean_squared_error(Train_Y, pred1)))
print("测试集MSE:",np.sqrt(mean_squared_error(Test_Y, pred2)))

Train_X_std = Train_X_std.loc[:,rfe.support_]
Test_X_std = Test_X_std.loc[:,rfe.support_]
代码语言:javascript
复制
训练集MSE: 152984.3455868294
测试集MSE: 157283.79051514965

6. 预测建模

首先,定义一个函数来评估模型。

代码语言:javascript
复制
Model_Evaluation_Comparison_Matrix = pd.DataFrame(np.zeros([5,8]), 
                                                  columns=['Train-R2','Test-R2','Train-RSS','Test-RSS',
                                                                            'Train-MSE','Test-MSE','Train-RMSE','Test-RMSE'])
rc=np.random.choice(Train_X_std.loc[:,Train_X_std.nunique()>=50].columns.values,2,replace=False)
def Evaluate(n, pred1,pred2):
    """
    在实际数据点旁边绘制预测的预测值 
    """
    # 详情见完整代码(@公众号:数据STUIDO 后台回复 240707 获取)

现在,尝试建立多元回归模型并比较其评估指标,以选择训练集和测试集的最佳拟合模型...

多元线性回归

代码语言:javascript
复制
MLR = LinearRegression().fit(Train_X_std,Train_Y)
pred1 = MLR.predict(Train_X_std)
print('回归模型的截距为 ',MLR.intercept_)
Evaluate(0, pred1, pred2)
代码语言:javascript
复制
<<<------------------------------[1m 评估多元线性回归模型 [0m------------------------------>>>

回归模型的截距为  1047603.298112138
代码语言:javascript
复制
--------------------训练集指标--------------------

训练集的 R2 分数  ---> 0.9276826744775732
训练集的残差平方和 (RSS)  ---> 111450847994430.22
训练集的均方误差 (MSE)    ---> 23404209994.630455
训练集的均方根误差 (RMSE) ---> 152984.3455868294

--------------------测试集指标--------------------

测试集的 R2 分数 ---> 0.927676279121959
测试集的残差平方和 (RSS)  ---> 29463185193746.86
测试集的均方误差 (MSE)   ---> 24738190758.813484
测试集的均方根误差 (RMSE) ---> 157283.79051514965

--------------------残差图--------------------

岭回归模型

代码语言:javascript
复制
RLR = Ridge().fit(Train_X_std,Train_Y)
pred1 = RLR.predict(Train_X_std)
print('回归模型的截距为 ',MLR.intercept_)
Evaluate(1, pred1, pred2)
代码语言:javascript
复制
<<<-----------------------------------[1m 评估岭回归模型 [0m----------------------------------->>>

回归模型的截距为  1047603.298112138
代码语言:javascript
复制
--------------------训练集指标--------------------

训练集的 R2 分数  ---> 0.9276821973327432
训练集的残差平方和 (RSS)  ---> 111451583339598.72
训练集的均方误差 (MSE)    ---> 23404364414.02745
训练集的均方根误差 (RMSE) ---> 152984.85027618732

--------------------测试集指标--------------------

测试集的 R2 分数 ---> 0.927696636618113
测试集的残差平方和 (RSS)  ---> 29454891971661.734
测试集的均方误差 (MSE)   ---> 24731227516.08878
测试集的均方根误差 (RMSE) ---> 157261.65303750554

--------------------残差图--------------------

Lasso 回归模型

代码语言:javascript
复制
LLR = Lasso().fit(Train_X_std,Train_Y)
pred1 = LLR.predict(Train_X_std)
print('回归模型截距为 ',MLR.intercept_)
Evaluate(2, pred1, pred2)
代码语言:javascript
复制
<<<-----------------------------------[1m 评估回归模型 [0m----------------------------------->>>

回归模型截距为  1047603.298112138
代码语言:javascript
复制
--------------------训练集指标--------------------

训练集的 R2 分数  ---> 0.9276826740433101
训练集的残差平方和 (RSS)  ---> 111450848663688.89
训练集的均方误差 (MSE)    ---> 23404210135.171963
训练集的均方根误差 (RMSE) ---> 152984.3460461624

--------------------测试集指标--------------------

测试集的 R2 分数 ---> 0.9276767498337136
测试集的残差平方和 (RSS)  ---> 29462993435532.15
测试集的均方误差 (MSE)   ---> 24738029752.75579
测试集的均方根误差 (RMSE) ---> 157283.27868135186

--------------------残差图--------------------

弹性网络回归

代码语言:javascript
复制
ENR = ElasticNet().fit(Train_X_std,Train_Y)
pred1 = ENR.predict(Train_X_std)
print('回归模型截距为 ',MLR.intercept_)
Evaluate(3, pred1, pred2)
代码语言:javascript
复制
<<<-----------------------------------[1m 弹性网络回归模型 [0m----------------------------------->>>

回归模型截距为  1047603.298112138
代码语言:javascript
复制
--------------------训练集指标--------------------

训练集的 R2 分数  ---> 0.7477826893125253
训练集的残差平方和 (RSS)  ---> 388701226876489.0
训练集的均方误差 (MSE)    ---> 81625625131.56006
训练集的均方根误差 (RMSE) ---> 285701.9865726524

--------------------测试集指标--------------------

测试集的 R2 分数 ---> 0.7599512663907991
测试集的残差平方和 (RSS)  ---> 97790879783118.03
测试集的均方误差 (MSE)   ---> 82108211404.80104
测试集的均方根误差 (RMSE) ---> 286545.3042797963

--------------------残差图--------------------

多项式回归模式

检查不同度数的多项式回归性能

代码语言:javascript
复制
for i in range(2,n_degree):
    poly_reg = PolynomialFeatures(degree=i)
    X_poly = poly_reg.fit_transform(Train_X_std)
    X_poly1 = poly_reg.fit_transform(Test_X_std)
    LR = LinearRegression()
    LR.fit(X_poly, Train_Y)
    pred1 = LR.predict(X_poly)
    pred2 = LR.predict(X_poly1)

我们可以选择二阶多项式回归,因为它能给出最佳的训练和测试分数。

代码语言:javascript
复制
# 使用二阶多项式回归模型(degree=2)

poly_reg = PolynomialFeatures(degree=2)
X_poly = poly_reg.fit_transform(Train_X_std)
PR = LinearRegression()
PR.fit(X_poly, Train_Y)
pred1 = PR.predict(X_poly)
print('回归模型截距为 ',MLR.intercept_)

Evaluate(4, pred1, pred2)
代码语言:javascript
复制
<<<-----------------------------------[1m 评估多项式回归模型 [0m----------------------------------->>>

回归模型截距为  1047603.298112138
代码语言:javascript
复制
--------------------训练集指标--------------------

训练集的 R2 分数  ---> 0.9431089741641003
训练集的残差平方和 (RSS)  ---> 87676819169947.55
训练集的均方误差 (MSE)    ---> 18411763790.413177
训练集的均方根误差 (RMSE) ---> 135689.95464076617

--------------------测试集指标--------------------

测试集的 R2 分数 ---> -1.0263501127334386e+19
测试集的残差平方和 (RSS)  ---> 4.1811376790307856e+33
测试集的均方误差 (MSE)   ---> 3.5106109815539756e+30
测试集的均方根误差 (RMSE) ---> 1873662451338014.0

--------------------残差图--------------------

比较各种模型的评估指标

不同回归模型的 R2 分数对比

代码语言:javascript
复制
EMC = Model_Evaluation_Comparison_Matrix.copy()
EMC.index = ["多元线性回归 (MLR)","岭线性回归 (RLR)","Lasso线性回归 (LLR)","弹性网络回归 (ENR)","多项式回归 (PNR)"]
EMC

从上图可以看出,多项式回归模型在理解数据集方面具有最高的解释力。

不同回归模型的均方根误差比较

RMSE 越小,模型越好!而且,前提是模型必须与训练和测试得分非常接近。对于这个问题,可以说多项式回归明显过度拟合了当前问题。令人惊讶的是,简单的多元线性回归模型给出了最好的结果。

7. 项目成果和结论

以下是该项目的一些主要内容:

  • 数据集非常小,只有 6435 个样本,经过预处理后,7.5% 的数据样本被删除。
  • 可视化数据分布及其关系有助于我们深入了解特征集。
  • 这些特征具有较高的多重共线性,因此在特征提取步骤中,我们使用 VIF 技术筛选出了合适的特征。
  • 使用默认超参数器测试多种算法,让我们了解了各种模型在这一特定数据集上的性能。
  • 可以肯定的是,多元回归算法比其他算法表现得更好,因为它们的得分不相上下,而且更具通用性。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-07-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据STUDIO 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0. 项目案例描述
    • 项目流程
    • 1. 数据探索
    • 2. 探索性数据分析 (EDA)
      • 目标变量可视化
        • 类别型特征可视化
          • 数值型特征可视化
            • 特征之间的关系
            • 3. 据预处理
              • 删除重复值(如果存在)
                • 检查缺失值
                  • 类别型特征转换
                    • 去除离群值
                    • 4. 数据处理
                      • 数据划分为训练集和测试集
                        • 特征归一化
                        • 5. 特征工程
                          • 用统计模型测试线性回归模型
                            • 方法:
                          • 手动方法--方差膨胀因子(VIF)
                            • 自动方法--递归特征消除法(RFE)
                              • 使用 PCA 分解法消除特征
                                • 筛选出所选特征(RFE )
                                • 6. 预测建模
                                  • 多元线性回归
                                    • 岭回归模型
                                      • Lasso 回归模型
                                        • 弹性网络回归
                                          • 多项式回归模式
                                            • 比较各种模型的评估指标
                                            • 7. 项目成果和结论
                                            相关产品与服务
                                            腾讯云服务器利旧
                                            云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
                                            领券
                                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档