1. 程式人生 > >Kaggle:Getting Started of Titanic

Kaggle:Getting Started of Titanic

一、概要

泰坦尼克號倖存預測是Kaggle上參與人數最多的的比賽之一,要求參賽人員預測乘客是否能夠倖存,是一個典型的二分類問題。

二、資料簡介

官網提供訓練資料集train.csv和測試資料集test.csv和一個提交樣例資料集,資料中的各個欄位如下:

PassengerId: 乘客的ID
Survived:1代表倖存,0代表遇難
Pclass:票類別-社會地位, 1代表Upper,2代表Middle,3代表Lower
Name:姓名
Sex:性別
Age:年齡
SibSp:兄弟姐妹及配偶的個數
Parch:父母或子女的個數
Ticket:船票號
Fare:船票價格
Cabin:艙位
Embarked:登船口岸

三、資料探索

1、載入資料

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
path = '/titanic/data/{}'
train = pd.read_csv(path.format('train.csv'))
test = pd.read_csv(path.format('test.csv'))

2、概覽

train.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
PassengerId    891 non-null int64
Survived       891 non-null int64
Pclass         891 non-null int64
Name           891 non-null object
Sex            891 non-null object
Age            714 non-null float64
SibSp          891 non-null int64
Parch          891 non-null int64
Ticket         891 non-null object
Fare           891 non-null float64
Cabin          204 non-null object
Embarked       889 non-null object
dtypes: float64(2), int64(5), object(5)

資料中有大量的數值型資料,這部分可以考慮直接使用;其他非數值型資料要探索一下如何分類;同時裡面存在一定量的缺失值,這部分要注意填充或者丟棄。

3、分類樣本

train['Survived'].value_counts()

0    549
1    342

樣本量很小,同時存在不平衡的問題,可能需要考慮調整。

4、特徵權重

train_corr = train.drop('PassengerId', axis=1).corr()
train_corr

這裡寫圖片描述
計算數值型欄位的協方差,可以看到Pclass與Fare和生存的相關性相對比較明顯。

5、特徵探索

(1)Pclass

隨著船票等級的降低,人數逐漸增加,同時生存率也大大下降,相關性很明顯,後續模型需要考慮這個特徵。
這裡寫圖片描述

(2)Sex

婦女小孩先走,女性明顯有更高的倖存率。

train.groupby('Sex')['Survived'].mean()

Sex
female    0.742038
male      0.188908
Name: Survived, dtype: float64

(3)Age

Age欄位缺失數量較多,先去除缺失資料,再整體觀察
這裡寫圖片描述
根據年齡段的不同,生存出現明顯左右高,中間低的現象,對年齡進行分組再對比

age_not_miss['Age'] = pd.cut(age_not_miss['Age'], bins=[0, 18, 30, 45, 100], labels=[1,2,3,4])
age_not_miss.groupby('Age')['Survived'].mean()

Age
1    0.503597
2    0.355556
3    0.425743
4    0.368932

未成年人明顯具有更高的生存率。

(4)Sibsp + Parch

前面協方差表看到Sibsp和Parch與生存率之間存在一定的關係,但不是特別明顯,這裡可以考慮將這兩個特徵組合成一個新特徵Family再進行觀察

train['F_size'] = train['SibSp'] + train['Parch'] + 1
train.groupby('F_size')['Survived'].mean()

F_size
1     0.303538
2     0.552795
3     0.578431
4     0.724138
5     0.200000
6     0.136364
7     0.333333
8     0.000000
11    0.000000
Name: Survived, dtype: float64

可以看到家庭人數在2-4人的情況下,具有較高的生存率,而單獨一個與家庭人數大於等於5人的生存率明顯降低

(5)Ticket

船票出現重複的數量比較少,這裡可以考慮把船票重複與否派生出另一個特徵,因為相同的船票代表可能為相互認識的人,從而在逃生的時候更可能出現聚堆的情況。

(6)Fare

從協方差表能看到Fare與生存率有相對明顯的關係,因為船票價格眾多,先分組
這裡寫圖片描述
可以看出,很明顯的遞增關係。

(7)Cabin

艙位缺失數量非常多,可能無法提取出足夠的資訊,可以考慮以艙位缺失與否及艙位字首作為特徵。

(8)Embarked

登船港口有一個缺失值,可以考慮用眾數填充或不做處理,C港上船的乘客明顯具有更高的生存率,同一個港口上船更可能分佈在船中的同一片區域,從而影響生存率。

