大型商场销售预测

《大型商场销售预测》 屏幕快照 2018-03-30 12.42.58.png
《大型商场销售预测》 屏幕快照 2018-03-30 12.25.16.png
《大型商场销售预测》 屏幕快照 2018-03-30 12.43.13.png

分为以下几个阶段探讨这个问题

  • 1.假设生成 – 通过头脑风暴可能影响结果的可能因素更好地理解问题
  • 2.数据探索 – 查看分类和连续的功能摘要并对数据进行推断。
  • 3.数据清理 – 输入数据中的缺失值并检查异常值
  • 4.特征工程 – 修改现有变量并创建新的分析
  • 5.模型建立 – 对数据进行预测模型
%matplotlib inline
# 忽略警告提示
import warnings
warnings.filterwarnings('ignore')
import pandas as pd
import numpy as np

加载数据

train=pd.read_csv('Train_UWu5bXk.csv')
test=pd.read_csv('Test_u94Q5KV.csv')

探索数据

train.shape,test.shape
((8523, 12), (5681, 11))

训练数据有8523行,12列,测试数据有5681行,11列数据

#训练集和测试集合并到一起处理
all_data=pd.concat([train,test],ignore_index=True)
#查看数据统计描述信息
all_data.describe()
#查看前5列
all_data.head()
#查看数据类型
all_data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 14204 entries, 0 to 14203
Data columns (total 12 columns):
Item_Fat_Content             14204 non-null object
Item_Identifier              14204 non-null object
Item_MRP                     14204 non-null float64
Item_Outlet_Sales            8523 non-null float64
Item_Type                    14204 non-null object
Item_Visibility              14204 non-null float64
Item_Weight                  11765 non-null float64
Outlet_Establishment_Year    14204 non-null int64
Outlet_Identifier            14204 non-null object
Outlet_Location_Type         14204 non-null object
Outlet_Size                  10188 non-null object
Outlet_Type                  14204 non-null object
dtypes: float64(4), int64(1), object(7)
memory usage: 1.3+ MB

#查看缺失值情况
all_data.isnull().sum().sort_values(ascending=False).head()
Item_Outlet_Sales       5681
Outlet_Size             4016
Item_Weight             2439
Outlet_Type                0
Outlet_Location_Type       0
dtype: int64
  • Item_Weight 产品重量存在缺失值,需要处理
  • Outlet_Size 商店大小存在缺失值,需要处理
  • Item_Outlet_Sales 商品销售额的缺失值符合测试集的数目,不用处理
  • Item_Fat_Content 商品低脂变量,有LF,low fat,Low Fat,reg,Regula五个类别,从类别描述看可以分为Low Fat和Regular两个变量,表示低脂和常规
  • Item_Visibility 商品的展示百分比数据中存在最低值为0,由于训练集的每个商品数据都有对应的销售额,因此这是异常的值,需要处理
  • Item_Type 产品类别归类较多,需要进一步看看能够生成新的归类变量
  • Item_MRP 给出了商品的标价,能够计算出对应商品的销量(相对于销售额,销量更易于理解和探索)
  • Outlet_Establishment_Year 商店的开店年份19xx年,这样的数据不直观,可以调整为年数

清洗数据

1、处理缺失值:商品重量

由于每种商品的重量应该是相同的,所以用商品的重量均值补插缺失的值

item_avg_weighttt=all_data.groupby(['Item_Identifier'])['Item_Weight'].mean()
item_avg_weighttt['FDS02']
miss_bool = all_data ['Item_Weight'].isnull()
all_data.loc[miss_bool,'Item_Weight']=all_data.loc[miss_bool,'Item_Identifier'].apply(lambda x : item_avg_weighttt[x])

print (sum(all_data.Item_Weight.isnull()))
0

2、缺失值处理:商店大小

  • 结果看出每个商店的类型,大小与平均销售额的关系,似乎商店的大小确实和设想的一样,与商店的类型有比较强的对应关系
  • 可以构建一个决策树来预测和补插商店大小的缺失值
  • 个人理解商店大小是比较关键的变量,但因为数据集中的商店数量较少,因此在商店层面的区分效果没有商店类型变量那么好
