泰坦尼克號之災分析
大神經驗:
1、應用機器學習,千萬不要一上來就試圖做到完美,先擼一個baseline的model出來,再進行後續的分析步驟,一步步提高,所謂後續步驟可能包括『分析model現在的狀態(欠/過擬合),分析我們使用的feature的作用大小,進行feature selection,以及我們模型下的bad case和產生的原因』等等。
2、
對數據的認識太重要了!
數據中的特殊點/離群點的分析和處理太重要了!
特征工程(feature engineering)太重要了!在很多Kaggle的場景下,甚至比model本身還要重要!
要做模型融合(model ensemble)!
3、
問題或問題的定義。
獲取訓練和測試數據。
Wrangle,準備,清理數據。
分析,識別模式並探索數據。
建模,預測和解決問題。
可視化,報告和呈現問題解決步驟和最終解決方案。
提供或提交結果。
工作流程
分類。我們可能希望對樣本進行分類。我們可能還想了解不同類的含義或相關性與我們的解決方案目標。
相關。可以基於訓練數據集中的可用特征來解決問題。數據集中的哪些功能對我們的解決方案目標有重大貢獻?從統計上講,功能和解決方案目標之間是否存在相關性?隨著特征值的變化,解決方案狀態也會發生變化,反之亦然?這可以針對給定數據集中的數字和分類特征進行測試。我們可能還希望確定除後續目標和工作流程階段的生存之外的特征之間的相關性。關聯某些功能可能有助於創建,完成或更正功能。
轉換。對於建模階段,需要準備數據。根據模型算法的選擇,可能需要將所有特征轉換為數值等效值。例如,將文本分類值轉換為數值。
填補。數據準備可能還需要我們估計要素中的任何缺失值。當沒有缺失值時,模型算法可能最有效。
糾正。我們還可以分析給定的訓練數據集中的錯誤或可能在特征內刪除值,並嘗試糾正這些值或排除包含錯誤的樣本。一種方法是檢測我們的樣本或特征中的任何異常值。如果某項功能無法進行分析,或者可能會嚴重影響結果,我們也可能會完全丟棄該功能。
創建。我們是否可以基於現有功能或一組功能創建新功能,以便新功能遵循關聯,轉換和完整性目標。
圖表。如何根據數據的性質和解決方案目標選擇正確的可視化圖表和圖表。
正式開始
介紹
數據集包含兩部分
- training set (train.csv)
- test set (test.csv)
具體內容
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 |
import相關包變量解釋
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.
# 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 %matplotlib inline # 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
導入數據,把訓練集和測試集合並來一起實施操作
train_df = pd.read_csv(‘../input/train.csv‘) test_df = pd.read_csv(‘../input/test.csv‘) combine = [train_df, test_df]
對數據進行分析,先看看有哪些特征列
print(train_df.columns.values)
out:
[‘PassengerId‘ ‘Survived‘ ‘Pclass‘ ‘Name‘ ‘Sex‘ ‘Age‘ ‘SibSp‘ ‘Parch‘
‘Ticket‘ ‘Fare‘ ‘Cabin‘ ‘Embarked‘]
哪些特征是 categorical,哪些是numerical的?
- 分類: Survived, Sex, and Embarked. 序列: Pclass.
- 連續: Age, Fare. 分離: SibSp, Parch.
整體觀看數據
# preview the data train_df.head()
哪些特征是混合數據類型的?
船票是數字和字母數字數據類型的混合。 船艙是字母和數字混合。
哪些特征是包含錯誤值或錯別字的?
名稱功能可能包含錯誤或拼寫錯誤,因為有多種方法可用於描述名稱,包括標題,圓括號和用於替代或短名稱的引號。
哪些特征包含blank, null or empty values?
- Cabin> Age> Embarked 按訓練數據集的順序包含許多空值。
- Cabin > Age 在測試集上是不完整的
各個特征的數據類型是什麽?
如訓練集上2個float,5個int,5個object
train_df.info() print(‘_‘*40) test_df.info()
out:
樣本的特征(數字類)的分布如何?分析一下:
這有助於我們了解早期的數據概貌。
- 實際乘客人數(891)是總樣本泰坦尼克號(2,224)上的40%。
- Survived是一個具有0或1值的分類特征。
- 約38%的樣本存活,代表實際存活率為32%。
- 大多數乘客(> 75%)沒有與父母或孩子一起旅行。
- 近30%的乘客有兄弟姐妹和/或配偶。
- 票價差異很大,很少有乘客(<1%)支付高達512美元。
- 年齡在65-80歲之間的老年乘客(<1%)很少。
train_df.describe() # Review survived rate using `percentiles=[.61, .62]` knowing our problem description mentions 38% survival rate. # Review Parch distribution using `percentiles=[.75, .8]` # SibSp distribution `[.68, .69]` # Age and Fare `[.1, .2, .3, .4, .5, .6, .7, .8, .9, .99]`
分析完數據型的特征,那分類型的特征的分布又是怎樣的?
- Names 在整個數據集中是唯一的(count = unique = 891)
- Sex 變量為兩個可能的值,男性為65%(top=男性,fre= 577 /計數= 891)。
- Cabin 的值在樣本中有幾個副本。 因為有幾名乘客共用一間cabin。
- Embarked 采取三個可能的值。 大多數乘客前往S港口(top= S)
- Ticket 特征具有高比率(22%)的重復值(unique= 681)。
train_df.describe(include=[‘O‘]) #參數O代表只列出object類型的列
out:
綜上分析一波:
基於數據分析的假設
我們基於迄今為止所做的數據分析得出以下假設。我們可能會在采取適當行動之前進一步驗證這些假設。
相關:
我們想知道每個特征與生存的相關性。我們希望在項目的早期階段完成這項工作,並將這些快速關聯與項目後期的建模關聯相匹配。
填補:
1.我們可能希望填補Age特征,因為它與生存明確相關。
2.我們可能希望填補Embarked特征,因為它也可能與生存或其他重要功能相關。
修正:
1. Ticket 特征可能會從我們的分析中刪除,因為它包含高比例的重復項(22%),並且Ticket和生存之間可能沒有相關性。
2. Cabin 特征可能會因為高度不完整而丟棄,因為在訓練和測試數據集中包含許多空值。
3. PassengerId 可能會從訓練數據集中刪除,因為它對生存沒有貢獻。
4. Names 特征相對不標準,可能無法直接促成生存,因此可能會丟掉。
創建:
1.我們可能想要創建一個名為Family的基於Parch和SibSp的新特征,以獲得船上家庭成員的總數。
2.我們可能想要創建Name 特征來將Title 提取為新特征。
3.我們可能想為Age列創建新特征。這將連續的數字特征轉換為序數分類特征。
4.如果有助於我們的分析,我們可能還想創建一個Fare 範圍特征。
分類
我們還可以根據前面提到的問題描述添加我們的假設。
1.女性更有可能幸存下來。
2.兒童(Age<?)更有可能存活下來。
3.上層乘客(Pclass = 1)更有可能幸存下來。
通過pivoting進行分析
為了確認我們的一些觀察和假設,我們可以通過相互pivote特征來快速分析我們的特征相關性。 我們只能在此階段對沒有任何空值的特征執行此操作。 對於分類(Sex),序數(Pclass)或離散(SibSp,Parch)類型的特征,這樣做也是有意義的。
- Pclass 我們觀察到 Pclass = 1 和 Survived(分類#3)之間存在顯著的相關性(> 0.5)。 我們決定在我們的模型中包含此功能。
- Sex 我們在問題定義中確認 Sex=female 的存活率非常高,為74%(分類#1)。
- SibSp和Parch 這些特征對於某些值具有零相關性。 最好從這些單獨的特征中導出一個特征或一組特征(創建#1)。
//as_index參數為false時輸出表格為SQL的風格
train_df[[‘Pclass‘, ‘Survived‘]].groupby([‘Pclass‘], as_index=False).mean().sort_values(by=‘Survived‘, ascending=False)
out:
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)
到現在為止,進行可視化數據來直觀分析
g = sns.FacetGrid(train_df, col=‘Survived‘) g.map(plt.hist, ‘Age‘, bins=20)
關聯數字和序數特征
我們可以使用單個圖組合多個功能來識別相關性。 這可以通過具有數值的數字和分類特征來完成。
觀察
- Pclass = 3有大多數乘客,但大部分都無法生存。 證實我們的分類假設#2。
- Pclass = 2和 Pclass = 3 的嬰兒乘客大部分幸存下來。 進一步證實了我們的分類假設#2。
- Pclass = 1的大多數乘客幸免於難。 證實我們的分類假設#3。
- Pclass在乘客年齡分布方面有所不同。
決定
- 考慮使用Pclass進行模型訓練。
#grid = sns.FacetGrid(train_df, col=‘Pclass‘, hue=‘Survived‘) 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();
關聯分類功能
現在我們可以將分類特征與我們的解決方案目標相關聯。
觀察
- 女性乘客的生存率遠高於男性。 證實分類(#1)。
- 在 Embarked = C 中的例外情況,其中男性的存活率更高。 這可能是 Pclass 和 Embarked 之間的相關性,反過來是 Pclass 和 Survived,不一定是 Embarked 和 Survived 之間的直接相關。
- 對 C 和 Q港口,男性在 Pclass = 3相比在Pclass = 2時的存活率更高。 證實填補(#2)。
- 對於Pclass = 3的男性,不同的登船港口他們的生存率各不相同。 證實相關(#1)。
決定
- 為模型訓練添加 Sex 特征。
- 填補並添加 Embarked 特征來進行模型訓練。
grid = sns.FacetGrid(train_df, col=‘Embarked‘) #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()
關聯分類和數字特征
我們可能還想關聯分類特征(使用非數字值)和數字特征。 我們可以考慮將Embarked(分類非數字),Sex(分類非數字),Ticket(數字連續)與 Survivied(分類數字)相關聯。
觀察
- 高票價乘客的生存率更高。 證實我們創建(#4)Fare 範圍的假設。
- 登船港與生存率相關。 證實相關(#1)和填補(#2)。
決定
- 考慮綁定票價功能。
grid = sns.FacetGrid(train_df, col=‘Embarked‘, hue=‘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()
Wrangle數據
我們收集了有關數據集和解決方案要求的若幹假設和決策。 到目前為止,我們沒有必要更改單個功能或值來實現這些功能。 現在讓我們執行我們的決策和假設,以糾正,創建和填補目標。
通過刪除功能進行更正
這是一個很好的起始目標。 通過刪除功能,我們處理的數據點更少。 加速我們的筆記本電腦並簡化分析。
根據我們的假設和決定,我們希望放棄 Cabin(糾正#2)和 Ticket(糾正#1)特征。
請註意,在適用的情況下,我們同時對訓練和測試數據集執行操作以保持一致。
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)
out:
Before (891, 12) (418, 11) (891, 12) (418, 11) After (891, 10) (418, 9) (891, 10) (418, 9)
從現有特征提取的新特征
在丟棄 Name 和 PassengerId 特征之前,我們想要分析是否可以設計 Name 特征來提取 titles(稱謂) 並測試 titles (稱謂)和 Survived 之間的相關性,然後再刪除 Name 和 PassengerId 功能。
在下面的代碼中,我們使用正則表達式提取標題功能。 RegEx模式 `(\w+\.)` 匹配名稱特征中以點字符結尾的第一個單詞。 `expand = False` 標誌返回一個DataFrame。
觀察
當我們繪制 Title(稱謂),Age和 Survived 時,我們註意到以下觀察結果。
- 大多數titles(稱謂)準確地圍繞年齡組。 例如:Master 稱謂的年齡均值為5年。
- Title 和 Age 列的生存率略有不同。
- 某些 titles稱謂 大多存活下來(Mme,Lady,Sir)或者沒有(Don,Rev,Jonkheer)。
決策
- 我們決定保留新的 Title(稱謂) 特征以進行模型訓練。
for dataset in combine: dataset[‘Title‘] = dataset.Name.str.extract(‘ ([A-Za-z]+)\.‘, expand=False) # 參數expand=false 標誌返回一個df pd.crosstab(train_df[‘Title‘], train_df[‘Sex‘])
我們可以用更常見的名稱替換許多稱謂或將它們歸類為 ‘Rare’。
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_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
out:
((891, 9), (418, 9))
轉換分類特征
現在我們可以將包含字符串的特征轉換為數值。 這是大多數模型算法所必需的。 這樣做也有助於我們實現功能完成目標。
讓我們首先將性別特征轉換為名為Gender的新功能,其中女性= 1,男性= 0。
for dataset in combine: dataset[‘Sex‘] = dataset[‘Sex‘].map( {‘female‘: 1, ‘male‘: 0} ).astype(int) train_df.head()
完成數字連續特征
現在我們應該開始估計和填補缺少值或空值的功能。我們將首先針對Age功能執行此操作。
我們可以考慮三種方法來填補數值連續特征。
1. 一種簡單的方法是在均值和標準偏差之間生成隨機數。
2. 更準確地猜測缺失值的方法是使用其他相關特征。在我們的例子中,我們註意到Age,Gender和Pclass之間的相關性。使用 中位數 的值來猜測年齡值,包括 Pclass 和 Gender 特征組合的集合。因此,年齡中位數對於 Pclass = 1且 Gender = 0,對於Pclass = 1且 Gender = 1,依此類推......
3. 結合方法1和2。因此,不是基於中位數來猜測年齡值,而是根據Pclass和Gender組合的集合使用均值和標準差之間的隨機數。
方法1和3將隨機噪聲引入我們的模型。多次執行的結果可能會有所不同。我們更喜歡方法2。
#grid = sns.FacetGrid(train_df, col=‘Pclass‘, hue=‘Gender‘) 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()
讓我們首先準備一個空數組,以包含基於Pclass 和 Gender組合的Age猜測值。
然後我們叠代Sex(0或1)和Pclass(1,2,3)來計算六種組合的Age的猜測值。
guess_ages = np.zeros((2,3))
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_mean = guess_df.mean() # age_std = guess_df.std() # age_guess = rnd.uniform(age_mean - age_std, age_mean + age_std) 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 bands 並確定與 Survived 的相關性。
train_df[‘AgeBand‘] = pd.cut(train_df[‘Age‘], 5) #把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()
基於現有的特征來創建新的特征
我們可以為 FamilySize 創建一個新特征,它結合了 Parch 和 SibSp 。 這將使我們能夠從數據集中刪除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()
我們還可以創建一個結合了Pclass和Age的人工特征。
for dataset in combine: dataset[‘Age*Class‘] = dataset.Age * dataset.Pclass train_df.loc[:, [‘Age*Class‘, ‘Age‘, ‘Pclass‘]].head(10)
完成分類功能
Embarked 特征根據 登船港口獲取S,Q,C值。 我們的訓練數據集有兩個缺失值。 我們只是填寫最常見的事件。
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)
### Converting categorical feature to numeric
We can now convert the EmbarkedFill feature by creating a new numeric Port feature.
泰坦尼克號之災分析