train.groupby(['Embarked'])['Survived'].mean()

Embarked
C    0.553571
Q    0.389610
S    0.336957
Name: Survived, dtype: float64

(9)Name

Name中包含了對乘客的稱呼、性別及可能的社會地位等資訊,這裡需要先對資訊進行抽取,再結合領域知識進行特徵構建,這個放到後邊再列出來。

四、特徵構建

構建特徵時,先把訓練集和測試集合併到一起

    test['Survived'] = 0
    train_test = train.append(test)

1、船票等級

船票等級只需要簡單的把資料分列即可,構造one-hot向量

train_test = pd.get_dummies(train_test, columns=['Pclass'], prefix='P')

2、名稱

對於名稱,先用正則表示式提取出其中的稱呼

train_test['Name1'] = train_test['Name'].str.extract('.*?,(.*?)\.').str.strip()

對於稱呼進行分類,最終構造出了四類特徵

train_test['Name1'].replace(['Master'], 'Master' , inplace = True)
train_test['Name1'].replace(['Jonkheer', 'Don', 'Sir', 'the Countess', 'Dona', 'Lady', 'Capt', 'Col', 'Major', 'Dr', 'Rev'], 'Royalty' , inplace = True)
train_test['Name1'].replace(['Mme', 'Ms', 'Mrs', 'Mlle', 'Miss'], 'Mrs' , inplace = True)
train_test['Name1'].replace(['Mr'], 'Mr' , inplace = True)

看看這幾類特徵與生存率的相關性如何
這裡寫圖片描述
很明顯,分組為男士的生存率是最低的,而女士則有佔有最高的生存率,Mater組代表了年長同時擁有更高知識水平的人群也獲得了極高的存活率,其他剩下的數量較少,但從稱號上基本代表了擁有相對高社會身份的人群,如:貴族等。

3、性別

同1,直接分組即可

train_test = pd.get_dummies(train_test, columns=['Sex'], prefix='S')

4、家庭

家庭按照人口數量,分為4個組別

train_test['F_size'] = train_test['SibSp'] + train_test['Parch'] + 1
train_test['F_Single'] = train_test['F_size'].map(lambda s: 1 if s == 1 else 0)
train_test['F_Small'] = train_test['F_size'].map(lambda s: 1 if 2<= s <= 3  else 0)
train_test['F_Med'] = train_test['F_size'].map(lambda s: 1 if s == 4 else 0)
train_test['F_Large'] = train_test['F_size'].map(lambda s: 1 if s >= 5 else 0)

5、船票

按照是否共享船票,提取特徵

tpc = train_test['Ticket'].value_counts().reset_index()
tpc.columns = ['Ticket', 'Ticket_sum']
train_test = pd.merge(train_test, tpc, how='left', on='Ticket')
train_test.loc[train_test['Ticket_sum'] == 1, 'T_share'] = 0
train_test.loc[train_test['Ticket_sum'] != 1, 'T_share'] = 1

6、船票價格

船票價格可能和船票等級及登船港口相關,根據這兩個特徵,找到對應的均值,填充缺失值
這裡寫圖片描述
根據Embarked為S,填充之後分組

train_test['Fare'].fillna(14.644083, inplace=True)
train_test['Fare_bin'] = pd.cut(train_test['Fare'], 3, labels=[1,2,3])

7、船艙

根據是否缺失及船艙首字母構造特徵

train_test['Cabin'] = train_test['Cabin'].apply(lambda x: str(x)[0] if pd.notnull(x) else x)
train_test.loc[train_test['Cabin'].isnull(), 'Cabin_nan'] = 1
train_test.loc[train_test['Cabin'].notnull(), 'Cabin_nan'] = 0
train_test = pd.get_dummies(train_test, columns=['Cabin'])

8、港口

直接用眾數S填充缺失值,分組

train_test['Embarked'].fillna('S')
train_test = pd.get_dummies(train_test, columns=['Embarked'], prefix='E')

9、年齡

先提取年齡是否缺失特,這很有可能與是否生存有關係

train_test.loc[train_test['Age'].isnull(), 'Age_nan'] = 1
train_test.loc[train_test['Age'].notnull(), 'Age_nan'] = 0

後面考慮建立模型填充缺失的年齡資料,剔除可能與年齡無關或者冗餘的欄位