#用随机森林插补缺失值,用商店类型特征
print (all_data.Outlet_Size.value_counts())
all_data.Outlet_Type.value_counts()
Medium    4655
Small     3980
High      1553
Name: Outlet_Size, dtype: int64





Supermarket Type1    9294
Grocery Store        1805
Supermarket Type3    1559
Supermarket Type2    1546
Name: Outlet_Type, dtype: int64
def number_Outlet_Size(x):
    if x =='Medium':
        return 1
    elif x=='Small':
        return 2
    elif x=='High':
        return 3
    else:
        return x
def number_Outlet_Type(x):
    if x=='Supermarket Type1':
        return 1
    elif x=='Supermarket Type2':
        return 2
    elif x=='Supermarket Type3':
        return 3
    elif x=='Grocery Store':
        return 4
    else :
        return x
all_data['Outlet_Size']=all_data['Outlet_Size'].map(number_Outlet_Size)
all_data['Outlet_Type']=all_data['Outlet_Type'].map(number_Outlet_Type)
print (all_data.Outlet_Size.value_counts())
all_data.Outlet_Type.value_counts()
1.0    4655
2.0    3980
3.0    1553
Name: Outlet_Size, dtype: int64





1    9294
4    1805
3    1559
2    1546
Name: Outlet_Type, dtype: int64
x=all_data.loc[all_data['Outlet_Size'].notnull(),['Outlet_Type']]
y=all_data.loc[all_data['Outlet_Size'].notnull(),['Outlet_Size']]
z=all_data.loc[all_data['Outlet_Size'].isnull(),['Outlet_Type']]
import matplotlib.pyplot as plt
import seaborn as sns
sns.barplot(x='Outlet_Size',y='Item_Outlet_Sales',hue='Outlet_Type',data=all_data)
<matplotlib.axes._subplots.AxesSubplot at 0x11b559f60>

《大型商场销售预测》 output_21_1.png

# #决策树
# #DecisionTreeClassifier
from sklearn.datasets import load_iris
from sklearn import tree
clf = tree.DecisionTreeClassifier()
clf = clf.fit(x,y)
pred=clf.predict(z)
all_data.loc[all_data['Outlet_Size'].isnull(),['Outlet_Size']]=pred
print ('处理后商店大小的分布:'\
       ,all_data['Outlet_Size'].value_counts())
all_data['Outlet_Size'].isnull().sum()
处理后商店大小的分布: 2.0    7996
1.0    4655
3.0    1553
Name: Outlet_Size, dtype: int64
0

sns.barplot(x='Outlet_Size',y='Item_Outlet_Sales',hue='Outlet_Type',data=all_data)
<matplotlib.axes._subplots.AxesSubplot at 0x11c052390>

《大型商场销售预测》 output_24_1.png

all_data['Outlet_Size'].value_counts(normalize=True)
2.0    0.562940
1.0    0.327725
3.0    0.109335
Name: Outlet_Size, dtype: float64
#查看商店大小的平均销售额
all_data.pivot_table(values='Item_Outlet_Sales',index='Outlet_Size')
# #构建一个决策树来插补缺失值,特征用商店类型
# size_train=all_data[['Outlet_Size','Outlet_Type']]
# size_train=pd.get_dummies(size_train)
# known_size=size_train[size_train.Outlet_Size.notnull()].as_matrix()
# unknown_size=size_train[size_train.Outlet_Size.isnull()].as_matrix()
# y=known_size[:,0]
# x=known_size[:,1:]
# test_size=unknown_size[:,1::]

# #决策树
# #DecisionTreeClassifier
# from sklearn.datasets import load_iris
# from sklearn import tree
# clf = tree.DecisionTreeClassifier()
# clf = clf.fit(x, y)
# pred=clf.predict(test_size)
# all_data.loc[(all_data.Outlet_Size.isnull()),'Outlet_Size']=pred
# #检查
# all_data.Outlet_Size.isnull().value_counts()

