分为以下几个阶段探讨这个问题
- 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>
# #决策树
# #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>
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>
商店类型1有较完整的数据,但在地理位置的表现上,销量并无太大的区别
sns.boxplot(x="Outlet_Years", y="Item_Outlet_Sales", hue='Outlet_Type',data=all_data,palette='Set3')
<matplotlib.axes._subplots.AxesSubplot at 0x11cf99588>
商店类型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>
商店类型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>
商品展示的结果与假设接近,展示越大,平均销量越高
# 看看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>
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)
#创建用于上传评分的测试结果
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)
公开排行榜得分: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)
公开排行榜得分: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
公开排行榜得分:2702
额得分这么低