miss_age = train_test.drop(['PassengerId', 'Name', 'Ticket', 'Fare', 'Survived'], axis=1)
miss_age_train = miss_age[miss_age['Age'].notnull()]
miss_age_test = miss_age[miss_age['Age'].isnull()]
miss_age_train_x = miss_age_train.drop(['Age'], axis=1)
miss_age_train_y = miss_age_train['Age']
miss_age_test_x = miss_age_test.drop(['Age'], axis=1)

特徵都是one-hot向量,先標準化處理一下

from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
ss.fit(miss_age_train_x)
miss_age_train_x_ss = ss.transform(miss_age_train_x)
miss_age_test_x_ss = ss.transform(miss_age_test_x)

用貝葉斯模型進行進行預測

from sklearn import linear_model
model = linear_model.BayesianRidge()
model.fit(miss_age_train_x_ss, miss_age_train_y)
train_test.loc[train_test['Age'].isnull(), 'Age'] = model.predict(miss_age_test_x_ss)

最後按年齡段進行分組

train_test['Age'] = pd.cut(train_test['Age'], bins=[0, 18, 30, 45, 100], labels=[1, 2, 3, 4])
train_test = pd.get_dummies(train_test, columns=['Age'], prefix='A')

10、去除冗餘特徵

feature_columns = ['PassengerId', 'Name', 'Ticket', 'Fare', 'SibSp', 'Parch']
train_test = train_test.drop(feature_columns, axis=1)

最終提取出來的特徵如下所示:
這裡寫圖片描述

11、特徵評價

畫出特徵的協方差圖
這裡寫圖片描述
從相關圖上看,我們選取的大部分特徵彼此之間都沒有太大的相關性,這對於建立模型來說是一個好訊息,我們希望每一個特徵彼此之間無關,能夠提供不同的層面的資訊,從而更加充分的表達出資料的整體資訊。

五、模型構建

1、資料標準化

和上文提到的預測年齡一樣,先分割資料集和標準化資料

train_data = train_test[:891]
test_data = train_test[891:]
train_data_x = train_data.drop(['Survived'], axis=1)
train_data_y = train_data['Survived']
test_data_x = test_data.drop(['Survived'], axis=1)
ss1 = StandardScaler()
ss1.fit(train_data_x)
train_data_x_ss = ss1.transform(train_data_x)
test_data_x_ss = ss1.transform(test_data_x)

2、普通模型

模型調參

from sklearn.model_selection import GridSearchCV
params = {name: value}
cv = GridSearchCV(estimator=cls), param_grid=params, scoring='roc_auc', cv=5)
cv.fit(train_data_x_ss, train_data_y)
cv.best_params_
(1)RandomForestClassifier
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier(n_estimators=220, min_samples_leaf=3, max_depth=5, min_samples_split=9)
rf.score(train_data_x_ss, train_data_y)

(2)LogisticRegression

lr = LogisticRegression(class_weight='balanced', C=0.01, max_iter=100)
lr.fit(train_data_x_ss, train_data_y)
lr.score(train_data_x_ss, train_data_y)

(3)SVC

from sklearn import svm
svc = svm.SVC(C=10, max_iter=350, probability=True)
svc.fit(train_data_x_ss, train_data_y)
svc.score(train_data_x_ss, train_data_y)

(4)GradientBoostingClassifier

from sklearn.ensemble import GradientBoostingClassifier
gbdt = GradientBoostingClassifier(learning_rate=0.5, n_estimators=120)
gbdt.fit(train_data_x_ss, train_data_y)
gbdt.score(train_data_x_ss, train_data_y)

(5)XGBClassifier

import xgboost as xgb
xg = xgb.XGBClassifier(learning_rate=0.8, n_estimators=100)
xg.fit(train_data_x_ss, train_data_y)
xg.score(train_data_x_ss, train_data_y)

(6)ExtraTreesClassifier

from sklearn.ensemble import ExtraTreesClassifier
et = ExtraTreesClassifier(n_estimators=200)
et.fit(train_data_x_ss, train_data_y)
et.score(train_data_x_ss, train_data_y)

3、融合模型

(1)VotingClassifier

from sklearn.ensemble import VotingClassifier
rf = RandomForestClassifier(n_estimators=220, min_samples_leaf=3, max_depth=5, oob_score=True)
lr = LogisticRegression(class_weight='balanced', C=0.01, max_iter=100)
svc = svm.SVC(C=10, max_iter=350, probability=True)
gbdt = GradientBoostingClassifier(learning_rate=0.5, n_estimators=120)
xg = xgb.XGBClassifier(learning_rate=0.8, n_estimators=100)
et = ExtraTreesClassifier(n_estimators=200)