特征工程和EDA

1、创建一个表示销售量的变量

查看商店类型的平均销售额

all_data.pivot_table(values='Item_Outlet_Sales',index='Outlet_Type')

显示它们之间有显着的差异[图片上传中…(output_93_1.png-76053b-1522385784103-0)]

Item_Visibility

将Item_Visibility中为0的商品调整为每个商店中的平均值

#visibility_avg=all_data.pivot_table(values='Item_Visibility',index='Item_Identifier')
visibility_avg=all_data.groupby(['Item_Identifier'])['Item_Visibility'].mean()
miss_bool=(all_data['Item_Visibility']==0)
print ("Item_Visibility值等于0的数量",sum(miss_bool))
all_data.loc[miss_bool,'Item_Visibility']=\
all_data.loc[miss_bool,'Item_Identifier'].apply(lambda x :visibility_avg[x])
print ("处理后Item_Visibility值等于0的数量",sum(all_data['Item_Visibility']==0))


Item_Visibility值等于0的数量 879
处理后Item_Visibility值等于0的数量 0
#计算每个商品的销售量
all_data['Item_Sales_Vol']=all_data.Item_Outlet_Sales/all_data.Item_MRP

  • 为了贴近实际销量
  • 销售量将作为模型的预测变量

2、创建一个新变量

利用Item_Identifier创建一个新变量

all_data['Item_Type_Combined']=all_data['Item_Identifier'].apply(lambda x :x[0:2])
#将她们重新命名
all_data['Item_Type_Combined']=all_data['Item_Type_Combined'].map({'FD':'Food',
                                                                  'NC':'Non-Consumable',
                                                                  'DR':'Drinks'})
all_data['Item_Type_Combined'].value_counts()
Food              10201
Non-Consumable     2686
Drinks             1317
Name: Item_Type_Combined, dtype: int64

修改Item_Fat_Content的类别

print ("打印原始类别:",all_data['Item_Fat_Content'].value_counts())
all_data['Item_Fat_Content']=all_data['Item_Fat_Content'].replace({'LF':'Low Fat',
                                                                  'reg':'Regular',
                                                                  'low fat':'Low Fat'})

print ("调整后的类别:",all_data['Item_Fat_Content'].value_counts())

打印原始类别: Low Fat    9185
Regular    5019
Name: Item_Fat_Content, dtype: int64
调整后的类别: Low Fat    9185
Regular    5019
Name: Item_Fat_Content, dtype: int64

3、对Item_Type进一步归类,创建新的商品分类变量

#将非消耗品标记为low_fat中的单独类别:
all_data.loc[all_data['Item_Type_Combined']=='Non-Consumable','Item_Fat_Content']='Non-edible '
all_data.Item_Fat_Content.value_counts()
Low Fat        6499
Regular        5019
Non-edible     2686
Name: Item_Fat_Content, dtype: int64
#商品的类别可以进一步归类
all_data.Item_Type.describe()
count                     14204
unique                       16
top       Fruits and Vegetables
freq                       2013
Name: Item_Type, dtype: object

第5步:确定商店的运营年限

#年份
all_data['Outlet_Years']=2013-all_data['Outlet_Establishment_Year']
all_data['Outlet_Years'].describe()
# #年份:
# 数据['Outlet_Years'] = 2013  - 数据['Outlet_Establishment_Year']
# 数据[ 'Outlet_Years']。描述()
count    14204.000000
mean        15.169319
std          8.371664
min          4.000000
25%          9.000000
50%         14.000000
75%         26.000000
max         28.000000
Name: Outlet_Years, dtype: float64

这显示运营年限为4-28岁的商店

all_data.Item_Fat_Content.value_counts()
Low Fat        6499
Regular        5019
Non-edible     2686
Name: Item_Fat_Content, dtype: int64

在步骤2中,我们看到有一些非消耗品,并且不应为他们指定脂肪含量。所以我们也可以为这种观察创建一个单独的类别

