泰坦尼克號資料集,是kaggle(Titanic: Machine Learning from Disaster)上入門機器學習(ML)的一個好的可選資料集,當然,也是不錯的練習資料分析的資料集。對 python ,在資料分析方面,作為一柄利器,涵蓋了「資料獲取→資料處理→資料分析→資料視覺化」這個流程中每個環節, 這風騷的操作,也是沒誰了。
這個專案做下來,除了沒有涉及到資料抓取(python爬蟲)外,基本上把python 資料處理分析的各個版塊都做了一個完整的貫穿。對此進行歸納總結,算是倒逼自己對所接觸到的知識,進行結構化的梳理和輸出。
探索的問題
主要探尋坦尼克號上的生還率和各因素(客艙等級、年齡、性別、上船港口等)的關係。
獲取資料
我把原始資料 titanic-data.csv 放在和 notebook 檔案同一目錄下,然後通過read_csv 來載入檔案,當然在開始載入資料前,我必須按照需求將需要用到的 Python 包匯入進來。
# 用於資料分析
import pandas as pd
import numpy as np
# 用於繪圖
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
# 讀取前五行資料
data_t = pd.read_csv(`titanic-data.csv`)
data_t.head()
複製程式碼
PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 3 | Braund, Mr. Owen Harris | male | 22.0 | 1 | 0 | A/5 21171 | 7.2500 | NaN | S |
1 | 2 | 1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Th… | female | 38.0 | 1 | 0 | PC 17599 | 71.2833 | C85 | C |
2 | 3 | 1 | 3 | Heikkinen, Miss. Laina | female | 26.0 | 0 | 0 | STON/O2. 3101282 | 7.9250 | NaN | S |
3 | 4 | 1 | 1 | Futrelle, Mrs. Jacques Heath (Lily May Peel) | female | 35.0 | 1 | 0 | 113803 | 53.1000 | C123 | S |
4 | 5 | 0 | 3 | Allen, Mr. William Henry | male | 35.0 | 0 | 0 | 373450 | 8.0500 | NaN | S |
# 資料集資訊,包含資料集大小,列名,型別
data_t.info()
data_t.columns.values
複製程式碼
<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
array([`PassengerId`, `Survived`, `Pclass`, `Name`, `Sex`, `Age`, `SibSp`,
`Parch`, `Ticket`, `Fare`, `Cabin`, `Embarked`], dtype=object)
複製程式碼
資料觀察
-
載入 titanic-data.csv 到一個 DataFrame ,然後用 head() 函式列印出前5行資料(p.s 用 tail() 函式可以列印出後5行)。
-
通過對資料的初步觀測,這個資料樣本一共有 891 行 * 12 列資料,欄位包含:
`PassengerId(乘客id)`, `Survived(是否活下來)`, `Pclass(船艙等級)`, `Name(姓名)`, `Sex(性別)`, `Age(年齡)`, `SibSp(兄弟姐妹同行數量)`,`Parch(父母配偶同行數量)`, `Ticket(票)`, `Fare(費)`, `Cabin(船艙)`, `Embarked(上船站)` -
其中, 定類變數 包括 Survived,Sex,Embarked, 定序變數 包括 Pclass, 數字變數 包括 PassengerId,Age,SibSp,Parch,Fare
-
通過觀測發現,Age、Cabin、Embarked 包含了有空值
# 欄位分析
def y(x):
return data_t[x].unique()
print(`=`*20 + `Survived欄位內容` + `=`*20)
print(y(`Survived`))
print(`=`*20 + `Sex欄位內容` + `=`*20)
print(y(`Sex`))
print(`=`*20 + `Pclass欄位內容` + `=`*20)
print(y(`Pclass`))
print(`=`*20 + `Embarked欄位內容` + `=`*20)
print(y(`Embarked`))
複製程式碼
====================Survived欄位內容====================
[0 1]
====================Sex欄位內容====================
[`male` `female`]
====================Pclass欄位內容====================
[3 1 2]
====================Embarked欄位內容====================
[`S` `C` `Q` nan]
複製程式碼
變數的值
- Survived 的值:0(死亡),1(存活)
- Sex 的值:male(男性),female(女性)
- Embarked的值包含 `S` `C` `Q`
# 顯示重複的資料數量
data_t.duplicated().value_counts()
複製程式碼
False 891
dtype: int64
複製程式碼
重複資料
資料集一共有 891 行資料,不重複。
# 顯示有空值的列
print(data_t[`Age`].isnull().value_counts())
print(`-`*50)
print(data_t[`Cabin`].isnull().value_counts())
print(`-`*50)
print(data_t[`Embarked`].isnull().value_counts())
print(`-`*50)
複製程式碼
False 714
True 177
Name: Age, dtype: int64
--------------------------------------------------
True 687
False 204
Name: Cabin, dtype: int64
--------------------------------------------------
False 889
True 2
Name: Embarked, dtype: int64
--------------------------------------------------
複製程式碼
空值情況
- Age 一共有 714 行空資料
- Cabin(船艙)一共有 204 行空資料
- Embarked(上船站)一共有 2 行空資料。
# 描述性分析
data_t.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 |
描述性統計
- 在這次旅行的 891 名乘客中,有 38% 的人活了下來,幸運兒。
- 所有旅客中,年齡最小的只有 0.4 歲,最大的有 80 歲,平均年齡在 28 歲左右。
- 平均每個乘客有 0.52 個兄弟姐妹陪同,有 0.38 個父母配偶陪同。
- 有些乘客居然有 8 名同行的人。
- 旅客為這趟旅行平均花費 32 美元,最高花費 512 美元(貴族吧)
資料清洗(cleanse the data)
題外話
據說資料清洗這一塊在實際業務中大概佔有 80% 的時間,可真是苦逼。
缺失值處理中,我們一般會刪除缺失值。pandas模組中,提供了將包含NaN值的行刪除的方法dropna(),但其實處理缺失值最好的思路是用最接近的資料替換
首先,清洗資料就是處理空值,讓這些空值參與到之後的資料分析中去。其次,我將刪除那些對於資料分析本身並沒有相關性的資料列,比如Cabin(因為一個船艙號對於是否能夠逃生確實沒有任何影響)。最後,我會觀察資料集,看看是否可以創造出一些新的特性,讓我們的分析能夠更直觀快捷。
# 處理空值
data_t[`Age`] = data_t[`Age`].fillna(data_t[`Age`].mean()).astype(np.int64)
data_t[`Embarked`] = data_t[`Embarked`].fillna({"Embarked":"S"},inplace=True)
# 刪除無關的列
data_t = data_t.drop([`Ticket`,`Cabin`],axis=`columns`)
data_t.info()
複製程式碼
<class `pandas.core.frame.DataFrame`>
RangeIndex: 891 entries, 0 to 890
Data columns (total 10 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 891 non-null int64
SibSp 891 non-null int64
Parch 891 non-null int64
Fare 891 non-null float64
Embarked 0 non-null object
dtypes: float64(1), int64(6), object(3)
memory usage: 69.7+ KB
複製程式碼
處理空值和多餘的值
上面用年齡的平均數來代替空值,因為 `S` 出現的頻數最多,咖位最高,所以用 `S` 代替空值。
我刪除掉了 `Ticket`,`Cabin` 兩列資料,實際上這兩列資料對於我們分析資料並沒有太多用處。
資料視覺化分析
資料透視表是 Excel 中最常用的資料彙總分析工具,它可以根據一個或多個制定的維度對資料進行聚合,探索資料內深層次的資訊。
在 pandas 中,同樣提供了pandas.pivot_table 函式來實現這些功能。在接下來的分析中,我們會多次用到這個函式,所以先來熟悉下下這個函式:
pandas.pivot_table 函式中包含四個主要的變數,以及一些可選擇使用的引數。四個主要的變數分別是資料來源 data,行索引 index,列 columns,和數值 values。可選擇使用的引數包括數值的彙總方式,NaN值的處理方式,以及是否顯示彙總行資料等。
基本情況分析
我們先來看下基本情況:891人當中,生還比率與未生還比率是多少?
total_survived = data_t[`Survived`].sum()
total_no_survived = 891 - total_survived
plt.figure(figsize = (10,5)) # 建立畫布
plt.subplot(121) # 新增第一個子圖
sns.countplot(x=`Survived`,data=data_t)
plt.title(`Survived count`)
plt.subplot(122) # 新增第二個子圖
plt.pie([total_survived,total_no_survived],labels=[`Survived`,`No survived`],autopct=`%1.0f%%`)
plt.title(`Survived rate`)
plt.show()
複製程式碼
結論:這891名乘客中,生還和未生還的比率分別為 38% 和 62%。
分別探索下 Pclass、Sex、Age 和 Embarked 等與“生還率”的關係.
艙位(Pclass)與生還率關係
把 pivot_table 派上場。
# 不同船艙人數分佈
data_t.pivot_table(values=`Name`,index=`Pclass`,aggfunc=`count`)
複製程式碼
Name | |
---|---|
Pclass | |
1 | 216 |
2 | 184 |
3 | 491 |
傳幾個引數就出來了,是不是很方便。
如果不使用 pivot_table 函式,我們一般用 group_by 來分組聚合。
data_t[[`Pclass`,`Name`]].groupby([`Pclass`]).count()
複製程式碼
Name | |
---|---|
Pclass | |
1 | 216 |
2 | 184 |
3 | 491 |
比較來說,pivot_table 函式可讀性更高。
視覺化操作
plt.figure(figsize = (10,5)) # 建立畫布
sns.countplot(x=`Pclass`,data=data_t)
plt.title(`Person Count Across on Pclass`)
plt.show()
複製程式碼
還可以用餅圖。
plt.figure(figsize = (10,5)) # 建立畫布
plt.pie(data_t[[`Pclass`,`Name`]].groupby([`Pclass`]).count(),labels=[`1`,`2`,`3`],autopct=`%1.0f%%`)
plt.axis("equal") #繪製標準的圓形圖
plt.show()
複製程式碼
好了,這是不同艙位的人數分佈情況,我們需要求出的是艙位與生還率的關係。
艙位與生還率的關係
data_t.pivot_table(values=`Survived`,index=`Pclass`,aggfunc=np.mean)
複製程式碼
Survived | |
---|---|
Pclass | |
1 | 0.629630 |
2 | 0.472826 |
3 | 0.242363 |
視覺化操作
plt.figure(figsize= (10 ,5))
sns.barplot(data=data_t,x="Pclass",y="Survived",ci=None) # ci表示置信區間
plt.show()
複製程式碼
結論:頭等艙的生還概率最大,其次是二等艙,三等艙的概率最小。
性別(Sex)與生還率關係
# 不同性別生還率
data_t.pivot_table(values=`Survived`,index=`Sex`,aggfunc=np.mean)
複製程式碼
Survived | |
---|---|
Sex | |
female | 0.742038 |
male | 0.188908 |
視覺化操作
plt.figure(figsize=(10,5))
sns.barplot(data=data_t,x=`Sex`,y=`Survived`,ci=None)
plt.show()
複製程式碼
結論:女性倖存概率遠遠大於男性。
綜合考慮性別(Sex),艙位(Pclass)與生還率關係
#首先計算不同艙位不同性別的人的生還概率
data_t.pivot_table(values=`Survived`,index=[`Pclass`,`Sex`],aggfunc=np.mean)
複製程式碼
Survived | ||
---|---|---|
Pclass | Sex | |
1 | female | 0.968085 |
male | 0.368852 | |
2 | female | 0.921053 |
male | 0.157407 | |
3 | female | 0.500000 |
male | 0.135447 |
視覺化操作
plt.figure(figsize=(10,5))
sns.pointplot(data=data_t,x=`Pclass`,y=`Survived`,hue=`Sex`,ci=None)
plt.show()
複製程式碼
結論
- 在各個船艙中,女性的生還率都大於男性。
- 一二等船艙中女性生還率接近,且遠大於三等艙。
- 一等艙的男性生還率大於二三等艙,二三等艙男性生還率接近。
年齡(Age)與生還率關係
與上面的艙位、性別這些分類變數不同,年齡是一個連續的數值變數,一般處理這樣的資料型別,我們採用將連續性的變數離散化的方法。
所謂離散化,指的是將某個變數的所在區間分割為幾個小區間,落在同一個區間的觀測值用同一個符號表示,簡單理解就是將屬於統一範圍類的觀測值分為一組。然後分組觀察。
pandas中提供了cut函式,對變數進行離散化分割。
data_t[`AgeGroup`] = pd.cut(data_t[`Age`],5) # 將年齡的列數值劃分為五等份
data_t.AgeGroup.value_counts(sort=False)
複製程式碼
(-0.08, 16.0] 100
(16.0, 32.0] 525
(32.0, 48.0] 186
(48.0, 64.0] 69
(64.0, 80.0] 11
Name: AgeGroup, dtype: int64
複製程式碼
各個年齡段的生還率
data_t.pivot_table(values=`Survived`,index=`AgeGroup`,aggfunc=np.mean)
複製程式碼
Survived | |
---|---|
AgeGroup | |
(-0.08, 16.0] | 0.550000 |
(16.0, 32.0] | 0.344762 |
(32.0, 48.0] | 0.403226 |
(48.0, 64.0] | 0.434783 |
(64.0, 80.0] | 0.090909 |
視覺化操作
plt.figure(figsize=(10,5))
sns.barplot(data=data_t,x=`AgeGroup`,y=`Survived`,ci=None)
plt.xticks(rotation=60) # 設定標籤刻度角度
plt.show()
複製程式碼
結論:兒童少年組的生還率更高。
多因素分析
以上是單獨看年齡/性別/艙位和生還率的關係,下面我們綜合多個因素來看生還率。
年齡(Age),性別(Sex)與生還率關係
data_t.pivot_table(values=`Survived`,index=`AgeGroup`,columns=`Sex`,aggfunc=np.mean)
複製程式碼
Sex | female | male |
---|---|---|
AgeGroup | ||
(-0.08, 16.0] | 0.673469 | 0.431373 |
(16.0, 32.0] | 0.718391 | 0.159544 |
(32.0, 48.0] | 0.791045 | 0.184874 |
(48.0, 64.0] | 0.916667 | 0.177778 |
(64.0, 80.0] | NaN | 0.090909 |
視覺化操作
plt.figure(figsize= (10 ,5))
sns.pointplot(data=data_t,x="AgeGroup",y="Survived",hue="Sex",ci=None,
markers=["^", "o"], linestyles=["-", "--"])
plt.xticks(rotation=60)
plt.show()
複製程式碼
結論:兒童少年,女性的生還率更高。男性生還的基本上都是兒童少年。
年齡(Age),性別(Sex),艙位(Pclass)與生還率關係
data_t.pivot_table(values="Survived",index="AgeGroup",columns=["Sex","Pclass"],aggfunc=np.mean)
複製程式碼
Sex | female | male | ||||
---|---|---|---|---|---|---|
Pclass | 1 | 2 | 3 | 1 | 2 | 3 |
AgeGroup | ||||||
(-0.08, 16.0] | 0.833333 | 1.000000 | 0.545455 | 1.000000 | 0.818182 | 0.270270 |
(16.0, 32.0] | 0.975610 | 0.923077 | 0.521277 | 0.354167 | 0.086207 | 0.138776 |
(32.0, 48.0] | 1.000000 | 0.904762 | 0.250000 | 0.435897 | 0.076923 | 0.055556 |
(48.0, 64.0] | 0.941176 | 0.833333 | 1.000000 | 0.269231 | 0.090909 | 0.000000 |
(64.0, 80.0] | NaN | NaN | NaN | 0.166667 | 0.000000 | 0.000000 |
視覺化操作
sns.FacetGrid(data=data_t,row="AgeGroup",aspect=2.5)
.map(sns.pointplot,"Pclass","Survived","Sex",hue_order=["male","female"],ci=None,palette="deep",
markers=["^", "o"], linestyles=["-", "--"]).add_legend()
plt.show()
複製程式碼
總結
本次分析主要探尋泰坦尼克號上的生還率和各因素(客艙等級、年齡、性別、上船港口等)的關係。
樣本數量為 891,海難發生後,生還者還剩 342 人,生還率為 38%。
泰坦尼克號上有一二三等艙三種船艙型別,其中頭等艙的生還概率最大,其次是二等艙,三等艙的概率最小。
891人中,男性共577人,女性314人,女性生還率遠遠大於男性。可見女性比男性在這次事故中更容易生還,表明“女士優先”的原則在本次事故中得到了發揚。
樣本的 891 人中,最小年齡為 0.42 ,最大年齡 80。按照[(0.34, 16.336] < (16.336, 32.252] < (32.252, 48.168] < (48.168, 64.084] < (64.084, 80.0]]劃分原則,劃分為5組,兒童少年組的生還率最高,年齡越大,生還率越低。“尊老愛幼”的原則在本次事故中沒有很好體現。
樣本的 891 人中,從 C 上船的生還率最高, Q上船的 次之, S上船生還率 最低。
最後需要說明的是,此次資料分析的資料集是從總體中抽樣而來的,如果抽樣無偏,樣本是從總體隨機選取,根據中心極限定理,分析結果具有代表性,如果不是隨機選出,那麼分析結果就不可靠了。