vot = VotingClassifier(estimators=[('rf', rf), ('lr', lr), ('svc', svc), ('gbdt', gbdt), ('xg', xg), ('et', et)], voting='hard')
vot.fit(train_data_x_ss, train_data_y)
vot.score(train_data_x_ss, train_data_y)

(2)Stacking

兩層模型,先處理資料

from sklearn.cross_validation import StratifiedKFold
clfs = [rf, lr, svc, gbdt, xg, et]
X = np.array(train_data_x_ss)
Y = np.array(train_data_y)
X_test = np.array(train_data_x_ss)
Y_test = np.array(train_data_y)

blend作為第二層模型輸入

train_blend = np.zeros((X.shape[0], len(clfs)))
test_blend = np.zeros((X_test.shape[0], len(clfs)))

K-flod

skf = list(StratifiedKFold(Y, 5))

依次訓練單個模型,再使用模型的預測值構建第二層模型的輸入

for i, clf in enumerate(clfs):
    test_blend_i = np.zeros((test_blend.shape[0], len(skf)))
    for j, (train, test) in enumerate(skf):
        clf.fit(X[train], Y[train])

        # 每一個模型的預測值,填充到blend的對應位置,k-flod的測試剛好覆蓋
        # 全部的訓練集,填充blend對應的一列
        train_blend[test, i] = clf.predict_proba(X[test])[:, 1]
        test_blend_i[:, j] = clf.predict_proba(X_test)[:, 1]

    # test_blend相比而言會多出k-1份資料,直接取均值即可
    test_blend[:, i] = test_blend_i.mean(1)

第二層模型直接使用邏輯迴歸

clf2 = LogisticRegression(C=10, max_iter=100)
clf2.fit(train_blend, Y)
clf2.score(test_blend, Y_test)

上述模型中,得分最高的是隨機森林,達到了0.80382,位於前11%

六、調優

觀察姓名欄位,在同一艘船上,姓相同的乘客很有可能是同一家人,從而在逃生的時候分佈在同一片區域,這裡可以考慮增加特徵。
提取乘客的姓名,對於只出現1次的姓,不進行考慮,直接命名為small,而其他則進行分組

train_test['Name2_'] = train_test['Name'].apply(lambda x: x.split('.')[1].strip())
names = train_test['Name2_'].value_counts().reset_index()
names.columns = ['Name2_', 'Name2_sum']
train_test = pd.merge(train_test, names, how='left', on='Name2_')
train_test.loc[train_test['Name2_sum'] <= 1, 'Name2'] = 'small'
train_test.loc[train_test['Name2_sum'] > 1, 'Name2'] = train_test['Name2_']
train_test = pd.get_dummies(train_test, columns=['Name2'], prefix='N')

再次進行模型訓練,最終得分0.81339,達到前5%

七、結果

最終得分最高的模型為隨機森林,得分為0.81339,排名551/11347,位於Top 5%
這裡寫圖片描述

八、後續

相關推薦

KaggleGetting Started of Titanic

一、概要 泰坦尼克號倖存預測是Kaggle上參與人數最多的的比賽之一,要求參賽人員預測乘客是否能夠倖存,是一個典型的二分類問題。 二、資料簡介 官網提供訓練資料集train.csv和測試資料集test.csv和一個提交樣例資料集,資料中的各個欄位如下:

解決mysql報錯- Expression #1 of ORDER BY clause is not in GROUP BY clause and contains nonaggregated column 'information_schema.PROFILING.SEQ'

_for tran contains column schema mysql eat table express mysql執行報錯: - Expression #1 of ORDER BY clause is not in GROUP BY clause and cont

解決The content of element type "web-app" must match "(icon?display

內容 param match filter res ros welcome local page http://www.educity.cn/wenda/126463.html 解決:The content of element type "web-app" must ma

解決QTforward declaration of &#39;struct Ui::xxx&#39;;invalid use of incomplete struct &quot;Ui::Widget&quot; 等莫名奇異錯誤

執行 center dex text nco jsb ims complete class 今天在進行QT Widget的UI設計時,改了下Widget的對象名,然後在多次成功編譯執行後,執行清理,又一次構建,就出現了好多莫名奇異的錯誤: widget.