#Mark non-consumables as separate category in low_fat:
#将非消耗品标记为low_fat中的单独类别:
all_data.loc[all_data['Item_Type_Combined']==\
             'Non-Consumable','Item_Fat_Content']='Non-Edible'
all_data['Item_Fat_Content'].value_counts()
Low Fat       6499
Regular       5019
Non-Edible    2686
Name: Item_Fat_Content, dtype: int64

可视化探索各个变量

1、商店层面的4个因素:Outlet_Type,Outlet_Location_Type,Outlet_Years,Outlet_Size

sns.boxplot(x="Outlet_Type", y="Item_Outlet_Sales", data=all_data,palette='Set3',hue='Outlet_Type')

<matplotlib.axes._subplots.AxesSubplot at 0x11cb1fa20>

[图片上传失败…(image-9515d8-1522386628279)]

商店类型因素中,Grocery Store销量最低,type3平均销量最高

sns.boxplot(x="Outlet_Location_Type", y="Item_Outlet_Sales",hue='Outlet_Type', data=all_data,palette='Set3')

<matplotlib.axes._subplots.AxesSubplot at 0x11cd64748>

《大型商场销售预测》 output_54_1.png

商店类型1有较完整的数据,但在地理位置的表现上,销量并无太大的区别

sns.boxplot(x="Outlet_Years", y="Item_Outlet_Sales", hue='Outlet_Type',data=all_data,palette='Set3')

<matplotlib.axes._subplots.AxesSubplot at 0x11cf99588>

《大型商场销售预测》 output_56_1.png

商店类型1有较完整的数据,从成立的年数来看,似乎近10年内建立的商店的销售量表现高一点

sns.boxplot(x="Outlet_Size", y="Item_Outlet_Sales",hue='Outlet_Type', data=all_data,palette='Set3')

<matplotlib.axes._subplots.AxesSubplot at 0x11d253940>

《大型商场销售预测》 output_58_1.png

商店类型1有较完整的数据,从商店大小的区分来看,并无差异

可视化的结果表明:

  • 商店类型因素能够较明显区分每个商店的销量水平
  • 商店的地理位置似乎很难看出区别,但其主要原因在于商店数量较少,难以在这个维度区分开来,不能否定该因素的价值
  • 商店的年份看,在同个类型的商店,似乎越新的商店有着更好的表现,似乎消费者会更喜欢新开张的商店,而不是因为年代久知名度高的原因,与我们前面的假设有点出入。
  • 从开店的趋势看,似乎管理公司更倾向于商品销量表现平稳的类型1商店
  • 从商店的大小看似乎也很难看出商品销量的区别,其原因也在于商店数量较少,该变量还是一个比较关键的,因为商店大小跟开店成本相关,在其他预测问题上表现应该很不错,但在此处,可能表现会一般

2、商品层面的因素:Item_Visibility,Item_Weight,Item_MRP,Item_Attribute,Item_Fat_Content,Item_Type(控制商店类型)

sns.lmplot(x='Item_Visibility',y='Item_Outlet_Sales',data=all_data,hue='Outlet_Type')

<seaborn.axisgrid.FacetGrid at 0x11d253c88>

《大型商场销售预测》 output_62_1.png

商品展示的结果与假设接近,展示越大,平均销量越高

# 看看Item_Outlet_Sales的分布和用log1p处理后的形状
all_data['Item_Outlet_Sales_log']=np.log1p(all_data.Item_Outlet_Sales)
all_data.Item_Outlet_Sales.hist()

<matplotlib.axes._subplots.AxesSubplot at 0x11d89ce10>

《大型商场销售预测》 output_64_1.png
《大型商场销售预测》 output_65_1.png

all_data.Item_Outlet_Sales_log.hist()
<matplotlib.axes._subplots.AxesSubplot at 0x11da2f828>

[图片上传失败…(image-41865-1522386628280)]

步骤6:分类变量进行编码

