Kaggle入門級賽題:泰坦尼克號生還者預測——數據分析篇

本次分享的項目來自 Kaggle 的經典賽題:泰坦尼克號生還者預測。分爲數據分析和數據挖掘兩部分介紹。本篇爲數據分析篇。html


賽題解讀

比賽概述

RMS 泰坦尼克號的沉沒是歷史上最爲人熟知的海難事件之一。 1912 年 4 月 15 日,在她的處女航中,泰坦尼克號在與冰山相撞後沉沒,在船上的 2224 名乘客和機組人員中,共形成 1502 人死亡。這場聳人聽聞的悲劇震驚了國際社會,從而促進了船舶安全規定的完善。python

形成海難失事的緣由之一是乘客和機組人員沒有足夠的救生艇。儘管在沉船事件中倖存下有一些運氣因素,但有些人比其餘人更容易存活下來,好比女人,孩子和上流社會。安全

在這個挑戰中,要求完成哪些人可能存活下來的分析。特別的,要求運用機器學習工具來預測哪些乘客可以倖免於悲劇。app

所需技巧

最終目標

  • 預測一名乘客是否可以在泰坦尼克號沉沒事件中倖存。
  • 對測試集中的每個PassengerId,將其相應的Survived變量預測爲值 0 或 1(這裏 1 表示倖存,0 表示遇難)。

數據分析

數據描述

首先,導入數據:機器學習

data_train = pd.read_csv('train.csv')
data_test = pd.read_csv('test.csv')

數據須要進行轉換可以做爲模型輸入。咱們對訓練集data_train進行變換,一樣須要對測試集data_test進行變換,保證模型輸入的一致性。工具

合併在一塊兒能夠進行統一變換,變換後再拆分。也可以使用以前沒有通過變換的data_train。對數據集進行以下處理:學習

# 將目標變量 Survived 單獨提取,從特徵變量中移除目標變量 Survived
df_target = data_train['Survived']
data_train_feature = data_train.drop(['Survived'], axis=1)

# 合併 train 和 test 數據集,以方便數據處理對兩個數據集同時進行
df = data_train_feature.append(data_test)

合併後的數據集以下圖所示:測試

clipboard.png

咱們看到,共有 11 個特徵:spa

  • PassengerId: 乘客 ID
  • Pclass: 艙位等級 (1 = 1st, 2 = 2nd, 3 = 3rd)
  • Name: 乘客姓名
  • Sex: 性別
  • Age: 年齡
  • SibSp: 在船上的兄弟姐妹/配偶個數
  • Parch: 在船上的父母/小孩個數
  • Ticket: 船票信息
  • Fare: 票價
  • Cabin: 客艙
  • Embarked: 登船港口 (C = Cherbourg, Q = Queenstown, S = Southampton)

以上就是對比賽和數據意義的解讀,下面咱們對數據進行探索,來達到最終目標。3d

數據預處理

特徵分析


咱們已經在上一步查看了有哪些特徵,接下來要作的是:

  • 查看哪些特徵存在缺失值。
  • 判斷哪些特徵存在異常值,經過對數值型特徵進行簡單的描述性統計。
  • 判斷這些特徵的數據類型,從而爲後面的可視化分析找到合適的方法。
  • 對數據進行可視化分析。
  • 分析得出有用的結論。

1. 查看哪些特徵存在缺失值
查看 traintest 合併後的數據:

df.info()

clipboard.png

查看哪些特徵存在缺失值,缺失程度是否嚴重:

print(pd.isnull(df).sum())

clipboard.png

能夠看出 agecabinembarkedFare 四個特徵有缺失值,其中 cabin 的值缺失較嚴重。


2. 判斷哪些特徵存在異常值

對數值型特徵進行簡單的描述性統計,包括均值,中位數,標準差,最大值,最小值等,從而判斷哪些特徵存在異常值。

df.describe()

clipboard.png

觀察上述值,其中 Age 的最小值爲 0.17,表示的應該是嬰兒的年齡,最大值爲 80,年齡有些偏大。Fare 表示船票價格,它的平均值爲 33.2,中位數 14,平均值比中位數大不少,說明該特徵分佈是嚴重的右偏,又有最大值約 512,因此這個值極可能是一個異常值。在 SibSpParch 中,Sibsp 最大值爲 8,有多是異常值,但 Parch 最大值也爲 9。這兩個特徵同時出現相近的較大的數值,而又由兩者所表示的含義,說明這個數值是有可能的,須要進一步的觀察。

由上所述,咱們看到了一些可能的異常值,但還不能肯定。須要咱們進一步經過可視化來清楚的顯示,並結合對業務的理解來肯定。


