一展运、提出問題:
1912年4月15日孔厉,號稱“永不沉沒”的泰坦尼克號巨輪,在首航期間谎亩,撞上冰山之后沉沒梢薪。船上的2224名乘客和機組人員蹬铺,只有772人存活了下來,生存率只有32%秉撇,讓人唏噓不已甜攀。
導(dǎo)致大量的沒法存活的重要原因之一秋泄,就是沒有足夠的救生艇,在上船前规阀,露絲就曾向船長提出救生艇不夠的問題恒序。雖然幸存下來有一些運氣因素,但是谁撼,有一些人可能比其他人更有可能生存歧胁,比如婦女,兒童以及上層階級厉碟。
所以喊巍,我們研究的問題是:什么樣的人能夠更容易在泰坦尼克號存活?
二箍鼓、理解數(shù)據(jù):
1.采集數(shù)據(jù)
數(shù)據(jù)來源于kaggle平臺項目:點擊下載Kaggle泰坦尼克號數(shù)據(jù)
2.導(dǎo)入數(shù)據(jù)
import os??#查看工作路徑崭参,首先要導(dǎo)入os工作包。
os.getcwd()??# 查看默認(rèn)工作路徑款咖。
os.chdir("/Users//Desktop/KAGGLE/datas/")??#修改工作路徑為文件存放路徑何暮。
導(dǎo)入處理數(shù)據(jù)包
import numpy as np
import pandas as pd
導(dǎo)入數(shù)據(jù)
訓(xùn)練數(shù)據(jù)集
train = pd.read_csv("./train.csv")
測試數(shù)據(jù)集
test??= pd.read_csv("./test.csv")
print ('訓(xùn)練數(shù)據(jù)集:',train.shape,'測試數(shù)據(jù)集:',test.shape)
合并數(shù)據(jù)集,方便同時對兩個數(shù)據(jù)集進行清洗
full = train.append( test , ignore_index = True )
3.查看數(shù)據(jù)
查看數(shù)據(jù)
full.head()
查看每一列的數(shù)據(jù)類型之剧,和數(shù)據(jù)總數(shù)
full.info()
根據(jù)上面打印的結(jié)果郭卫,我們發(fā)現(xiàn)數(shù)據(jù)總共有1309行砍聊。
其中數(shù)據(jù)類型列:年齡(Age)背稼、船艙號(Cabin)里面有缺失數(shù)據(jù):
年齡(Age)里面數(shù)據(jù)總數(shù)是1046條,缺失了1309-1046=263玻蝌,缺失率263/1309=20%
船票價格(Fare)里面數(shù)據(jù)總數(shù)是1308條蟹肘,缺失了1條數(shù)據(jù)
字符串列:
登船港口(Embarked)里面數(shù)據(jù)總數(shù)是1307,只缺失了2條數(shù)據(jù)俯树,缺失比較少
船艙號(Cabin)里面數(shù)據(jù)總數(shù)是295帘腹,缺失了1309-295=1014,缺失率=1014/1309=77.5%许饿,缺失比較大
? ? 這為我們下一步數(shù)據(jù)清洗指明了方向阳欲,只有知道哪些數(shù)據(jù)缺失數(shù)據(jù),我們才能有針對性的處理陋率。
三球化、數(shù)據(jù)清洗
1.缺失值處理
在前面,理解數(shù)據(jù)階段瓦糟,我們發(fā)現(xiàn)數(shù)據(jù)總共有1309行筒愚。
?其中數(shù)據(jù)類型列:年齡(Age)、船艙號(Cabin)里面有缺失數(shù)據(jù)菩浙。?
?字符串列:登船港口(Embarked)巢掺、船艙號(Cabin)里面有缺失數(shù)據(jù)句伶。
用均值填充年齡以及船票價格的缺失值:
?#年齡(Age)缺失率為20%:
full['Age']=full['Age'].fillna( full['Age'].mean() )
?#船票價格(Fare)缺失1條數(shù)據(jù):
full['Fare'] = full['Fare'].fillna( full['Fare'].mean() )
#登船港口缺失2條數(shù)據(jù),用眾數(shù)填充:
full['Embarked'] = full['Embarked'].fillna( 'S’ )
#船艙號缺失值為77.5%陆淀,用U填充:
full['Cabin'] = full['Cabin'].fillna( 'U’ )
四考余、特征提取
1.數(shù)據(jù)分類:
? 數(shù)值類型:
? 乘客編號(PassengerId),年齡(Age)倔约,船票價格(Fare)秃殉,同代直系親屬人數(shù)(SibSp),不同代直系親屬人數(shù)(Parch)
? 時間序列:無
? 分類數(shù)據(jù):
? 1)有直接類別的
? 乘客性別(Sex):男性male浸剩,女性female
? 登船港口(Embarked):出發(fā)地點S=英國南安普頓Southampton钾军,途徑地點1:C=法國 瑟堡市Cherbourg,出發(fā)地點2:Q=愛爾蘭 昆士敦Queenstown
? 客艙等級(Pclass):1=1等艙绢要,2=2等艙吏恭,3=3等艙
? 2)字符串類型:可能從這里面提取出特征來,也歸到分類數(shù)據(jù)中
? 乘客姓名(Name)
? 客艙號(Cabin)
? 船票編號(Ticket)
2.分類數(shù)據(jù)處理:
2.1?乘客性別(Sex)
將性別的值映射為數(shù)值:
男(male)對應(yīng)數(shù)值1重罪,女(female)對應(yīng)數(shù)值0
sex_mapDict={'male':1,
????????????'female':0}
#map函數(shù):對Series每個數(shù)據(jù)應(yīng)用自定義的函數(shù)計算
full['Sex']=full['Sex'].map(sex_mapDict)
2.2?登船港口(Embarked)
#存放提取后的特征
embarkedDf = pd.DataFrame()
使用get_dummies進行one-hot編碼樱哼,產(chǎn)生虛擬變量(dummy variables),列名前綴是Embarked
embarkedDf = pd.get_dummies( full['Embarked'] , prefix='Embarked' )
embarkedDf.head()
#添加one-hot編碼產(chǎn)生的虛擬變量(dummy variables)到泰坦尼克號數(shù)據(jù)集full剿配,并把登船港口(Embarked)刪掉搅幅。
full = pd.concat([full,embarkedDf],axis=1)
full.drop('Embarked',axis=1,inplace=True)
2.3?客艙等級(Pclass)
客艙等級(Pclass):
1=1等艙,2=2等艙呼胚,3=3等艙
#存放提取后的特征
pclassDf = pd.DataFrame()
#使用get_dummies進行one-hot編碼茄唐,列名前綴是Pclass
pclassDf = pd.get_dummies( full['Pclass'] , prefix='Pclass’ )
#刪掉客艙等級(Pclass)這一列
full.drop('Pclass',axis=1,inplace=True)
字符串類型:
2.4 ?乘客姓名(Name)
定義函數(shù):從姓名中獲取頭銜:
def getTitle(name):
????str1=name.split( ',' )[1] #Mr. Owen Harris
????str2=str1.split( '.' )[0]#Mr
????#strip() 方法用于移除字符串頭尾指定的字符(默認(rèn)為空格)
????str3=str2.strip()
????return str3
#存放提取后的特征
titleDf = pd.DataFrame()
#map函數(shù):對Series每個數(shù)據(jù)應(yīng)用自定義的函數(shù)計算
titleDf['Title'] = full['Name'].map(getTitle)
titleDf.head()
定義以下幾種頭銜類別:
Officer政府官員
Royalty王室(皇室)
Mr已婚男士
Mrs已婚婦女
Miss年輕未婚女子
Master有技能的人/教師
#姓名中頭銜字符串與定義頭銜類別的映射關(guān)系
title_mapDict = {
????????????????????"Capt":???????"Officer"; "Col":? "Officer";?
? ? ? ? ? ? ? ? ? ?"Major": "Officer";? ? ?"Jonkheer":???"Royalty",
????????????????????"Don": "Royalty";? ? ?"Sir" :???????"Royalty",
????????????????????"Dr": "Officer";? ? "Rev": "Officer",
????????????????????"the Countess":"Royalty",
????????????????????"Dona": "Royalty",;? ? "Mme": "Mrs",
? ? ? ? ? ? ? ? ? ?"Mlle": "Miss";? ? ?"Ms":"Mrs",
????????????????????"Mr" :? "Mr";? ? "Mrs" : "Mrs",
????????????????????"Miss" :"Miss";? ? "Master" : "Master",
????????????????????"Lady" : "Royalty"}
#map函數(shù):對Series每個數(shù)據(jù)應(yīng)用自定義的函數(shù)計算
titleDf['Title'] = titleDf['Title'].map(title_mapDict)
#使用get_dummies進行one-hot編碼
titleDf = pd.get_dummies(titleDf['Title'])
titleDf.head()
#添加one-hot編碼產(chǎn)生的虛擬變量(dummy variables)到泰坦尼克號數(shù)據(jù)集full
full = pd.concat([full,titleDf],axis=1)
#刪掉姓名這一列
full.drop('Name',axis=1,inplace=True)
2.5 ?客艙號(Cabin)
#存放客艙號信息
cabinDf = pd.DataFrame()
客場號的類別值是首字母,例如:
C85 類別映射為首字母C
full[ 'Cabin' ] = full[ 'Cabin' ].map( lambda c : c[0] )
##使用get_dummies進行one-hot編碼蝇更,列名前綴是Cabin
cabinDf = pd.get_dummies( full['Cabin'] , prefix = 'Cabin’ )
#添加one-hot編碼產(chǎn)生的虛擬變量(dummy variables)到泰坦尼克號數(shù)據(jù)集full
full = pd.concat([full,cabinDf],axis=1)
#刪掉客艙號這一列
full.drop('Cabin',axis=1,inplace=True)
2.6 ? ?家庭人數(shù)
#存放家庭信息
familyDf = pd.DataFrame()
家庭人數(shù)=同代直系親屬數(shù)(Parch)+不同代直系親屬數(shù)(SibSp)+乘客自己
(因為乘客自己也是家庭成員的一個沪编,所以這里加1)
familyDf[ 'FamilySize' ] = full[ 'Parch' ] + full[ 'SibSp' ] + 1
家庭類別:
小家庭Family_Single:家庭人數(shù)=1
中等家庭Family_Small: 2<=家庭人數(shù)<=4
大家庭Family_Large: 家庭人數(shù)>=5
#if 條件為真的時候返回if前面內(nèi)容,否則返回0
familyDf[ 'Family_Single' ] = familyDf[ 'FamilySize' ].map( lambda s : 1 if s == 1 else 0 )
familyDf[ 'Family_Small' ]??= familyDf[ 'FamilySize' ].map( lambda s : 1 if 2 <= s <= 4 else 0 )
familyDf[ 'Family_Large' ]??= familyDf[ 'FamilySize' ].map( lambda s : 1 if 5 <= s else 0 )
familyDf.head()
#添加one-hot編碼產(chǎn)生的虛擬變量(dummy variables)到泰坦尼克號數(shù)據(jù)集full
full = pd.concat([full,familyDf],axis=1)
full.head()
五年扩、特征選擇
相關(guān)系數(shù)法:計算各個特征的相關(guān)系數(shù)
#相關(guān)性矩陣
corrDf = full.corr()
corrDf
查看各個特征與生成情況(Survived)的相關(guān)系數(shù)蚁廓,
ascending=False表示按降序排列
corrDf['Survived'].sort_values(ascending =False)
我們發(fā)現(xiàn)頭銜Mrs和生存情況有著很強的正線性相關(guān),頭銜Mr和生存情況有負(fù)線性相關(guān)性厨幻。有可能當(dāng)時船上執(zhí)行了相嵌,發(fā)生災(zāi)難時:婦女兒童優(yōu)先。雖然災(zāi)難發(fā)生况脆,很多人還是很好的遵守了這一人性的原則饭宾。
根據(jù)各個特征與生成情況(Survived)的相關(guān)系數(shù)大小,我們選擇了這幾個特征作為模型的輸入:
頭銜(前面所在的數(shù)據(jù)集titleDf)漠另、客艙等級(pclassDf)捏雌、家庭大小(familyDf)笆搓、船票價格(Fare)性湿、船艙號(cabinDf)纬傲、登船港口(embarkedDf)、性別(Sex)
#特征選擇
full_X = pd.concat( [titleDf,#頭銜
?????????????????????pclassDf,#客艙等級
?????????????????????familyDf,#家庭大小
?????????????????????full['Fare'],#船票價格
?????????????????????cabinDf,#船艙號
?????????????????????embarkedDf,#登船港口
?????????????????????full['Sex']#性別
????????????????????] , axis=1 )
full_X.head()
六肤频、模型構(gòu)建
1.建立訓(xùn)練數(shù)據(jù)集和測試數(shù)據(jù)集
用訓(xùn)練數(shù)據(jù)和某個機器學(xué)習(xí)算法得到機器學(xué)習(xí)模型叹括,用測試數(shù)據(jù)評估模型
#原始數(shù)據(jù)集:特征
source_X = full_X.loc[0:sourceRow-1,:]
#原始數(shù)據(jù)集:標(biāo)簽
source_y = full.loc[0:sourceRow-1,'Survived']???
#預(yù)測數(shù)據(jù)集:特征
pred_X = full_X.loc[sourceRow:,:]
source_y
source_X
pred_X
from sklearn.model_selection import train_test_split
#建立模型用的訓(xùn)練數(shù)據(jù)集和測試數(shù)據(jù)集
train_X, test_X, train_y, test_y = train_test_split(source_X ,
????????????????????????????????????????????????????source_y,
????????????????????????????????????????????????????train_size=.80)
#交叉驗證
#cross_validation.cross_val_score(LR, train_X , train_y, cv=10)
#輸出數(shù)據(jù)集大小
print ('原始數(shù)據(jù)集特征:',source_X.shape,
???????'訓(xùn)練數(shù)據(jù)集特征:',train_X.shape ,
??????'測試數(shù)據(jù)集特征:',test_X.shape)
print ('原始數(shù)據(jù)集標(biāo)簽:',source_y.shape,
???????'訓(xùn)練數(shù)據(jù)集標(biāo)簽:',train_y.shape ,
??????'測試數(shù)據(jù)集標(biāo)簽:',test_y.shape)
2.模型訓(xùn)練評估
邏輯回歸:
#第1步:導(dǎo)入算法
from sklearn.linear_model import LogisticRegression
#第2步:創(chuàng)建模型:邏輯回歸(logisic regression)
model = LogisticRegression()
#第3步:訓(xùn)練模型
model.fit( train_X , train_y )
# 分類問題,score得到的是模型的準(zhǔn)確率
model.score(test_X , test_y )
結(jié)果:0.83240223463687146
神經(jīng)網(wǎng)絡(luò):
from sklearn.neural_network import MLPClassifier
mlp = MLPClassifier(hidden_layer_sizes=(30,20,20,20), max_iter=1000)
# 計算交叉驗證的準(zhǔn)確率
mlp.fit(train_X,train_y)
mlp.score(test_X , test_y)
結(jié)果:0.83798882681564246
KNN:
from sklearn import neighbors
knn = neighbors.KNeighborsClassifier(n_neighbors=8)
scores = cross_validation.cross_val_score(knn, train_X , train_y, cv=5)
print(scores.mean())
結(jié)果:0.762612035851
決策樹:
from sklearn import tree
dtree = tree.DecisionTreeClassifier(max_depth=3, min_samples_split=4)
scores = cross_validation.cross_val_score(dtree, train_X , train_y, cv=3)
print(scores.mean())
結(jié)果:0.815921283768
隨機森林:
from sklearn.ensemble import RandomForestClassifier
RF = RandomForestClassifier(n_estimators=100, min_samples_split=4)
scores = cross_validation.cross_val_score(RF, train_X , train_y, cv=3)
print(scores.mean())
結(jié)果:0.81598062954
Bagging:
from sklearn.ensemble import BaggingClassifier
bagging_clf = BaggingClassifier(RF,n_estimators=20)
scores = cross_validation.cross_val_score(bagging_clf,train_X , train_y, cv=3)
print(scores.mean())
結(jié)果:0.831410530314
Adaboost:
from sklearn.ensemble import AdaBoostClassifier
adaboost = AdaBoostClassifier(bagging_clf, n_estimators=10)
scores = cross_validation.cross_val_score(adaboost, train_X , train_y, cv=3)
print(scores.mean())
結(jié)果:0.827196980487
Stacking:
from sklearn.ensemble import VotingClassifier
sclf = VotingClassifier([('LR',LR),('mlp',mlp),('bagging_clf',bagging_clf),('knn',knn),('dtree',dtree)])
scores = cross_validation.cross_val_score(sclf, train_X , train_y, cv=15)
print(scores.mean())
結(jié)果:0.829916895836
結(jié)果可以看出:神經(jīng)網(wǎng)絡(luò)預(yù)測結(jié)果最好宵荒。
七汁雷、方案實施
1.結(jié)果上傳
使用預(yù)測數(shù)據(jù)集到底預(yù)測結(jié)果,并保存到csv文件中报咳,上傳到Kaggle中侠讯,就可以看到排名。
#使用機器學(xué)習(xí)模型暑刃,對預(yù)測數(shù)據(jù)集中的生存情況進行預(yù)測
pred_Y = model.predict(pred_X)
'''
生成的預(yù)測值是浮點數(shù)(0.0,1,0)
但是Kaggle要求提交的結(jié)果是整型(0,1)
所以要對數(shù)據(jù)類型進行轉(zhuǎn)換
'''
pred_Y=pred_Y.astype(int)
#乘客id
passenger_id = full.loc[sourceRow:,'PassengerId']
#數(shù)據(jù)框:乘客id厢漩,預(yù)測生存情況的值
predDf = pd.DataFrame(
????{ 'PassengerId': passenger_id ,
?????'Survived': pred_Y } )
predDf.shape
predDf.head()
#保存結(jié)果
predDf.to_csv( 'titanic_pred.csv' , index = False )
2.結(jié)論
2.1 ? 從性別來看:女性比男性(負(fù)線性相關(guān))更容易存活,可能是在災(zāi)難來臨的時候岩臣,整個群體遵循了“女人小孩先走的”的人性原則溜嗜。
2.2 ?從倉位來看:高等級的倉位,生存率更高架谎,可能是因為高等級的倉位處于上層甲板炸宵,逃生比低級倉位更容易
2.3 ?從頭銜可以看出社會等級的差異,不同的社會等級往往生存率也有所不同谷扣。