由于scikit-learn仅接受数值变量,因此我将所有类别的名义变量转换为数字类型.此外,我还希望将Outlet_Identifier作为变量。于是我创建了一个与Outlet_Identifier相同的新变量“Outlet”并对其进行了编码。Outlet_Identifier应保持原样,因为它将在提交文件中被要求。

建模和优化

已经处理好数据。开始制作预测模型,我会通过4个模型来预测,包括线性回归,决策树、随机森林、xgboost

现在开始制作一个基准模型,基准模型是不需要预测模型的模型,
将销售额预测为总体平均销售额

mean_sales=train['Item_Outlet_Sales'].mean()
#定义一个提交的数据框
base1=test[['Item_Identifier','Outlet_Identifier']]
base1['Item_Outlet_Sales']=mean_sales
#到出数据
base1.to_csv('alg_0.csv',index=False)

公开排行榜得分:1773.82513778.

#选择特征
data=all_data[['Item_Outlet_Sales_log','Outlet_Type','Item_Visibility','Outlet_Location_Type',
            'Item_MRP','Item_Type','Outlet_Years','Outlet_Size','Item_Type_Combined']]
#所有定性变量进行One-hot编码
print("one-hot之前",data.shape)
data=pd.get_dummies(data)
print ("one-hot之后",data.shape)
#将Item_Outlet_Sales进行log转化

one-hot之前 (14204, 9)
one-hot之后 (14204, 28)
#分训练和预测数据
train_one=data.loc[data.Item_Outlet_Sales_log.notnull(),:]
test_one=data.loc[data.Item_Outlet_Sales_log.isnull(),:]
train_one['Item_Outlet_Sales_log']=train_one['Item_Outlet_Sales_log'].astype(int)
print (train_one.shape,test_one.shape)
#特征和标签
x=train_one.as_matrix()[:,1:]
y=train_one.as_matrix()[:,0]
(8523, 28) (5681, 28)
test_one_aa=test_one.copy()
test_one_aa.drop(['Item_Outlet_Sales_log'],axis=1,inplace=True)
predictors=[x for x in train_one.columns if x not in 'Item_Outlet_Sales_log']
# #取出所有float64类型的特征
# numeric_cols=test_one_aa.columns[test_one_aa.dtypes=='float64']
# numeric_cols

线性回归模型

from sklearn.linear_model import LinearRegression  
regr = LinearRegression().fit(train_one[predictors],train_one['Item_Outlet_Sales_log'] )  

LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)
pred=regr.predict(test_one_aa)
coef1 = pd.Series(regr.coef_, predictors).sort_values()
coef1.plot(kind='bar', title='Model Coefficients')

#rmse(pred*x_test.Item_MRP,x_test.Item_Outlet_Sales)
print(pred)

《大型商场销售预测》 output_78_1.png

#创建用于上传评分的测试结果 
base1=test[['Item_Identifier','Outlet_Identifier']]
base1['Item_Outlet_Sales']=np.expm1(pred)
#导出数据
base1.to_csv('alg_20.csv',index=False)

公开排行榜得分:1613

随机森林模型

from sklearn.ensemble import RandomForestRegressor
rf = RandomForestRegressor()
rf.fit(train_one[predictors],train_one['Item_Outlet_Sales_log'])
pred=rf.predict(test_one_aa)

coef1 = pd.Series(rf.feature_importances_,predictors).sort_values()
coef1.plot(kind='bar', title='Model Coefficients')

#创建用于上传评分的测试结果 
base1=test[['Item_Identifier','Outlet_Identifier']]
base1['Item_Outlet_Sales']=np.expm1(pred)
#导出数据
base1.to_csv('alg_3.csv',index=False)

《大型商场销售预测》 output_78_1.png

公开排行榜得分:1653

支持向量机模型

#支持向量机Support Vector Machines
from sklearn.svm import SVC, LinearSVC
model = SVC()
model.fit(train_one[predictors],train_one['Item_Outlet_Sales_log'])
red=model.predict(test_one_aa)


print (model.support_)


