參考Kernels里面評論較高的一篇文章,整理作者解決整個問題的過程,梳理該篇是用以了解到整個完整的建模過程,如何思考問題,處理問題,過程中又為何下那樣或者這樣的結論等!
最后得分並不是特別高,只是到34%,更多是整理一個解決問題的思路,另外前面三個大步驟根據思維導圖看即可,代碼跟文字等從第四個步驟開始寫起。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(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.
------------------------------------------------------------------------------------------------------
查看訓練集里面各字段:
print(train_df.columns.values)
['PassengerId' 'Survived' 'Pclass' 'Name' 'Sex' 'Age' 'SibSp' 'Parch' 'Ticket' 'Fare' 'Cabin' 'Embarked']
PassengerId => 乘客ID
Pclass => 乘客等級(1/2/3等艙位)
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的特征可能包含錯誤或者打字錯誤,因為會有好幾種方法來描述名字
#默認倒數5行 train_df.tail()
6. 哪些特征包含空格,null或者空值
這些空格,null值或者空值很可能需要修正。
在這個問題中:
- 這些特征包含null值的數量大小為:Cabin > Age > Embarked
- 在訓練集里有不完整數據的數量的大小為:Cabin > Age
7.每個特征下的數據類型是什么?
這個可以在我們做數據轉換時起到較大的幫助。
在這個問題中:
- 有7個特征是int型或float 型。在測試數據集里有6個
- 有5個特征是string(object)類型
train_df.info()
test_df.info()
8. 在樣本里,數值型特征的數值分布是什么樣的?
這個可以幫助我們初步了解:訓練數據集如何體現了實際問題。
在這個問題中:
- 一共有891個樣本
- Survived的標簽是通過0或1來區分
- 大概38%的樣本是survived
- 大多數乘客(>76%)沒有與父母或是孩子一起旅行
- 大約30%的乘客有親屬和/或配偶一起登船
- 票價的差別非常大,少量的乘客(<1%)付了高達$512的費用
- 很少的乘客(<1%)年紀在64-80之間
我們可以通過以下方式獲取上述信息:
train_df.describe()
# 通過使用 percentiles=[.61, .62] 來查看數據集可以了解到生存率為 38%
train_df.describe(percentiles=[.61, .62])
# 通過使用 percentiles=[.76, .77] 來查看Parch的分布 train_df.describe(percentiles=[.76, .77])
# 通過使用 percentile=[.68, .69] 來查看SibSp的分布 train_df.describe(percentiles=[.68, .69])
#通過使用 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])
8. 在樣本里,離散型數據的分布是什么?
在這個問題中:
- 各個乘客的Name 屬性完全是唯一的(count=unique=891)
- Sex特征里65%為男性(top=male,fre=577/count=891)
- Cabin的count與unique並不相等,即說明有些乘客會共享一個cabin
- Embarked一共有種取值,其中從S港口登船的人最多
- Ticket的特征下,有22%左右的重復值(unique=681)
可以通過以下方法獲得以上信息:
train_df.describe(include=['O'])
(7)基於以上數據分析后的假設
根據以上的數據分析步驟后,我們可以暫時得出以下假設。當然,我們也可以在之后驗證這些假設。
相互關系:
我們想知道每個特征與Survival的相關性如何。我們希望能夠今早的做這一步,並且將這些相關性特征匹配到建模后的相關性特征上。
補全數據:
- 我們可能會去補全Age特征下的數據,因為它一定是與存活率是相關的
- 我們可能會去補全Embarked特征下的數據,因為它可能與存活率或者其他重要的特征之間存在相關性
修正數據:
- Ticket特征可能需要從我們的分析中丟棄,因為它的數值重復率高達22%,並且Ticket與survival之間很可能並沒有聯系
- Cabin特征可能也需要丟棄,因為它的數值非常不完整,並且在訓練集以及測試集里均包含較多的null值
- PassengerId特征可能也需要被丟棄,因為它對survival沒任何作用
- Name特征相對來說不是特別規范,並且很有可能與survival之間沒有直接聯系,所以可能也應該被丟棄
創造數據:
- 我們可以根據Parch和SibSp的特征來創建一個新的Family特征,以此得到每個乘客有多少家庭成員登了船
- 我們可以對Name特征做進一步加工,提取出名字里的Title作為一個新的特征
- 我們可以為Age特征創建一個新的特征,將它原本的連續型數值特征轉換為有序的離散型特征
- 我們也可以創建一個票價(Fare)范圍的特征,如果它對我們的分析有幫助的話
分類:
根據之前的問題描述或者已有的數據,我們也可以提出以下假設:
- 女人(Sex=female)更有可能存活
- 孩子(Age<?)也更有可能存活
- 上等倉的乘客(Pclass=1)有更大的存活率
(8)通過轉換部分特征后的分析
為了驗證之前的觀察與假設,我們可以通過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)
train_df[['Sex', 'Survived']].groupby(['Sex'], as_index=False).mean().sort_values(by='Survived', ascending=False)
train_df[['SibSp', 'Survived']].groupby(['SibSp'], as_index=False).mean().sort_values(by='Survived', ascending=False)
train_df[['Parch', 'Survived']].groupby(['Parch'], as_index=False).mean().sort_values(by='Survived', ascending=False)
(9)通過將數據可視化進行分析
現在我們可以通過將數據可視化對數據做進一步分析,並繼續驗證我們之前的假設是否正確
數值型特征與Survived之間的聯系:
柱狀圖在用於分析連續型的數值特征時非常有用,如特征Age,它的柱狀圖數值范圍(不同的年齡范圍)可以幫助我們識別一些有用的模式。
通過使用默認或自定的數值范圍(年齡范圍),柱狀圖可以幫助我們描繪出樣本所遵循的分布。
它可以幫助我們發現是否某些特定的年齡范圍(如嬰兒)有更高的存活率。
我們可以通過以下代碼來畫出Age的柱狀圖:
g = sns.FacetGrid(train_df, col='Survived') g.map(plt.hist, 'Age', bins=20) plt.show()
觀察:
- 嬰兒(Age<=4)有較高的生存率(20個bin,每個bin為4歲)
- 老人(Age=80)全部生還
- 大量的15-25年紀的乘客沒有生還
- 乘客主要在15-35的年紀范圍內
結論:
以上簡單的分析驗證了我們之前的假設:
- 我們需要將Age考慮到訓練模型里
- 為Age特征補全null值
- 我們應該划分不同的年齡層
數值型與序列型特征之間的聯系:
我們可以將多個特征組合,然后通過一個簡單的圖來識別它們之間的關系。這種方法可以應用在數值型以及分類型(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()
觀察:
- Pclass=3 有着最多的乘客,但是他們大多數卻沒有存活。這也驗證了我們之前在“分類”里的假設
- 在Pclass=2和Pclass=3中,大多數嬰兒活了下來,進一步驗證了我們之前在“分類”里的假設
- 大多數Pclass=1的乘客存活,驗證我們之前在“分類”里的假設
- 不同Pclass中Age的分布不同
結論:
考慮將Pclass特征加入模型訓練
離散型特征與Survived之間的聯系:
現在我們可以查看離散型特征與survived之間的關系
我們可以通過以下方式將數據可視化:
grid = sns.FacetGrid(train_df, row='Embarked', size=2.2, aspect=1.6) grid.map(sns.pointplot, 'Pclass', 'Survived', 'Sex',order=[1,2,3],hue_order=train_df.Sex.unique(),palette='deep') grid.add_legend() plt.show() #原作者代碼沒有加入order、hue_order因此圖示會有錯誤,並得出了錯誤的結論,不過那個結論沒有應用到后續的特征選擇。。建議代碼完成后結果可執行但是會提示有可能引起錯誤提示的話,還是修改下代碼比較好
觀察:
- 女性乘客相對於男性乘客有着更高的存活率
- Embarked和Survived之間可能並沒有直接的聯系。
- 對於Pclass=3以及男性乘客來說,Embarked的港口不同會導致存活率的不同
結論:
- 將Sex特征加入訓練模型
- 補全Embarked特征下的數據並將此特征加入訓練模型
離散型特征與數值型特征之間的聯系:
我們可能也想找出離散型與數值型特征之間的關系。
我們可以考慮查看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', order=train_df.Sex.unique(),alpha=.5, ci=None) grid.add_legend() plt.show()
觀察:
- 1. 付了高票價的乘客有着更高的生存率,驗證了我們之前的假設
- 2. Embarked與生存率相關,驗證了我們之前所做的假設
結論:
- 1. 考慮將Fare特征做不同的區間
(10)加工數據
我們根據數據集以及題目的要求已經收集了一些假設與結論。到現在為止,我們暫時還沒有對任何特征或數據進行處理。
接下來我們會根據之前做的假設與結論,以“修正數據”、“創造數據”以及“補全數據”為目標,對數據進行處理。
通過丟棄特征來修正數據:
這個步驟比較好的一個開始。通過丟棄某些特征,可以讓我們處理更少的數據點,並讓分析更簡單。
根據我們之前的假設和結論,我
們希望丟棄Cabin和Ticket這兩個特征。
在這里需要注意的是,為了保持數據的一致,我們需要同時將訓練集與測試集里的這兩個特征均丟棄。
具體步驟如下:
print("Before", train_df.shape, test_df.shape, combine[0].shape, combine[1].shape)
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)
通過已有的特征創建新特征:
我們在丟棄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']) #西方姓名中間會加入稱呼,比如小男童會在名字中間加入Master,女性根據年齡段及婚姻狀況不同也會使用Miss 或 Mrs 等,這算是基於業務的理解做的衍生特征,原作者應該是考慮可以用作區分人的特征因此在此嘗試清洗數據后加入
我們可以使用高一些更常見的名字或“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轉換為有序的數值型:
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
train_df.head()
train_df[['Title', 'Survived']].groupby(['Title'], as_index=False).mean()
新的發現:
當我們畫出Title,Age和Survived的圖后,我們有了以下新的發現:
- 大多數Title分段與年齡字段對應准確,比如,Title為Master平均年齡為5歲
- 不同組別Title與生產率有一定的區分度。
- 某些特定的title如Mme,Lady,Sir的乘客存活率較高,但某些title如Don,Rev,Jonkheer的乘客存活率不高
結論:
- 我們決定保留這個新的Title特征並加入到訓練模型
轉換一個離散型的特征
現在我們可以將一些包含字符串數據的特征轉換為數值型特征,因為在很多建模算法里,輸入的參數要求為數值型。
這個步驟可以讓我們達到補全數據的目標。
我們可以從轉換Sex特征開始,將female轉換為1,male轉換為0。我們可以將新的特征命名為Gender:
for dataset in combine: dataset['Sex'] = dataset['Sex'].map({'female':1, 'male':0}).astype(int)
train_df.head()
補全連續數值型特征
現在我們可以開始為那些含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()
現在我們對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)
然后我們根據上面的分段,使用有序的數值來替換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()
接着我們可以丟棄AgeBand特征:
train_df = train_df.drop(['AgeBand'], axis=1) combine = [train_df, test_df] train_df.head()
通過已有的特征組合出新特征
現在我們可以通過組合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)
接着我們可以創建另一個名為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()
基於上面的數據表現,我們現在可以丟棄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()
for dataset in combine: dataset['Age*Class'] = dataset.Age * dataset.Pclass train_df.loc[:, ['Age*Class', 'Age', 'Pclass']].head(10)
補全一個離散型的特征
Embarked特征主要有三個值,分別為S,Q,C,對應了三個登船港口。在訓練集里,這個有2個缺失值,我們會使用頻率最高的值來填充這個缺失值。
freq_port = train_df.Embarked.dropna().mode()[0]
freq_port
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特征轉換為數值型特征
for dataset in combine: dataset['Embarked'] = dataset['Embarked'].map({'S': 0, 'C': 1, 'Q': 2}).astype(int) train_df.head()
補全數值型特征
現在我們可以開始為測試集里的Fare特征補全數據。在補全時,我們可以使用最頻繁出現的數據用於補全缺失值。
(我們也可以將Fare的數值做四舍五入,將它精確到2位)
test_df['Fare'].fillna(test_df['Fare'].dropna().median(), inplace=True) test_df.head()
接下來我們將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,將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與其他特征之間的相關性
corrmat = train_df.corr() k = 10 cols = corrmat.nlargest(k,'Survived')['Survived'].index #取出與Survived相關性最大的十項 cm = np.corrcoef(train_df[cols].values.T) #相關系數 sns.set(font_scale = 1.25) hm = sns.heatmap(cm,cbar = True,annot = True,square = True ,fmt = '.2f',annot_kws = {'size': 10},yticklabels = cols.values,xticklabels = cols.values) plt.show()
(11)建模,預測,並解決問題
現在我們已經做好了訓練模型的准備,在模型訓練完后,我們即可將其應用到解決問題中。對於預測的問題,我們至少有60多種算法可供選擇。
所以我們必須理解問題的類型和解決方案的需求,這樣才能縮小模型的選擇范圍。現在這個問題是一個分類與回歸的問題,
我們希望找出輸出(即Survived)與其他特征(即Gender,Age,Port等)之間的關系。因為給定了訓練集,所以這在機器學習里是一個有監督學習。
所以現在對算法的需求是:有監督學習加上分類與回歸。根據這個條件,我們有以下模型可供選擇:
- Logistic Regression
- kNN
- SVM
- Naïve Bayes classifier
- Decision Tree
- Random Forrest
- Perceptron
- Artificial neural network
- RVM or Relevance Vector Machine
現在我們將訓練集與測試集再做一下區分:
X_train = train_df.drop('Survived', axis=1) Y_train = train_df['Survived'] X_test = test_df.drop('PassengerId', axis=1).copy() X_train.shape, Y_train.shape, X_test.shape
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.36
我們可以用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)
從上面的結果我們可以看出:
- Sex是有最高正系數的特征。這個表面當Sex 的值增加時(從male:0到female:1),Survived=1的概率增加最多
- 相反的,當Pclass增加時,Survived=1的概率減少最多
- 從結果來看,我們創建的新特征Age*Class非常有用,因為它與Survived的負相關性是第二高的
- Title是第二高的正系數特征
下一步我們使用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.84
可以看到使用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) acc_knn #84.74
可以看到使用KNN的正確率比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) acc_gaussian #72.28
看來在這個問題中使用朴素貝葉斯不是一個很好的選擇,從當前來看,它的正確率是最低的。
接下來我們試試 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) acc_perceptron #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) acc_linear_svc #79.01
與隨機梯度下降分類器:
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) acc_sgd #69.92
接下來我們看看很常見的決策樹算法:
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) acc_decision_tree #86.76
可以看到,使用決策樹的算法使得正確率達到了一個更高的值。在目前為止,它的正確率是最高的。
然后我們看看隨機森林,隨機森林通過組合多個決策樹算法來完成:
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) acc_random_forest #86.76
通過比較模型的正確率,我們決定使用最高正確率的模型,即隨機森林的輸出作為結果提交。
(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)
其中決策樹與隨機森林的正確率最高,但是我們在這里會選擇隨機森林算法,因為它相對於決策樹來說,彌補了決策樹有可能過擬合的問題。
最后我們做提交:
submission = pd.DataFrame({"PassengerId": test_df["PassengerId"], "Survived": Y_pred})
參考博文
http://www.cnblogs.com/zackstang/p/8185531.html