LLVM每日談之十九 LLVM的第一本系統的書&lt;Getting Started with LLVM Core Libraries&gt;

關於 日本 簡單的 lvm 作者 普通 lan 最好 裏的 作者:史寧寧(snsn1984)LLVM最終有了一本系統的書了——《Getting Started with LLVM Core Libraries》。這本書號稱是LLVM的第一本書,可是據說日本早就有兩本日文的

Apache ZooKeeper Getting Started Guide 翻譯

tail var directory storage 我們 刪除 multi 包括 initial ZooKeeper 開始向導 開始: 用zookeeper協調分布式程序 單例操作管理zookeeper存儲連接zookeeper執行zookeeper以復

Scala函數式程序設計原理 week1 Getting Started + Functions & Evaluation

lua cin margin cal brk star oci http scala函數 6職4T扒守1F39琳俸http://www.docin.com/app/user/userinfo?userid=178838116 OE泄妨繕5H93419IYhttp://wei

Getting started with Kentico

sbo short conf doc body his learn cati site https://docs.kentico.com/k10tutorial https://docs.kentico.com/k10tutorial/getting-started

報錯illegal mix of collations (utf8_general_ci,IMPLICIT) and (utf8_unicode_ci,IMPLICIT) for operation '='

imp ner 解決方法 block wid sql cit 編碼 不知道 sql語句: select t1.block_info, t2.zone from device_indoor_tbl t1 left join blackwhitezone_tbl t2 o

[原創]Getting Started with Skywalking

-c java word nta rec compress tar mbed already Getting Started with Skywalking Pre JDK 1.8+ Skywalking(v3.2.6) (All packages can

Review the foundation of the transaction、Transaction characteristics in Spring

img 不可重復讀 操作 邏輯 AD 連續 tro required cit 1.什麽是事務: 事務是程序中一系列嚴密的操作,所有操作執行必須成功完成,否則在每個操作所做的更改將會被撤銷,這也是事務的原子性(要麽成功,要麽失敗)。 2.事務特性: 事務特性分為四個:原子

play02-Getting started-Creating a new application

Scala play sbt https://www.playframework.com/documentation/2.6.x/NewApplication 使用Play starter project:https://playframework.com/download#starters如果你

play01-Getting started-Installing Play

Scala sbt Play https://www.playframework.com/documentation/2.6.x/HomeGetting started https://www.playframework.com/documentation/2.6.x/Installing 這個頁

SPOJDecreasing Number of Visible Box(不錯的,背包?貪心?)

test case mini num collect con like clas pos 別人 Shadowman loves to collect box but his roommates woogieman and itman don‘t like box and s

mysql5.6修改字符編碼,ERRIllegal mix of collations for operation 'concat'

查看 AD lob 步驟 creat 處理 col min rep mysql5.6修改字符編碼,ERR:Illegal mix of collations for operation ‘concat‘ 1.問題起因:搭建環境初始化mysql的時候看到mysql配置文件

MySQL8.0 新特性Partial Update of LOB Column

sql摘要: MySQL8.0對json進行了比較完善的支持, 我們知道json具有比較特殊的存儲格式,通常存在多個key value鍵值對,對於類似更新操作通常不會更新整個json列,而是某些鍵值。 對於某些復雜的應用,json列的數據可能會變的非常龐大,這時候一個突出的問題是:innodb並不識別json

e673. Getting Amount of Free Accelerated Image Memory

volatile rap fas ima usually link before reat end Images in accelerated memory are much faster to draw on the screen. However, accelerate

Part 1 - Getting Started(1-3)

pre ews djang clas -c django request htm complex https://simpleisbetterthancomplex.com/series/2017/09/04/a-complete-beginners-guide-to-d

論文筆記Empirical Evaluation of Gated Recurrent Neural Networks on Sequence Modeling

感想 最近深度學習面試的時候,有個面試官問了我LSTM,我一下子傻眼了,確實不怎麼好懂,學LSTM已經有半年的時間了,但是對這個玩意兒卻還不怎麼明白,可能是沒用過它的緣故吧,我找了一篇它和GRU比較的論文,這篇論文沒有從理論上證明哪個模型的好壞,只是從實驗,應用場景的角度發現GRU在一些場景比LST

HDU2444 The Accomodation of Students(二分圖染色+二分圖匹配)

The Accomodation of Students Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 9