#创建用于上传评分的测试结果 
base1=test[['Item_Identifier','Outlet_Identifier']]
base1['Item_Outlet_Sales']=np.expm1(pred)
#导出数据
base1.to_csv('alg_4.csv',index=False)
[ 178  255  405 ..., 7930 8039 8201]

公开排行榜得分:1653

决策树回归模型

from sklearn import tree
clf = tree.DecisionTreeRegressor()

clf.fit(train_one[predictors],train_one['Item_Outlet_Sales_log'])
red=clf.predict(test_one_aa)

coef1 = pd.Series(clf.feature_importances_,predictors).sort_values()
coef1.plot(kind='bar', title='Model Coefficients')

#创建用于上传评分的测试结果 
base1=test[['Item_Identifier','Outlet_Identifier']]
base1['Item_Outlet_Sales']=np.expm1(pred)
#导出数据
base1.to_csv('alg_5.csv',index=False)

《大型商场销售预测》 output_88_0.png

公开排行榜得分:1653

from sklearn.cross_validation import train_test_split #2.7python可用

X_train, X_val, y_train, y_val = train_test_split(train_one[predictors], train_one['Item_Outlet_Sales_log'],test_size=0.9, random_state=42)

/Users/zhongyaode/anaconda/lib/python3.6/site-packages/sklearn/cross_validation.py:44: DeprecationWarning: This module was deprecated in version 0.18 in favor of the model_selection module into which all the refactored classes and functions are moved. Also note that the interface of the new CV iterators are different from that of this module. This module will be removed in 0.20.
  "This module will be removed in 0.20.", DeprecationWarning)
X_train, X_test, y_train, y_test = train_test_split(train_one[predictors], train_one['Item_Outlet_Sales_log'],test_size=0.2, random_state=42)
print(X_train.shape, X_test.shape)

(6818, 27) (1705, 27)

xgboost模型

import xgboost  as xgb
import os
import datetime
from datetime import datetime


#模型参数设置
xlf = xgb.XGBRegressor(max_depth=10, 
                        learning_rate=0.1, 
                        n_estimators=10, 
                        silent=True, 
                        objective='reg:linear', 
                        nthread=-1, 
                        gamma=0,
                        min_child_weight=1, 
                        max_delta_step=0, 
                        subsample=0.85, 
                        colsample_bytree=0.7, 
                        colsample_bylevel=1, 
                        reg_alpha=0, 
                        reg_lambda=1, 
                        scale_pos_weight=1, 
                        seed=1440, 
                        missing=None)

xlf.fit(X_train, y_train, eval_metric='rmse', verbose = True, eval_set = [(X_test, y_test)],early_stopping_rounds=100)

xgb_start=datetime.now()
xgb_end=datetime.now()
xgb.plot_importance(xlf)

# 计算 auc 分数、预测
# pre_data = xgboost.DMatrix(X_test, y_test)
# predict=bst.predict(test_one_aa)



predss = xlf.predict(test_one_aa)

#创建用于上传评分的测试结果 
base1=test[['Item_Identifier','Outlet_Identifier']]
base1['Item_Outlet_Sales']=np.expm1(predss)
#导出数据
base1.to_csv('alg_6.csv',index=False)
[0] validation_0-rmse:5.71174
Will train until validation_0-rmse hasn't improved in 100 rounds.
[1] validation_0-rmse:5.14637
[2] validation_0-rmse:4.64377
[3] validation_0-rmse:4.18711
[4] validation_0-rmse:3.7774
[5] validation_0-rmse:3.41
[6] validation_0-rmse:3.07942
[7] validation_0-rmse:2.78631
[8] validation_0-rmse:2.52036
[9] validation_0-rmse:2.28272

《大型商场销售预测》 output_88_0.png

公开排行榜得分:2702
额得分这么低

以上是分析和建模过程,直接用Item_Outlet_Sales作为标签表现不好,可以试试别的方法

参考 一

参考 二

《大型商场销售预测》 来一起学吧

    原文作者:彭健平6点30
    原文地址: https://www.jianshu.com/p/b3f412a2165e
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