原文地址以下:python
https://www.kaggle.com/startupsci/titanic-data-science-solutions正則表達式
----------------------------------------------------------------算法
泰坦尼克數據科學解決方案:數組
1. 工做流程步驟:app
在 Data Science Solutions book 這本書裏,描述了在解決一個競賽問題時所須要作的具體工做流程:less
以上的工做流程僅僅描述了通常的問題解決步驟,然而依然會有一些特殊的案例並不嚴格遵循以上流程:dom
2. 問題的定義:機器學習
通常的競賽網站(如 Kaggle)會在提供訓練數據以及測試數據的同時,給出問題的定義。對與這次的「泰坦尼克號倖存者」問題的定義以下:學習
「在訓練數據中,提供了在泰坦尼克號上乘客的具體數據以及他們是否在那次災難中存活的信息。參賽者可否經過已有的訓練數據訓練出一個模型,此模型須要根據輸入的測試數據裏乘客信息,來預測此乘客是否能在災難中存活」測試
咱們可能也想要經過問題描述獲取更多有關此問題的信息。在此問題的描述中,比較有意義的描述以下:
3. 工做流程裏的目標:
數據科學解決方案的流程主要有7個目標:
4. 會用到的庫:
如下是在接下來的實驗裏會用到的一些庫:
# data analysis and wrangling
import pandas as pd
import numpy as np
import random as rnd
# visualization
import seaborn as sns
import matplotlib.pyplot as plt
# machine learning
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC, LinearSVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.linear_model import Perceptron
from sklearn.linear_model import SGDClassifier
from sklearn.tree import DecisionTreeClassifier
5. 獲取數據:
咱們能夠用python 的 Pandas 來幫助咱們處理數據。首先能夠將訓練數據以及測試數據讀入到Pandas 的 DataFrames 裏。咱們也會將這兩個數據集結合起來,用於在兩個數據集上同時作一些特定的操做。
# set pandas
pd.set_option('display.width', 1000)
# use pandas to manage data
train_df = pd.read_csv('data/train.csv')
test_df = pd.read_csv('data/test.csv')
combine = [train_df, test_df]
6. 經過描述數據來分析:
Pandas 也能夠幫助咱們描述數據集。咱們能夠經過如下問答的方式來查看數據集:
1. 在數據集中有哪些可用的特徵?
首先須要注意的是,數據集裏特徵的描述已經在問題描述裏給出了,這次數據集裏的特徵描述以下:
https://www.kaggle.com/c/titanic/data
------------------------------------------------------------------------------------------------------
主要內容爲:
Data Dictionary
Variable |
Definition |
Key |
survival |
Survival |
0 = No, 1 = Yes |
pclass |
Ticket class |
1 = 1st, 2 = 2nd, 3 = 3rd |
sex |
Sex |
|
Age |
Age in years |
|
sibsp |
# of siblings / spouses aboard the Titanic |
|
parch |
# of parents / children aboard the Titanic |
|
ticket |
Ticket number |
|
fare |
Passenger fare |
|
cabin |
Cabin number |
|
embarked |
Port of Embarkation |
C = Cherbourg, Q = Queenstown, S = Southampton |
Variable Notes
pclass: A proxy for socio-economic status (SES)
1st = Upper
2nd = Middle
3rd = Lower
age: Age is fractional if less than 1. If the age is estimated, is it in the form of xx.5
sibsp: The dataset defines family relations in this way...
Sibling = brother, sister, stepbrother, stepsister
Spouse = husband, wife (mistresses and fiancés were ignored)
parch: The dataset defines family relations in this way...
Parent = mother, father
Child = daughter, son, stepdaughter, stepson
Some children travelled only with a nanny, therefore parch=0 for them.
------------------------------------------------------------------------------------------------------
在 Pandas裏:
>>> print(train_df.columns.values)
['PassengerId' 'Survived' 'Pclass' 'Name' 'Sex' 'Age' 'SibSp' 'Parch'
'Ticket' 'Fare' 'Cabin' 'Embarked']
2. 哪些特徵是離散型的?
這些離散型的數值能夠將樣本分類爲一系列類似的樣本。在離散型特徵裏,它們的數值是基於名詞的?仍是基於有序的?又或是基於比率的?仍是基於間隔類的?除此以外,這個能夠幫助咱們爲數據選擇合適的圖形作可視化。
在這個問題中,離散型的變量有:Survived,Sex 和 Embarked。基於序列的有:Pclass
3. 哪些特徵是數值型?
哪些特徵是數值型的?這些數據的值隨着樣本的不一樣而不一樣。在數值型特徵裏,它們的值是離散的仍是連續的?又或者是基於時間序列?除此以外,這個能夠幫助咱們爲數據選擇合適的圖形作可視化。
在這個問題中,連續型的數值特徵有:Age,Fare。離散型數值有:SibSp,Parch
>>> train_df.head()
4. 哪些特徵是混合型數據?
數值型、字母數值型數據在同一特徵下面。這些有多是咱們須要修正的目標數據。
在這個問題中,Ticket是混合了數值型以及字母數值型的數據類型,Cabin是字母數值型數據
5. 哪些特徵可能包含錯誤數據或打字錯誤?
在大型數據集裏要發現這些可能比較困難,然而經過觀察小型的數據集裏少許的樣本,可能也能夠徹底告訴咱們哪些特徵須要修正。
在這個問題中,Name的特徵可能包含錯誤或者打字錯誤,由於會有好幾種方法來描述名字
>>> train_df.tail()
6. 哪些特徵包含空格,null或者空值
這些空格,null值或者空值極可能須要修正。
在這個問題中:
7.每一個特徵下的數據類型是什麼?
這個能夠在咱們作數據轉換時起到較大的幫助。
在這個問題中:
>>> train_df.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)
memory usage: 83.6+ KB
>>> test_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 418 entries, 0 to 417
Data columns (total 11 columns):
PassengerId 418 non-null int64
Pclass 418 non-null int64
Name 418 non-null object
Sex 418 non-null object
Age 332 non-null float64
SibSp 418 non-null int64
Parch 418 non-null int64
Ticket 418 non-null object
Fare 417 non-null float64
Cabin 91 non-null object
Embarked 418 non-null object
dtypes: float64(2), int64(4), object(5)
memory usage: 36.0+ KB
8. 在樣本里,數值型特徵的數值分佈是什麼樣的?
這個能夠幫助咱們初步瞭解:訓練數據集如何體現了實際問題。
在這個問題中:
咱們能夠經過如下方式獲取上述信息:
>>> train_df.describe()
PassengerId Survived Pclass Age SibSp Parch Fare
count 891.000000 891.000000 891.000000 714.000000 891.000000 891.000000 891.000000
mean 446.000000 0.383838 2.308642 29.699118 0.523008 0.381594 32.204208
std 257.353842 0.486592 0.836071 14.526497 1.102743 0.806057 49.693429
min 1.000000 0.000000 1.000000 0.420000 0.000000 0.000000 0.000000
25% 223.500000 0.000000 2.000000 20.125000 0.000000 0.000000 7.910400
50% 446.000000 0.000000 3.000000 28.000000 0.000000 0.000000 14.454200
75% 668.500000 1.000000 3.000000 38.000000 1.000000 0.000000 31.000000
max 891.000000 1.000000 3.000000 80.000000 8.000000 6.000000 512.329200
# 經過使用 percentiles=[.61, .62] 來查看數據集能夠了解到生存率爲 38%
>>> train_df.describe(percentiles=[.61, .62])
PassengerId Survived Pclass Age SibSp Parch Fare
count 891.000000 891.000000 891.000000 714.000000 891.000000 891.000000 891.000000
mean 446.000000 0.383838 2.308642 29.699118 0.523008 0.381594 32.204208
std 257.353842 0.486592 0.836071 14.526497 1.102743 0.806057 49.693429
min 1.000000 0.000000 1.000000 0.420000 0.000000 0.000000 0.000000
50% 446.000000 0.000000 3.000000 28.000000 0.000000 0.000000 14.454200
61% 543.900000 0.000000 3.000000 32.000000 0.000000 0.000000 23.225000
62% 552.800000 1.000000 3.000000 32.000000 0.000000 0.000000 24.150000
max 891.000000 1.000000 3.000000 80.000000 8.000000 6.000000 512.329200
# 經過使用 percentiles=[.75, .8] 來查看Parch的分佈
>>> train_df.describe(percentiles=[.75, .8])
PassengerId Survived Pclass Age SibSp Parch Fare
count 891.000000 891.000000 891.000000 714.000000 891.000000 891.000000 891.000000
mean 446.000000 0.383838 2.308642 29.699118 0.523008 0.381594 32.204208
std 257.353842 0.486592 0.836071 14.526497 1.102743 0.806057 49.693429
min 1.000000 0.000000 1.000000 0.420000 0.000000 0.000000 0.000000
50% 446.000000 0.000000 3.000000 28.000000 0.000000 0.000000 14.454200
75% 668.500000 1.000000 3.000000 38.000000 1.000000 0.000000 31.000000
80% 713.000000 1.000000 3.000000 41.000000 1.000000 1.000000 39.687500
max 891.000000 1.000000 3.000000 80.000000 8.000000 6.000000 512.329200
# 經過使用 percentile=[.68, .69] 來查看SibSp的分佈
>>> train_df.describe(percentiles=[.68, .69])
PassengerId Survived Pclass Age SibSp Parch Fare
count 891.000000 891.000000 891.000000 714.000000 891.000000 891.000000 891.000000
mean 446.000000 0.383838 2.308642 29.699118 0.523008 0.381594 32.204208
std 257.353842 0.486592 0.836071 14.526497 1.102743 0.806057 49.693429
min 1.000000 0.000000 1.000000 0.420000 0.000000 0.000000 0.000000
50% 446.000000 0.000000 3.000000 28.000000 0.000000 0.000000 14.454200
68% 606.200000 1.000000 3.000000 35.000000 0.000000 0.000000 26.307500
69% 615.100000 1.000000 3.000000 35.000000 1.000000 0.000000 26.550000
max 891.000000 1.000000 3.000000 80.000000 8.000000 6.000000 512.329200
# 經過使用 percentile=[.1, .2, .3, .4, .5, .6, .7, .8, .9, .99] 來查看Age和Fare的分佈
>>> train_df.describe(percentiles=[.1, .2, .3, .4, .5, .6, .7, .8, .9, .99])
PassengerId Survived Pclass Age SibSp Parch Fare
count 891.000000 891.000000 891.000000 714.000000 891.000000 891.000000 891.000000
mean 446.000000 0.383838 2.308642 29.699118 0.523008 0.381594 32.204208
std 257.353842 0.486592 0.836071 14.526497 1.102743 0.806057 49.693429
min 1.000000 0.000000 1.000000 0.420000 0.000000 0.000000 0.000000
10% 90.000000 0.000000 1.000000 14.000000 0.000000 0.000000 7.550000
20% 179.000000 0.000000 1.000000 19.000000 0.000000 0.000000 7.854200
30% 268.000000 0.000000 2.000000 22.000000 0.000000 0.000000 8.050000
40% 357.000000 0.000000 2.000000 25.000000 0.000000 0.000000 10.500000
50% 446.000000 0.000000 3.000000 28.000000 0.000000 0.000000 14.454200
60% 535.000000 0.000000 3.000000 31.800000 0.000000 0.000000 21.679200
70% 624.000000 1.000000 3.000000 36.000000 1.000000 0.000000 27.000000
80% 713.000000 1.000000 3.000000 41.000000 1.000000 1.000000 39.687500
90% 802.000000 1.000000 3.000000 50.000000 1.000000 2.000000 77.958300
99% 882.100000 1.000000 3.000000 65.870000 5.000000 4.000000 249.006220
max 891.000000 1.000000 3.000000 80.000000 8.000000 6.000000 512.329200
8. 在樣本里,離散型數據的分佈是什麼?
在這個問題中:
能夠經過如下方法得到以上信息:
>>> train_df.describe(include=['O'])
Name Sex Ticket Cabin Embarked
count 891 891 891 204 889
unique 891 2 681 147 3
top Andrew, Mr. Edgardo Samuel male CA. 2343 G6 S
freq 1 577 7 4 644
7. 基於以上數據分析後的假設
根據以上的數據分析步驟後,咱們能夠暫時得出如下假設。固然,咱們也能夠在以後驗證這些假設。
相互關係:
咱們想知道每一個特徵與Survival的相關性如何。咱們但願可以今早的作這一步,而且將這些相關性特徵匹配到建模後的相關性特徵上。
補全數據:
修正數據:
創造數據:
分類:
根據以前的問題描述或者已有的數據,咱們也能夠提出如下假設:
8. 基於pivoting features的分析
爲了驗證以前的觀察與假設,咱們能夠經過pivoting feature的方法簡單的分析一下特徵之間的相關性。
這種方法僅僅對那些沒有特別多空值的屬性有效,而且僅僅對那些分類型的(Sex)、有序型的(Pclass)以及離散型(SibSp,Parch)的特徵纔有意義。
1. Pclass:咱們觀察到Pclass=1與Survived的相關性較大(>0.5),因此能夠考慮將此特徵放入到以後的模型裏
2. Sex:咱們能夠確認Sex=female有着高達74%的生存率
3. SibSp 和 Parch:這些特徵下有些值與survived有相關性,可是有些又毫無相關性。因此咱們可能須要基於這些單獨的特徵或一系列特徵建立一個新特徵,以作進一步分析
以上結論能夠經過下面的操做獲取:
>>> train_df[['Pclass', 'Survived']].groupby(['Pclass'], as_index=False).mean().sort_values(by='Survived', ascending=False)
Pclass Survived
0 1 0.629630
1 2 0.472826
2 3 0.242363
>>> train_df[['Sex', 'Survived']].groupby(['Sex'], as_index=False).mean().sort_values(by='Survived', ascending=False)
Sex Survived
0 female 0.742038
1 male 0.188908
>>> train_df[['SibSp', 'Survived']].groupby(['SibSp'], as_index=False).mean().sort_values(by='Survived', ascending=False)
SibSp Survived
1 1 0.535885
2 2 0.464286
0 0 0.345395
3 3 0.250000
4 4 0.166667
5 5 0.000000
6 8 0.000000
>>> train_df[['Parch', 'Survived']].groupby(['Parch'], as_index=False).mean().sort_values(by='Survived', ascending=False)
Parch Survived
3 3 0.600000
1 1 0.550847
2 2 0.500000
0 0 0.343658
5 5 0.200000
4 4 0.000000
6 6 0.000000
9.經過將數據可視化進行分析
如今咱們能夠經過將數據可視化對數據作進一步分析,並繼續驗證咱們以前的假設是否正確
數值型特徵與Survived之間的聯繫:
柱狀圖在用於分析連續型的數值特徵時很是有用,如特徵Age,它的柱狀圖數值範圍(不一樣的年齡範圍)能夠幫助咱們識別一些有用的模式。
經過使用默認或自定的數值範圍(年齡範圍),柱狀圖能夠幫助咱們描繪出樣本所遵循的分佈。
它能夠幫助咱們發現是否某些特定的年齡範圍(如嬰兒)有更高的存活率。
咱們能夠經過如下代碼來畫出Age的柱狀圖:
>>> g = sns.FacetGrid(train_df, col='Survived')
>>> g.map(plt.hist, 'Age', bins=20)
>>> plt.show()
觀察:
結論:
以上簡單的分析驗證了咱們以前的假設:
數值型與序列型特徵之間的聯繫:
咱們能夠將多個特徵組合,而後經過一個簡單的圖來識別它們之間的關係。這種方法能夠應用在數值型以及分類型(Pclass)的特徵裏,由於它們的值都是數值型。
咱們能夠經過如下代碼來畫出Pclass的柱狀圖:
>>> grid = sns.FacetGrid(train_df, col='Survived', row='Pclass', size=2.2, aspect=1.6)
>>> grid.map(plt.hist, 'Age', alpha=.5, bins=20)
>>> grid.add_legend()
>>> plt.show()
觀察:
結論:
離散型特徵與Survived之間的聯繫:
如今咱們能夠查看離散型特徵與survived之間的關係
咱們能夠經過如下方式將數據可視化:
>>> grid = sns.FacetGrid(train_df, row='Embarked', size=2.2, aspect=1.6)
>>> grid.map(sns.pointplot, 'Pclass', 'Survived', 'Sex', palette='deep')
>>> grid.add_legend()
>>> plt.show()
觀察:
結論:
離散型特徵與數值型特徵之間的聯繫:
咱們可能也想找出離散型與數值型特徵之間的關係。
咱們能夠考慮查看Embarked(離散非數值型),Sex(離散非數值型),Fare(連續數值型)與Survived(離散數值型)之間的關係
咱們能夠經過下方式將數據可視化:
>>> grid = sns.FacetGrid(train_df, row='Embarked', col='Survived', size=2.2, aspect=1.6)
>>> grid.map(sns.barplot, 'Sex', 'Fare', alpha=.5, ci=None)
>>> grid.add_legend()
>>> plt.show()
觀察:
結論:
10.加工數據
咱們根據數據集以及題目的要求已經收集了一些假設與結論。到如今爲止,咱們暫時尚未對任何特徵或數據進行處理。
接下來咱們會根據以前作的假設與結論,以「修正數據」、「創造數據」以及「補全數據」爲目標,對數據進行處理。
經過丟棄特徵來修正數據:
這個步驟比較好的一個開始。經過丟棄某些特徵,可讓咱們處理更少的數據點,並讓分析更簡單。
根據咱們以前的假設和結論,咱們但願丟棄Cabin和Ticket這兩個特徵。
在這裏須要注意的是,爲了保持數據的一致,咱們須要同時將訓練集與測試集裏的這兩個特徵均丟棄。
具體步驟以下:
>>> print("Before", train_df.shape, test_df.shape, combine[0].shape, combine[1].shape)
Before (891, 12) (418, 11) (891, 12) (418, 11)
>>> train_df = train_df.drop(['Ticket', 'Cabin'], axis=1)
>>> test_df = test_df.drop(['Ticket', 'Cabin'], axis=1)
>>> combine = [train_df, test_df]
>>> print('After', train_df.shape, test_df.shape, combine[0].shape, combine[1].shape)
After (891, 10) (418, 9) (891, 10) (418, 9)
經過已有的特徵建立新特徵:
咱們在丟棄Name與PassengerId這兩個特徵以前,但願從Name特徵裏提取出Titles的特徵,並測試Titles與survival之間的關係。
在下面的代碼中,咱們經過正則提取了Title特徵,正則表達式爲(\w+\.),它會在Name特徵裏匹配第一個以「.」號爲結束的單詞。
同時,指定expand=False的參數會返回一個DataFrame。
>>> for dataset in combine:
dataset['Title'] = dataset.Name.str.extract('([A-Za-z]+)\.', expand=False)
>>> pd.crosstab(train_df['Title'], train_df['Sex'])
Sex female male
Title
Capt 0 1
Col 0 2
Countess 1 0
Don 0 1
Dr 1 6
Jonkheer 0 1
Lady 1 0
Major 0 2
Master 0 40
Miss 182 0
Mlle 2 0
Mme 1 0
Mr 0 517
Mrs 125 0
Ms 1 0
Rev 0 6
Sir 0 1
咱們可使用高一些更常見的名字或「Rare」來代替一些Title,如:
for dataset in combine:
dataset['Title'] = dataset['Title'].replace(['Lady', 'Countess', 'Capt',
'Col', 'Don', 'Dr', 'Major',
'Rev', 'Sir', 'Jonkheer', 'Dona'], 'Rare')
dataset['Title'] = dataset['Title'].replace('Mlle', 'Miss')
dataset['Title'] = dataset['Title'].replace('Ms', 'Miss')
dataset['Title'] = dataset['Title'].replace('Mme', 'Mrs')
>>> train_df[['Title', 'Survived']].groupby(['Title'], as_index=False).mean()
Title Survived
0 Master 0.575000
1 Miss 0.702703
2 Mr 0.156673
3 Mrs 0.793651
4 Rare 0.347826
進一步的,咱們能夠將這些離散型的Title轉換爲有序的數值型:
# convert categorical titles to ordinal
title_mapping = {"Mr":1, "Miss":2, "Mrs":3, "Master":4, "Rare":5}
for dataset in combine:
dataset['Title'] = dataset['Title'].map(title_mapping)
dataset['Title'] = dataset['Title'].fillna(0)
>>> train_df.head()
如今咱們能夠放心的從訓練集與測試集裏丟棄Name特徵。同時,咱們也再也不須要訓練集裏的PassengerId特徵:
>>> train_df = train_df.drop(['Name', 'PassengerId'], axis=1)
>>> test_df = test_df.drop(['Name'], axis=1)
>>> combine = [train_df, test_df]
>>> train_df.shape, test_df.shape
((891, 9), (418, 9))
>>> train_df.head()
Survived Pclass Sex Age SibSp Parch Fare Embarked Title
0 0 3 male 22.0 1 0 7.2500 S 1
1 1 1 female 38.0 1 0 71.2833 C 3
2 1 3 female 26.0 0 0 7.9250 S 2
3 1 1 female 35.0 1 0 53.1000 S 3
4 0 3 male 35.0 0 0 8.0500 S 1
新的發現:
當咱們畫出Title,Age和Survived的圖後,咱們有了如下新的發現:
結論:
轉換一個離散型的特徵
如今咱們能夠將一些包含字符串數據的特徵轉換爲數值型特徵,由於在不少建模算法裏,輸入的參數要求爲數值型。
這個步驟可讓咱們達到補全數據的目標。
咱們能夠從轉換Sex特徵開始,將female轉換爲1,male轉換爲0。咱們能夠將新的特徵命名爲Gender:
for dataset in combine:
dataset['Sex'] = dataset['Sex'].map({'female':1, 'male':0}).astype(int)
>>> train_df.head()
Survived Pclass Sex Age SibSp Parch Fare Embarked Title
0 0 3 0 22.0 1 0 7.2500 S 1
1 1 1 1 38.0 1 0 71.2833 C 3
2 1 3 1 26.0 0 0 7.9250 S 2
3 1 1 1 35.0 1 0 53.1000 S 3
4 0 3 0 35.0 0 0 8.0500 S 1
補全連續數值型特徵
如今咱們能夠開始爲那些含null值或者丟失值的特徵補全數據。咱們首先會爲Age特徵補全數據。
如今咱們總結一下三種補全連續數值型特徵數據的方法:
1. 一個簡單的方法是產生一個隨機數,這個隨機數的範圍在這個特徵的平均值以及標準差之間
2. 更精準的一個作法是使用與它相關的特徵來作一個猜想。在這個案例中,咱們發現Age,Gender和Pclass之間有關聯。
因此咱們會使用一系列Pclass和Gender特徵組合後的中值,做爲猜想的Age值。
因此咱們會有一系列的猜想值如:當Pclass=1且Gender=0時,當Pclass=1且Gender=1時,等等…
3. 第三種方法是結合以上兩種方法。咱們能夠根據一系列Pclass與Gender的組合,並使用第一種方法裏提到的隨機數來猜想缺失的Age值
方法1與方法3會在模型裏引入隨機噪音,屢次的結果可能會有所不一樣。因此咱們在這更傾向於使用方法2:
>>> grid = sns.FacetGrid(train_df, row='Pclass', col='Sex', size=2.2, aspect=1.6)
>>> grid.map(plt.hist, 'Age', alpha=.5, bins=20)
>>> grid.add_legend()
>>> plt.show()
咱們先準備一個空的數組來存儲猜想的年齡,由於是Pclass與Gender的組合,因此數組大小爲2x3:
>>> guess_ages = np.zeros((2, 3))
而後咱們能夠對Sex(0或1)和Pclass(1,2,3)進行迭代,並計算出在6中組合下所獲得的猜想(Age)值:
for dataset in combine:
for i in range(0, 2):
for j in range(0, 3):
guess_df = dataset[(dataset['Sex'] == i) & (dataset['Pclass'] == j+1)]['Age'].dropna()
age_guess = guess_df.median()
# Convert random age float to nearest .5 age
guess_ages[i, j] = int(age_guess / 0.5 + 0.5) * 0.5
for i in range(0, 2):
for j in range(0, 3):
dataset.loc[ (dataset.Age.isnull()) & (dataset.Sex == i) & (dataset.Pclass == j+1),
'Age'] = guess_ages[i,j]
dataset['Age'] = dataset['Age'].astype(int)
>>> train_df.head()
Survived Pclass Sex Age SibSp Parch Fare Embarked Title
0 0 3 0 22 1 0 7.2500 S 1
1 1 1 1 38 1 0 71.2833 C 3
2 1 3 1 26 0 0 7.9250 S 2
3 1 1 1 35 1 0 53.1000 S 3
4 0 3 0 35 0 0 8.0500 S 1
如今咱們對Age分段,並查看每段與Survived之間的相關性:
>>> train_df['AgeBand'] = pd.cut(train_df['Age'], 5)
>>> train_df[['AgeBand', 'Survived']].groupby(['AgeBand'], as_index=False).mean().sort_values(by='AgeBand', ascending=True)
AgeBand Survived
0 (-0.08, 16.0] 0.550000
1 (16.0, 32.0] 0.337374
2 (32.0, 48.0] 0.412037
3 (48.0, 64.0] 0.434783
4 (64.0, 80.0) 0.090909
而後咱們根據上面的分段,使用有序的數值來替換Age裏的值:
for dataset in combine:
dataset.loc[ dataset['Age'] <= 16, 'Age'] = 0
dataset.loc[(dataset['Age'] > 16) & (dataset['Age'] <= 32), 'Age'] = 1
dataset.loc[(dataset['Age'] > 32) & (dataset['Age'] <= 48), 'Age'] = 2
dataset.loc[(dataset['Age'] > 48) & (dataset['Age'] <= 64), 'Age'] = 3
dataset.loc[ dataset['Age'] > 64, 'Age']
>>> train_df.head()
Survived Pclass Sex Age SibSp Parch Fare Embarked Title AgeBand
0 0 3 0 1 1 0 7.2500 S 1 (16.0, 32.0]
1 1 1 1 2 1 0 71.2833 C 3 (32.0, 48.0]
2 1 3 1 1 0 0 7.9250 S 2 (16.0, 32.0]
3 1 1 1 2 1 0 53.1000 S 3 (32.0, 48.0)
4 0 3 0 2 0 0 8.0500 S 1 (32.0, 48.0)
接着咱們能夠丟棄AgeBand特徵:
>>> train_df = train_df.drop(['AgeBand'], axis=1)
>>> combine = [train_df, test_df]
>>> train_df.head()
Survived Pclass Sex Age SibSp Parch Fare Embarked Title
0 0 3 0 1 1 0 7.2500 S 1
1 1 1 1 2 1 0 71.2833 C 3
2 1 3 1 1 0 0 7.9250 S 2
3 1 1 1 2 1 0 53.1000 S 3
4 0 3 0 2 0 0 8.0500 S 1
經過已有的特徵組合出新特徵
如今咱們能夠經過組合Parch和SibSp特徵,建立一個新的FamilySize特徵。這個步驟可讓咱們從數據集裏丟棄Parch與SibSp特徵。
for dataset in combine:
dataset['FamilySize'] = dataset['SibSp'] + dataset['Parch'] + 1
>>> train_df[['FamilySize', 'Survived']].groupby(['FamilySize'], as_index=False).mean().sort_values(by='Survived', ascending=False)
FamilySize Survived
3 4 0.724138
2 3 0.578431
1 2 0.552795
6 7 0.333333
0 1 0.303538
4 5 0.200000
5 6 0.136364
7 8 0.000000
8 11 0.000000
接着咱們能夠建立另外一個名爲IsAlone的特徵:
for dataset in combine:
dataset['IsAlone'] = 0
dataset.loc[dataset['FamilySize'] == 1, 'IsAlone'] = 1
>>> train_df[['IsAlone', 'Survived']].groupby(['IsAlone'], as_index=False).mean()
IsAlone Survived
0 0 0.505650
1 1 0.303538
基於上面的數據表現,咱們如今能夠丟棄Parch、SibSp以及FamilySize的特徵,保留IsAlone的特徵:
>>> train_df = train_df.drop(['Parch', 'SibSp', 'FamilySize'], axis=1)
>>> test_df = test_df.drop(['Parch', 'SibSp', 'FamilySize'], axis=1)
>>> combine = [train_df, test_df]
>>> train_df.head()
Survived Pclass Sex Age Fare Embarked Title IsAlone
0 0 3 0 1 7.2500 S 1 0
1 1 1 1 2 71.2833 C 3 0
2 1 3 1 1 7.9250 S 2 1
3 1 1 1 2 53.1000 S 3 0
4 0 3 0 2 8.0500 S 1 1
咱們還能夠經過結合Pclass 和 Age來建立一個新的特徵:
for dataset in combine:
dataset['Age*Class'] = dataset.Age * dataset.Pclass
>>> train_df.loc[:, ['Age*Class', 'Age', 'Pclass']].head(10)
Age*Class Age Pclass
0 3 1 3
1 2 2 1
2 3 1 3
3 2 2 1
4 6 2 3
5 3 1 3
6 3 3 1
7 0 0 3
8 3 1 3
9 0 0 2
補全一個離散型的特徵
Embarked特徵主要有三個值,分別爲S,Q,C,對應了三個登船港口。在訓練集裏,這個有2個缺失值,咱們會使用頻率最高的值來填充這個缺失值。
>>> freq_port = train_df.Embarked.dropna().mode()[0]
>>> freq_port
'S'
for dataset in combine:
dataset['Embarked'] = dataset['Embarked'].fillna(freq_port)
>>> train_df[['Embarked', 'Survived']].groupby(['Embarked'], as_index=False).mean().sort_values(by='Survived', ascending=False)
Embarked Survived
0 C 0.553571
1 Q 0.389610
2 S 0.339009
將離散型特徵轉換爲數值型
咱們如今能夠將離散型的Embarked特徵轉換爲數值型特徵
for dataset in combine:
dataset['Embarked'] = dataset['Embarked'].map({'S': 0, 'C': 1, 'Q': 2}).astype(int)
>>> train_df.head()
Survived Pclass Sex Age Fare Embarked Title IsAlone Age*Class
0 0 3 0 1 7.2500 0 1 0 3
1 1 1 1 2 71.2833 1 3 0 2
2 1 3 1 1 7.9250 0 2 1 3
3 1 1 1 2 53.1000 0 3 0 2
4 0 3 0 2 8.0500 0 1 1 6
補全數值型特徵
如今咱們能夠開始爲測試集裏的Fare特徵補全數據。在補全時,咱們可使用最頻繁出現的數據用於補全缺失值。
(咱們也能夠將Fare的數值作四捨五入,將它精確到2位)
>>> test_df['Fare'].fillna(test_df['Fare'].dropna().median(), inplace=True)
>>> test_df.head()
PassengerId Pclass Sex Age Fare Embarked Title IsAlone Age*Class
0 892 3 0 2 7.8292 2 1 1 6
1 893 3 1 2 7.0000 0 3 0 6
2 894 2 0 3 9.6875 2 1 1 6
3 895 3 0 1 8.6625 0 1 1 3
4 896 3 1 1 12.2875 0 3 0 3
接下來咱們將Fare分段:
>>> train_df['FareBand'] = pd.qcut(train_df['Fare'], 4)
>>> train_df[['FareBand', 'Survived']].groupby(['FareBand'], as_index=False).mean().sort_values(by='FareBand', ascending=True)
FareBand Survived
0 (-0.001, 7.91] 0.197309
1 (7.91, 14.454] 0.303571
2 (14.454, 31.0] 0.454955
3 (31.0, 512.329) 0.581081
根據分段後的特徵FareBand,將Fare轉換爲有序的數值型特徵:
for dataset in combine:
dataset.loc[ dataset['Fare'] <= 7.91, 'Fare'] = 0
dataset.loc[(dataset['Fare'] > 7.91) & (dataset['Fare'] <= 14.454), 'Fare'] = 1
dataset.loc[(dataset['Fare'] > 14.454) & (dataset['Fare'] <= 31), 'Fare'] = 2
dataset.loc[dataset['Fare'] > 31, 'Fare'] = 3
dataset['Fare'] = dataset['Fare'].astype(int)
>>> train_df = train_df.drop(['FareBand'], axis=1)
>>> combine = [train_df, test_df]
>>> train_df.head(10)
Survived Pclass Sex Age Fare Embarked Title IsAlone Age*Class
0 0 3 0 1 0 0 1 0 3
1 1 1 1 2 3 1 3 0 2
2 1 3 1 1 1 0 2 1 3
3 1 1 1 2 3 0 3 0 2
4 0 3 0 2 1 0 1 1 6
5 0 3 0 1 1 2 1 1 3
6 0 1 0 3 3 0 1 1 3
7 0 3 0 0 2 0 4 0 0
8 1 3 1 1 1 0 3 0 3
9 1 2 1 0 2 1 3 0 0
11.建模,預測,並解決問題
如今咱們已經作好了訓練模型的準備,在模型訓練完後,咱們便可將其應用到解決問題中。對於預測的問題,咱們至少有60多種算法可供選擇。
因此咱們必須理解問題的類型和解決方案的需求,這樣才能縮小模型的選擇範圍。如今這個問題是一個分類與迴歸的問題,
咱們但願找出輸出(即Survived)與其餘特徵(即Gender,Age,Port等)之間的關係。由於給定了訓練集,因此這在機器學習裏是一個有監督學習。
因此如今對算法的需求是:有監督學習加上分類與迴歸。根據這個條件,咱們有如下模型可供選擇:
如今咱們將訓練集與測試集再作一下區分:
>>> X_train = train_df.drop('Survived', axis=1)
>>> Y_train = train_df['Survived']
>>> X_test = test_df.drop('PassengerId', axis=1).copy()
>>> X_train, Y_train, X_test
((891, 8), (891,), (418, 8))
Logistic Regression 是一個很是有用的模型,能夠在工做流程裏優先使用。它經過使用估計機率的方法衡量了離散型特徵與其餘特徵之間的關係,是一個漸增型的邏輯分佈。
>>> logreg = LogisticRegression()
>>> logreg.fit(X_train, Y_train)
>>> Y_pred = logreg.predict(X_test)
>>> acc_log = round(logreg.score(X_train, Y_train) * 100, 2)
>>> acc_log
80.359999999999999
咱們能夠用Logistic Regression來驗證咱們之間作的假設與結論。這個能夠經過計算特徵值的係數來達成。正係數能夠提高對數概率(因此增加了機率),負係數會下降對數概率(所以下降了機率):
>>> coeff_df = pd.DataFrame(train_df.columns.delete(0))
>>> coeff_df.columns = ['Feature']
>>> coeff_df["Correlation"] = pd.Series(logreg.coef_[0])
>>> coeff_df.sort_values(by='Correlation', ascending=False)
Feature Correlation
1 Sex 2.201527
5 Title 0.398234
2 Age 0.287162
4 Embarked 0.261762
6 IsAlone 0.129140
3 Fare -0.085150
7 Age*Class -0.311202
0 Pclass -0.749007
從上面的結果咱們能夠看出:
下一步咱們使用SVM來分析數據並作分類與迴歸分析。
>>> svc = SVC()
>>> svc.fit(X_train, Y_train)
>>> Y_pred = svc.predict(X_test)
>>> acc_svc = round(svc.score(X_train, Y_train) * 100, 2)
>>> acc_svc
83.840000000000003
能夠看到使用SVM後的正確率獲得了提高。
在模式識別中,KNN算法是一種非參數的方法,用於作分類與迴歸。使用KNN來分析此問題的話:
>>> knn = KNeighborsClassifier(n_neighbors = 3)
>>> knn.fit(X_train, Y_train)
>>> Y_pred = knn.predict(X_test)
>>> acc_knn = round(knn.score(X_train, Y_train) * 100, 2)
84.739999999999995
能夠看到使用KNN的正確率比Logistic Regression更高,可是比SVM更低
下面咱們試試樸素貝葉斯:
>>> gaussian = GaussianNB()
>>> gaussian.fit(X_train, Y_train)
>>> Y_pred = gaussian.predict(X_test)
>>> acc_gaussian = round(gaussian.score(X_train, Y_train) * 100, 2)
72.280000000000001
看來在這個問題中使用樸素貝葉斯不是一個很好的選擇,從當前來看,它的正確率是最低的。
接下來咱們試試 perceptron(感知機)算法,它能夠用於二分類問題:
>>> perceptron = Perceptron()
>>> perceptron.fit(X_train, Y_train)
>>> Y_pred = perceptron.predict(X_test)
>>> acc_perceptron = round(perceptron.score(X_train, Y_train) * 100, 2)
78.0
能夠看到perceptron的正確率也不高
接下來試試Linear SVC:
>>> linear_svc = LinearSVC()
>>> linear_svc.fit(X_train, Y_train)
>>> Y_pred = linear_svc.predict(X_test)
>>> acc_linear_svc = round(linear_svc.score(X_train, Y_train) * 100, 2)
79.010000000000005
與隨機梯度降低分類器:
>>> sgd = SGDClassifier()
>>> sgd.fit(X_train, Y_train)
>>> Y_pred = sgd.predict(X_test)
>>> acc_sgd = round(sgd.score(X_train, Y_train) * 100, 2)
78.230000000000004
這幾個算法計算到的正確率都不夠理想。
接下來咱們看看很常見的決策樹算法:
>>> decision_tree = DecisionTreeClassifier()
>>> decision_tree.fit(X_train, Y_train)
>>> Y_pred = decision_tree.predict(X_test)
>>> acc_decision_tree = round(decision_tree.score(X_train, Y_train) * 100, 2)
86.760000000000005
能夠看到,使用決策樹的算法使得正確率達到了一個更高的值。在目前爲止,它的正確率是最高的。
而後咱們看看隨機森林,隨機森林經過組合多個決策樹算法來完成:
>>> random_forest = RandomForestClassifier(n_estimators=100)
>>> random_forest.fit(X_train, Y_train)
>>> Y_pred = random_forest.predict(X_test)
>>> acc_random_forest = round(random_forest.score(X_train, Y_train) * 100, 2)
86.760000000000005
經過比較模型的正確率,咱們決定使用最高正確率的模型,即隨機森林的輸出做爲結果提交。
12.模型評價
如今咱們能夠對以上的模型的正確率進行排名,並從中選擇一個正確率最高的模型:
models = pd.DataFrame({
'Model': ['Support Vector Machines', 'KNN', 'Logistic Regression',
'Random Forest', 'Naive Bayes', 'Perceptron',
'Stochastic Gradient Decent', 'Linear SVC',
'Decision Tree'],
'Score': [acc_svc, acc_knn, acc_log,
acc_random_forest, acc_gaussian, acc_perceptron,
acc_sgd, acc_linear_svc, acc_decision_tree]})
>>> models.sort_values(by='Score', ascending=False)
Model Score
3 Random Forest 86.76
8 Decision Tree 86.76
1 KNN 84.74
0 Support Vector Machines 83.84
2 Logistic Regression 80.36
7 Linear SVC 79.01
6 Stochastic Gradient Decent 78.23
5 Perceptron 78.00
4 Naive Bayes 72.28
其中決策樹與隨機森林的正確率最高,可是咱們在這裏會選擇隨機森林算法,由於它相對於決策樹來講,彌補了決策樹有可能過擬合的問題。
最後咱們作提交:
>>> submission = pd.DataFrame({"PassengerId": test_df["PassengerId"], "Survived": Y_pred})