这一次树根树根带着大家怎么把之前学到的东西都联系起来,做一次真正的数据挖掘。大家不知道有没有了解过谷歌旗下的kaggle数据挖掘大赛,假如大家觉得自己学来的东西想动手实践一下,那么像kaggle、天池这些的数据挖掘大赛将会是我们实践的一个很好的平台。
而泰坦尼克号数据集,则是kaggle上机器学习入门的一个非常好的数据集。这一次,树根就带着大家怎么用这个数据集作为切入点,做一次真正的数据挖掘。
Titanic数据集下载地址:
https://www.kaggle.com/c/titanic/data
树根这里先剧透一下我们接下来要做什么。
1.第一步我们先要对数据进行可视化,进行简单的数据探索;
2.然后对数据进行清洗,比如对缺失值进行填补、进行特征二值化,编制哑变量等等。值得一提的是,这里由于年龄缺失值较多,树根对年龄的缺失值采用随机森林模型预测填补的方法;
3.然后就是进行我们的特征工程,这一步是最重要的,再用xgboost进行特征选择;
4.最后就是构建我们的模型,在这一篇文章,树根会用到kaggle比赛最常见的构建模型的方法——模型融合(Ensemble)中的stack,base models分别选择随机森林、Extratrees、梯度提升树,最后构建xgboost模型来预测生还率;
5.进行参数调整把模型性能提升到最优(这里树根因为时间关系使用了网格搜索进行调参)。
由于篇幅较长代码较多,在上篇中树根先带大家做数据可视化、数据清洗以及简单的特征工程。
那么,现在让我们开始吧!
趣
童
Titanic数据集探索
Data
Mining
树根就废话少说了,一开始先导入我们要用到的工具库:
(可左右滑动代码窗口)
#导入相应的工具库
import pandas as pd
import xgboost as xgb
from xgboost.sklearn import XGBClassifier
from sklearn.model_selection import GridSearchCV,cross_val_score
from sklearn import metrics
import matplotlib.pylab as plt
from sklearn.ensemble import RandomForestClassifier, ExtraTreesClassifier, GradientBoostingClassifier,RandomForestRegressor
from sklearn.preprocessing import LabelEncoder
from sklearn.cross_validation import train_test_split
from sklearn.cross_validation import StratifiedKFold
import numpy as np
from sklearn.metrics import roc_auc_score
from sklearn.datasets.samples_generator import make_blobs
from matplotlib import pyplot
是不是看的眼花缭乱?确实要用到的库还是挺多的,因为单单是模型融合就要用到六个不同的模型。然后我们就可以导入泰坦尼克号数据集了,顺便用head()函数看一下数据集长的怎么样:
(可左右滑动代码窗口)
train = pd.read_excel('E:/数据集/Titanic/train.xlsx')
test = pd.read_excel('E:/数据集/Titanic/test.xlsx')
y_predict = pd.read_excel('E:/数据集/Titanic/y_test.xlsx')
y_predict = y_predict.iloc[:,1]
train.head(10)
一共包含了PassengerId 、Survived、 Pclass 、Name、 Sex、 Age、 SibSp 、Parch、 Ticket 、Fare、 Cabin、 Embarked,共12列。同样我们可以看一下测试集:
与训练集相比,缺少了 “Survived”这一特征,这正是我们需要预测的。
我们可以用上一次树根教大家的pyecharts进行数据的可视化。(忘记了或者没看?快戳开《还在用Excel做数据图表?教你如何用pyecharts做超高逼格的数据图表!》):
(可左右滑动代码窗口)
from pyecharts import Bar
Pclass_Survived_0 = train.Pclass[train.Survived == 0].value_counts()
Pclass_Survived_1 = train.Pclass[train.Survived == 1].value_counts()
Pclass_Survived = pd.DataFrame({'生还':Pclass_Survived_1, '死亡':Pclass_Survived_0})
attr = ["等级1","等级2","等级3"]
v1 = Pclass_Survived["死亡"]
v2 = Pclass_Survived["生还"]
bar_1 = Bar("各舱位等级的获救情况")
bar_1.add("死亡人数", attr, v1, is_stack=True)
bar_1.add("生还人数", attr, v2, is_label_show=True,is_stack=True)
bar_1
我们可以看出,各个等级的船舱还是有区别的,一等舱中的男子凭借自身的社会地位强行混入了救生艇。如白星航运公司主席伊斯梅(他否决了配备48艘救生艇的想法,认为少点也没关系)则抛下他的乘客、他的船员、他的船,在最后一刻跳进可折叠式救生艇C,而三等舱的人,死亡率是最高的。
我们再来把登录港口的情况可视化看一下:
(可左右滑动代码窗口)
Embarked_Survived_0 = train.Embarked[train.Survived == 0].value_counts()
Embarked_Survived_1 = train.Embarked[train.Survived == 1].value_counts()
Embarked_Survived = pd.DataFrame({'生还':Embarked_Survived_1, '死亡':Embarked_Survived_0})
attr = ["港口S","港口C","港口Q"]
v1 = Embarked_Survived["死亡"]
v2 = Embarked_Survived["生还"]
bar_2 = Bar("各登录港口乘客的获救情况")
bar_2.add("死亡人数", attr, v1, is_stack=True)
bar_2.add("生还人数", attr, v2, is_label_show=True,is_stack=True)
bar_2
泰坦尼克号从英国的南安普顿港出发,途径法国瑟堡和爱尔兰昆士敦,一部分在瑟堡或昆士敦下船的人逃过了一劫。
我们再来看一下不同性别的乘客的获救情况:
(可左右滑动代码窗口)
Sex_Survived_0 = train.Sex[train.Survived == 0].value_counts()
Sex_Survived_1 = train.Sex[train.Survived == 1].value_counts()
Sex_Survived = pd.DataFrame({'生还':Sex_Survived_1, '死亡':Sex_Survived_0})
attr = ["女性","男性"]
v1 = Sex_Survived["死亡"]
v2 = Sex_Survived["生还"]
bar_3 = Bar("各性别乘客的获救情况")
bar_3.add("死亡人数", attr, v1, is_stack=True)
bar_3.add("生还人数", attr, v2, is_label_show=True,is_stack=True)
bar_3
明显可以看出男性的死亡率比女性的高出许多,泰坦尼克号逃生总体符合女士优先。
数据的
预处理
Data
Mining
对数据可视化的过程中我们大体知道泰坦尼克号死亡率与什么有很大的关系,比如性别,登录港口以及舱位等级等等。但是由于数据集存在“肮脏数据”,因此接下来我们要对数据进行数据清洗。
首先我们要划分数据集,要注意的是,我们要把乘客的id去掉,因为这并不能帮助我们预测生还率:
(可左右滑动代码窗口)
y = train.iloc[:,1]
x_train = train.iloc[:,2:]
x_test = test.iloc[:,1:]
我们把性别属性二值化,女性是”0“,男性是”1“:
(可左右滑动代码窗口)
s_train=x_train['Sex'].replace('female',0)
s_train=s_train.replace('male',1)
x_train['Sex']=s_train
在这里树根其实想把表示属性的特征统统转化成哑变量,但是这样就会造成很多的特征,树根先把这一步搁置一下,好让大家看清楚。
接下来把登录港口属性三值化,港口C为”0“,港口Q为”1“,港口S为”2“:
(可左右滑动代码窗口)
s_train=x_train['Embarked'].replace('C',0)
s_train=s_train.replace('Q',1)
s_train=s_train.replace('S',2)
x_train['Embarked']=s_train
然后用众数(mode)把该特征的缺失值填补:
(可左右滑动代码窗口)
x_train.Embarked[x_train.Embarked.isnull()] = x_train.Embarked.dropna().mode().values
在数据的Name项中包含了对该乘客的称呼,如Mr、Miss、Mrs等,这些信息包含了乘客的年龄、性别、也有可能包含社会地位,因此也是有用的信息,树根对该特征的称呼提取出来,然后转化成数值:
(可左右滑动代码窗口)
x_train['Title'] = x_train['Name'].str.extract('.+,(.+)').str.extract( '^(.+?)\.').str.strip()
title_Dict = {}
title_Dict.update(dict.fromkeys(['Capt', 'Col', 'Major', 'Dr', 'Rev'], 'Officer'))
title_Dict.update(dict.fromkeys(['Jonkheer', 'Don', 'Sir', 'the Countess', 'Dona', 'Lady'], 'Royalty'))
title_Dict.update(dict.fromkeys(['Mme', 'Ms', 'Mrs'], 'Mrs'))
title_Dict.update(dict.fromkeys(['Mlle', 'Miss'], 'Miss'))
title_Dict.update(dict.fromkeys(['Mr'], 'Mr'))
title_Dict.update(dict.fromkeys(['Master'], 'Master'))
x_train['Title'] = x_train['Title'].map(title_Dict)
s_train=x_train['Title'].replace('Officer',0)
s_train=s_train.replace('Mr',1)
s_train=s_train.replace('Miss',2)
s_train=s_train.replace('Mrs',3)
s_train=s_train.replace('Master',4)
s_train=s_train.replace('Royalty',5)
x_train['Title']=s_train
#把原名字属性删除
x_train = x_train.drop("Name",axis = 1)
把船票的费划分为四个等级,当然区间可以自己设置:
(可左右滑动代码窗口)
def fare_category(fare):
if fare
return 0
elif fare
return 1
elif fare
return 2
elif fare
return 3
else:
return 4
x_train['Fare_Category'] = x_train['Fare'].map(fare_category)
#再把原来的fare特征删除
x_train = x_train.drop("Fare",axis = 1)
“Parch” 和 ”SibSp“,父母孩子和兄弟姐妹这两组数据都能显著影响到Survived,但是影响方式不完全相同,所以将这两项合并成FamilySize组的同时保留这两项:
(可左右滑动代码窗口)
def family_size_category(Family_Size):
if Family_Size
return 0
elif Family_Size
return 1
else:
return 2
x_train['Family_Size_Category'] = x_train['Family_Size'].map(family_size_category)
x_train = x_train.drop("Family_Size",axis = 1)
x_train = x_train.drop("Cabin",axis = 1)
x_train.head()
由于Cabin属性缺失值太多,因此这里树根把该属性删除。
到这里,我们可以用head函数查看一下我们的数据集清洗成怎么样:
那么由于年龄Age属性的缺失值较多,无论采用众数还是平均值来填补都不太适合,在这里,树根结合其他的特征,采用机器学习的方法对该缺失值进行预测,并把预测值用于填补。模型采用的是随机森林:
(可左右滑动代码窗口)
missing_age_df = pd.DataFrame(x_train[['Age', 'Parch', 'Sex', 'SibSp', 'Family_Size_Category',
'Title', 'Fare_Category', 'Pclass', 'Embarked']])
missing_age_train = missing_age_df[missing_age_df['Age'].notnull()]
missing_age_test = missing_age_df[missing_age_df['Age'].isnull()]
train_age = missing_age_train.iloc[:,1:]
trainlabel_age = missing_age_train.iloc[:,0]
test_age = missing_age_test.iloc[:,1:]
#这里使用随机森林训练数据
rfr = RandomForestRegressor(n_estimators=1000,n_jobs=-1)
rfr.fit(train_age,trainlabel_age)
predictAges = rfr.predict(test_age)
x_train.loc[(x_train.Age.isnull()),'Age'] = predictAges
x_train.head(10)
到这里,数据清洗和简单的特征工程就结束了,我们来看看一下数据集变成怎么样:
在这里,树根没有对”ticket“属性进行特征工程并进行保留,因为”ticket“包含着船舱号和船票号,这三个因素都可能会影响乘客在船中的位置从而影响逃生顺序,但是因为这因素与生存之间看不出明显规律,所以在后期模型融合时,将这些因素交给模型来决定其重要性。
同样,我们对测试集进行类似的操作:
(可左右滑动代码窗口)
s_test=x_test['Sex'].replace('female',0)
s_test=s_test.replace('male',1)
x_test['Sex']=s_test
#设置性别的哑变量
#sex_dummies_df = pd.get_dummies(x_test['Sex'], prefix=x_test[['Sex']].columns[0])
#x_test = pd.concat([x_test, sex_dummies_df], axis=1)
s_test=x_test['Embarked'].replace('C',0)
s_test=s_test.replace('Q',1)
s_test=s_test.replace('S',2)
x_test['Embarked']=s_test
#设置登录港口的哑变量
#emb_dummies_df = pd.get_dummies(x_test['Embarked'],prefix=x_test[['Embarked']].columns[0])
#x_test = pd.concat([x_test, emb_dummies_df], axis=1)
#把Name的特征根据称呼提取出来
x_test['Title'] = x_test['Name'].str.extract('.+,(.+)').str.extract( '^(.+?)\.').str.strip()
title_Dict = {}
title_Dict.update(dict.fromkeys(['Capt', 'Col', 'Major', 'Dr', 'Rev'], 'Officer'))
title_Dict.update(dict.fromkeys(['Jonkheer', 'Don', 'Sir', 'the Countess', 'Dona', 'Lady'], 'Royalty'))
title_Dict.update(dict.fromkeys(['Mme', 'Ms', 'Mrs'], 'Mrs'))
title_Dict.update(dict.fromkeys(['Mlle', 'Miss'], 'Miss'))
title_Dict.update(dict.fromkeys(['Mr'], 'Mr'))
title_Dict.update(dict.fromkeys(['Master'], 'Master'))
x_test['Title'] = x_test['Title'].map(title_Dict)
s_test=x_test['Title'].replace('Officer',0)
s_test=s_test.replace('Mr',1)
s_test=s_test.replace('Miss',2)
s_test=s_test.replace('Mrs',3)
s_test=s_test.replace('Master',4)
s_test=s_test.replace('Royalty',5)
x_test['Title']=s_test
#设置头衔的哑变量
#title_dummies_df = pd.get_dummies(x_test['Title'], prefix=x_test[['Title']].columns[0])
#x_test = pd.concat([x_test, title_dummies_df], axis=1)
#把名字属性删除
x_test = x_test.drop("Name",axis = 1)
#把费用划分等级
def fare_category(fare):
if fare
return 0
elif fare
return 1
elif fare
return 2
elif fare
return 3
else:
return 4
x_test['Fare_Category'] = x_test['Fare'].map(fare_category)
#再把原来的fare特征删除
x_test = x_test.drop("Fare",axis = 1)
#Parch and SibSp这两组数据都能显著影响到Survived,但是影响方式不完全相同,所以将这两项合并成FamilySize组的同时保留这两项。
x_test['Family_Size'] = x_test['Parch'] + x_test['SibSp'] + 1
def family_size_category(Family_Size):
if Family_Size
return 0
elif Family_Size
return 1
else:
return 2
x_test['Family_Size_Category'] = x_test['Family_Size'].map(family_size_category)
x_test = x_test.drop("Family_Size",axis = 1)
x_test = x_test.drop("Cabin",axis = 1)
x_test.head(10)
同样,用随机森林对测试集的Age属性的缺失值进行预测:
(可左右滑动代码窗口)
missing_age_df = pd.DataFrame(x_test[['Age', 'Parch', 'Sex', 'SibSp', 'Family_Size_Category',
'Title', 'Fare_Category', 'Pclass', 'Embarked']])
missing_age_train = missing_age_df[missing_age_df['Age'].notnull()]
missing_age_test = missing_age_df[missing_age_df['Age'].isnull()]
train_age = missing_age_train.iloc[:,1:]
trainlabel_age = missing_age_train.iloc[:,0]
test_age = missing_age_test.iloc[:,1:]
#这里使用随机森林训练数据
rfr = RandomForestRegressor(n_estimators=1000,n_jobs=-1)
rfr.fit(train_age,trainlabel_age)
predictAges = rfr.predict(test_age)
x_test.loc[(x_test.Age.isnull()),'Age'] = predictAges
x_test.head(10)
到这里,数据清洗和特征工程就结束了,当然,大家有什么好的想法,比如对于ticket变量怎么处理,或者在Age属性的缺失值的填补还有什么更好的建议或者模型等等,都可以给树根留言,树根必定虚心受教~。
在下一篇中,树根就会进行模型的构建和参数调优,完成我们的泰坦尼克号数据集的挖掘实践~
~祝愿天下的大朋友、小朋友天天快乐~
机器学习人工智能
--欢迎大家后台调戏聊天机器人--
树根精选原创TOP
以下这些公众号都是不错的喔,也可以逛逛看看~
领取专属 10元无门槛券
私享最新 技术干货