3. 判斷特徵的數據類型

  • 定類:Name, Sex, Ticket, Embarked, Cabin
  • 定序:Pclass
  • 定比:Age, Fare, SibSp, Parch

根據以上對各個特徵數據類型的判斷,選擇合適的可視化方法完成可視化。經過可視化能夠:

  • 能夠發現事實問題,並尋找出現的緣由。
  • 更清晰的瞭解特徵對目標變量的影響,有助於特徵工程。
  • 能夠發現不易發現的特徵異常值。

4. 數據可視化分析
首先,定製畫布風格:

plt.style.use("bmh")

解決中文亂碼問題:

plt.rcParams['font.sans-serif'] = ['SimHei']
# 或者用 plt.rc('font', family='SimHei', size=13)

定類 / 定序特徵分析

cat_list = ['Pclass','Name','Sex','Embarked','Ticket','Cabin']
for n,i in enumerate(cat_list):  
    Cabin_cat_num = df[i].value_counts().index.shape[0]
    print('{0}. {1}特徵的類型數量是: {2}'.format(n+1,i,Cabin_cat_num))

clipboard.png

在上面各特徵值的類型中,一些比較少數量的特徵如 PclassSexEmbarked 等可進行可視化分析。剩下特徵如 TicketCabin 分類較多,進行可視化分析達不到想要效果,難以獲得有用的結論。


先對上面 3 種容易的分類進的特徵行可視化,而對於 NameTicketCabin 等到後續進行進一步分析。

f, [ax1,ax2,ax3] = plt.subplots(1,3,figsize=(20,5))
sns.countplot(x='Sex', hue='Survived', data=data_train, ax=ax1)
sns.countplot(x='Pclass', hue='Survived', data=data_train, ax=ax2)
sns.countplot(x='Embarked', hue='Survived', data=data_train, ax=ax3)
ax1.set_title('Sex特徵分析')
ax2.set_title('Pclass特徵分析')
ax3.set_title('Embarked特徵分析')
f.suptitle('定類/定序數據類型特徵分析', size=20, y=1.1)

plt.show()

clipboard.png

經過分別觀察各特徵值的分佈狀況和與目標變量之間的關係,得出如下結論:

  • Sex:咱們從 Sex 的特徵分析圖能夠清晰的看,男性總人數大於女性總人數,但女性的存活率遠遠高於男性。
  • Pclass:3 等艙的存活率明顯比 1 等艙和 2 等艙的低不少,這是因爲 3 等艙的多爲普通市民,而等級越高的艙位越有多是當時社會地位較高的人。
  • Embarked:登錄港口 S 的數量最多,但獲救率較低,而 C 港和 Q 港基本都有一半的人獲救。

其中,SexPclass 兩個特徵影響力較大。


以上是針對單獨特徵對生還與否的簡單分析,但在實際問題中,每每是由多個因素共同決定對目標變量的影響。所以,咱們須要知道在某個特定條件下的特徵的影響(如 Pclass 是 1 的狀況下男性和女性生還機率有何不一樣)才更加能幫助咱們分析,即在數據集的子集內可視化變量的分佈或多個變量之間的關係。這時就須要用到FacetGrid子集數據。

在不一樣社會等級下,男性和女性在不一樣登錄港口下的數量對比:

grid = sns.FacetGrid(df, col='Pclass', hue='Sex', palette='seismic', size=4)
grid.map(sns.countplot, 'Embarked', alpha=0.8)
grid.add_legend()

clipboard.png

觀察獲得:

  • 在 Q 港口登錄的乘客中,3 等艙的乘客佔了絕大多數,而其餘兩個艙位幾乎沒有乘客在該港口登錄,說明登錄港口可能與社會等級有關聯。
  • 在 C 港口登錄的乘客中,女性乘客佔大多數,這也間接的說明在 C 港口生還率很大。
  • 在 S 港口登錄的乘客中,3 等艙的乘客最多,且總的來講男性乘客佔比也最多,所以可推測 S 港口生還率較小。

定距/定比特徵分析


Age 特徵的 kde 分佈:

f,ax = plt.subplots(figsize=(10,5))
sns.kdeplot(data_train.loc[(data_train['Survived'] == 0),'Age'] , color='gray',shade=True,label='not survived')
sns.kdeplot(data_train.loc[(data_train['Survived'] == 1),'Age'] , color='g',shade=True, label='survived')
plt.title('Age特徵分佈', fontsize = 15)
plt.xlabel("Age", fontsize = 15)
plt.ylabel('Frequency', fontsize = 15)

clipboard.png

根據 Age 特徵獲得不一樣性別生還與否的分佈:

def plot_distribution( df , var , target , **kwargs ):
    row = kwargs.get( 'row' , None )
    col = kwargs.get( 'col' , None )
    facet = sns.FacetGrid( df , hue=target , aspect=4 , row = row , col = col )
    facet.map( sns.kdeplot , var , shade= True )
    facet.set( xlim=( 0 , df[ var ].max() ) )
    facet.add_legend()
plot_distribution( data_train , var = 'Age' , target = 'Survived' , row = 'Sex' )

clipboard.png

總體觀察獲得,0 到十幾歲的孩子生還率最高,20 歲到 30 歲左右的生還率較低,其餘年齡段沒有太大的區別。而對於男性來講,0 到十幾歲的乘客生還率明顯較高,20 歲到 30 歲左右的生還率較低;而對女性來講,反而是 30 歲到 40 歲的年齡段生還率相對較高,0 到十幾歲生還率並不高,其它各年齡段的生還率沒有較大差異。


Fare 特徵:

# 填充缺失值
data_test["Fare"].fillna(data_test["Fare"].median(), inplace=True)

data_train['Fare'] = data_train['Fare'].astype(int)
data_test['Fare'] = data_test['Fare'].astype(int)

# 分別得到生還和遇難乘客的 Fare
fare_not_survived = data_train["Fare"][data_train["Survived"] == 0]
fare_survived = data_train["Fare"][data_train["Survived"] == 1]

# 獲得 Fare 的均值和方差
avgerage_fare = pd.DataFrame([fare_not_survived.mean(), fare_survived.mean()])
std_fare = pd.DataFrame([fare_not_survived.std(), fare_survived.std()])

data_train['Fare'].plot(kind='hist', figsize=(15,3),bins=100, xlim=(0,50))

avgerage_fare.index.names = std_fare.index.names = ["Survived"]
avgerage_fare.plot(yerr=std_fare,kind='bar',legend=False)

clipboard.png
clipboard.png

容易觀察獲得 Fare 低的數量多,而 Fare 高的數量少,但生還率明顯要比 Fare 低的高不少。這也符合實際狀況,買得起高票價的有錢人居多,社會地位可能相對較高,更容易獲救。


SibSpParch 特徵:

data_train['Family'] =  data_train["Parch"] + data_train["SibSp"]
data_train['Family'].loc[data_train['Family'] > 0] = 1
data_train['Family'].loc[data_train['Family'] == 0] = 0

data_test['Family'] =  data_test["Parch"] + data_test["SibSp"]
data_test['Family'].loc[data_test['Family'] > 0] = 1
data_test['Family'].loc[data_test['Family'] == 0] = 0

# 刪除 Parch 和 SibSp
data_train = data_train.drop(['SibSp','Parch'], axis=1)
data_test = data_test.drop(['SibSp','Parch'], axis=1)

# 繪圖
fig, (axis1,axis2) = plt.subplots(1,2,sharex=True,figsize=(10,5))

sns.countplot(x='Family', data=data_train, order=[1,0], ax=axis1)

# 分爲和家人一塊兒、獨自乘船兩種狀況
family_perc = data_train[["Family", "Survived"]].groupby(['Family'],as_index=False).mean()
sns.barplot(x='Family', y='Survived', data=family_perc, order=[1,0], ax=axis2)

axis1.set_xticklabels(["With Family","Alone"], rotation=0)

clipboard.png

咱們將 SibSpParch 這兩個特徵合併爲了一個 Family 特徵,表示該乘客是否有和家人一塊兒乘船,來判斷是否會增長生還率。

能夠明顯的看出獨自乘船的乘客人數大於和家人一塊兒的乘客人數,但倖存人數相對而言較少。


5. 總結

將上述 6 個特徵的相互關聯圖進行彙總,對角線爲特徵自身的 kde 分佈,以下圖所示:

g = sns.pairplot(data_train[[u'Survived', u'Pclass', u'Sex', u'Age', u'Family', u'Fare', u'Embarked']], hue='Survived', palette = 'seismic',
                 size=4,diag_kind = 'kde',diag_kws=dict(shade=True),plot_kws=dict(s=50) )
g.set(xticklabels=[])

clipboard.png


數據分析部分就是這樣了,根據我的的不一樣理解還能夠有不少種不一樣的可視化方法,但最終目標都是爲了幫助咱們理解數據,進行挖掘分析。關於特徵工程和建模的部分將在下一篇的數據挖掘中來介紹。


參考連接:
【Kaggle入門級競賽top5%排名經驗分享】— 分析篇
樣式美化matplotlib.pyplot.style.use定製畫布風格
mac下python matplotlib中文亂碼解決方案
Seaborn(sns)官方文檔學習筆記(第六章 繪製數據網格)

不足之處,歡迎指正。

相關文章
相關標籤/搜索