Titanic - Machine Learning from Disaster 鐵達尼號生存預測 資料分析篇 [資料分析與機器學習系列]
===
###### tags `資料分析`,`機器學習`,`Kaggle`,`Titanic`,`鐵達尼號生存預測`
這篇文章主要運用kaggle鐵達尼號生存預測資料 **train.csv**,手把手的帶大家一起完成基礎的資料分析,內容包含**探索資料、特徵工程與建模到實際玩玩看**,為了能夠快速上手,省去了許多較為複雜的統計知識,讓我們一起運用實作進入資料的世界吧:smile:
強調一下,本文章只運用 **train.csv** 帶著大家走過一次資料分析,若想要跑一次資料分析流程以及透過實作有趣認識資料分析,那這篇文章很適合。但若是想要進一步優化,可以概略此篇文章,然後進一步至網路上查詢,網路有許多符合你需求的文章,這邊提供一些連結,請參閱底下Further Reading區塊。
[圖片來源](https://www.history.com/.image/t_share/MTc2NTQ1ODM1NDQwNDgyMDU4/sinking-of-the-titanic-gettyimages-542907919-1.jpg)

## 認識資料
要進行分析前可以先去Kaggle觀察資料並下載。
[Download train.csv ](https://www.kaggle.com/c/titanic/data)

現在,我們至Data 裡的 Data Dictionary 大致看一下我們拿到的資料,包含乘客的艙等(pClass)、性別(Sex)、年齡(Age)、兄弟姊妹+老婆丈夫數量(SibSp)、父母小孩的數量(Parch)、票號(Ticket)、票的費用(Fare)、出發港口(Embarked)、房間號碼(Cabin)等,運用這些資料去**預估乘客是否會在鐵達尼號意外中生存下來**。

## 載入套件與匯入資料
```
# Import Libaries
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
# load the data
train_data = pd.read_csv('train.csv')
```
## 探索性資料分析(Exploratory Data Analysis)
1. 印出前十筆資料觀察欄位
```
#Print the first 10 rows of the data
train_data.head(10)
```

2. 觀察資料總共有幾列幾欄
```
# count the number of rows and coloumns in the dataset
train_data.shape
```

:::info
總共有 891 列 12 欄(特徵)
:::
3. 觀察資料統計資訊
```
# Get some statistics
train_data.describe()
```

:::info
觀察出 Age 少了一些資料,觀察到一些欄位的最大最小值、平均值等,如可以看到有人花512.329 British pounds 搭這艘船, 最小值花 0 元搭這艘船
:::
4. 觀察 891 人最後到底有多少人存活
```
# Get a count of the number of survivors
train_data['Survived'].value_counts()
# Visualize the count of survivors
sns.countplot(train_data['Survived'])
```


:::info
觀察出 只有 **342** 人 活了下來
:::
5. 進一步運用圖表簡單觀察說,性別、艙等、碼頭等欄位那些人在這場災難中存活下來。
```
# Visualize the count of survivors for columns 'sex','pclass','sibsp','parch','embarked'
cols = ['Sex','Pclass','SibSp','Parch','Embarked']
n_rows = 2
n_cols = 3
# The subplot grid and figure size of each graph
fig, axs = plt.subplots(n_rows,n_cols,figsize = (n_cols * 3.2, n_rows * 3.2))
for r in range(0, n_rows):
for c in range(0, n_cols):
i = r * n_cols + c # index to go through the number of columns
if i < 5:
ax = axs[r][c] #show where to position each sub plots
sns.countplot(train_data[cols[i]], hue = train_data['Survived'], ax= ax)
ax.set_title(cols[i])
ax.legend(title = 'survived', loc = 'upper right')
plt.tight_layout()
```

:::info
大致可以觀察出
* 女性存活的機會比男性來得高
* 頭等艙存活機會較高
* 可能有帶兄弟姊妹、老婆丈夫的乘客存活機會較高
* 有帶小孩父母親的存活機會較高
* 從S碼頭出發有可能艙位比較低,存活機會較低
:::
6. 看一下性別存活率
```
# Look at survival rate by sex
train_data.groupby('Sex')[['Survived']].mean()
```

:::info
觀察出
* 女性存活率 大約有 74.2%
* 男性存活率 大約只有 18.9%
:::
6.1 性別 + 艙等
```
#Look at survival rate by sex and class
train_data.pivot_table('Survived',index = 'Sex', columns = 'Pclass')
```

:::info
觀察出
女性坐頭等艙存活機會高達 96.8%
反觀,男性若坐經濟艙那存活率 只勝 13.54%
:::
6.2 性別 + 年齡 + 艙等
```
# Look at survival rate by sex, age and class
age = pd.cut(train_data['Age'],[0, 18, 80])
train_data.pivot_table('Survived',['Sex',age], 'Pclass')
```

:::info
觀察出
女性坐頭等艙商務艙不管年紀,存活機會皆有 90 %
反觀,男性如果坐經濟艙且年紀大於 18 歲 ,那存活機會只有 13.36%
上述總結**可以推論出,在災難情況下,小孩女性存活機會較男性大,而艙位越高(頭等艙) 那存活機會又可以增加**。
:::
7. 這時想觀察一下,各艙的價錢落在多少,看我有沒有機會存活率拉高一些XDD
```
# plot the price paid of each class
plt.scatter(train_data['Fare'],train_data['Pclass'], color = 'purple', label = 'Passenger Paid')
plt.ylabel('Class')
plt.xlabel('Price / Fare')
plt.title('Price of Each Class')
plt.legend()
plt.show()
# someone paid over 500 pounds for fiest class. it looks like every class somebody paid zero pounds looks interesting.
```

:::info
觀察出
每一艙位都有人花 0 英鎊就搭上船,也有人花了 500 多英鎊搭到頭等艙,是一個蠻有趣的發現!
:::
## 特徵工程
### 資料清理
1. 有無缺失值
```
# Count the empty values in each column
train_data.isna().sum()
```

:::info
觀察出
Age 、 Cabin 與 Embarked 三個欄位都有缺失值,其中 Cabin 缺失值高達 687 筆。
:::
2. 進行處裡 以及 把不必要欄位 drop 掉
* 目前欄位中, PassengerId、Name、Ticket(票號),沒有要拿來分析 且 Cabin 缺失值過多,因此選擇直接 將此欄刪除。
:::warning
採用常見缺失值處理方法之一 : 把有缺失值的那筆資料,整理直接刪除。
:::
```
# Drop the columns
train_data = train_data.drop(['Cabin','Name','PassengerId'], axis=1)
train_data = train_data.drop(['Ticket'] , axis =1 )
# Remove the rows with missing values
train_data = train_data.dropna(subset = ['Embarked','Age'])
```
2.1 來看一下處理完剩下幾列幾欄(特徵)
```
# Count the new number of rows and columns in the dataset
train_data.shape
```

:::info
目前資料有 **712** 列 與 **8** 個欄位 (特徵)
:::
### 編碼
3. 看一下這 8 個欄位(特徵) 資料型態, 因為模型裡只能放入數字型資料,所以稍微觀察一下。
```
# Look at the data types
train_data.dtypes
```

:::info
觀察出 'Sex' 與 'Embarked' **是非數值型資料**,因此我們需進行編碼。
:::
3.1 觀察這兩欄,裡頭有哪些值。
```
# Print the unique values in the columns
print(train_data['Sex'].unique())
print(train_data['Embarked'].unique())
```

:::info
'Sex' 欄位有 ['male', 'female']
'Embarked' 欄位有 ['S', 'C','Q']
:::
3.2 這邊採用 label encoder 進行處理
```
from sklearn.preprocessing import LabelEncoder
labelencoder = LabelEncoder()
# Encode the sex column
train_data.iloc[:, 2] = labelencoder.fit_transform(train_data.iloc[:, 2].values)
# Encode the embarked column
train_data.iloc[:, 7] = labelencoder.fit_transform(train_data.iloc[:, 7].values)
```
:::warning
**為甚麼要做編碼?**
目的 : 類別 (categorical)或是文字(text)的資料轉換成數字,而讓程式能夠去理解及運算
:::
3.3 再次觀察這兩欄
```
# Print the unique values in the columns
print(train_data['Sex'].unique())
print(train_data['Embarked'].unique())
```

:::info
'Sex' 欄位有 ['male', 'female'] - > [1, 0]
'Embarked' 欄位有 ['S', 'C','Q'] - > [2,0,1]
:::
3.4 再次觀察所有欄位資料型態
```
train_data.dtypes
```

:::info
目前欄位都為數值型資料了!!!! 這樣待會就可以順利匯入模型,進行預測了。
:::
4. 準備一下資料, X 表示我們未來手上會有的資料(乘客資訊) y 為 Survived (船難中是否存活) 。
```
# Split the data into independent 'X' and dependent 'y' variables
X = train_data.iloc[:, 1:8].values
y = train_data.iloc[:, 0].values
```
5. 將現有資料 切分 80 % 作為訓練資料集 20 % 為 測試資料集
```
# Split the dataset into 80% training and 20% testing
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 0)
```
### 資料標準化(standardization)
```
#Scale the data
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
X_trian = sc.fit_transform(X_train)
X_test = sc.transform(X_test)
```
:::warning
**為甚麼要做標準化?**
因為在資料中,是用不同資料欄位與資料值所組成,他們**分佈狀況可能都不盡相同**,因此,就必須將**特徵資料按比例縮放,讓資料落在某一特定的區間**。
:::
## 建模
1. **基本分類模型建模 function**
```
# Create a function with many mechaine learning models
def models(X_train, y_train):
# Use Logistic Regression
from sklearn.linear_model import LogisticRegression
log = LogisticRegression(random_state = 0)
log.fit(X_train, y_train)
# Use KNeighbors
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors= 5, metric = 'minkowski', p = 2)
knn.fit(X_train, y_train)
# Use SVC (linear kernal)
from sklearn.svm import SVC
svc_lin = SVC(kernel='linear', random_state = 0)
svc_lin.fit(X_train,y_train)
# Use SVC (RBF kernal)
from sklearn.svm import SVC
svc_rbf = SVC(kernel='rbf', random_state = 0)
svc_rbf.fit(X_train,y_train)
# Use GaussianNB
from sklearn.naive_bayes import GaussianNB
gauss = GaussianNB()
gauss.fit(X_train, y_train)
# Use Dicision Tree
from sklearn.tree import DecisionTreeClassifier
tree = DecisionTreeClassifier(criterion = 'entropy', random_state = 0)
tree.fit(X_train, y_train)
# Use the RandomForestClassifier
from sklearn.ensemble import RandomForestClassifier
forest = RandomForestClassifier(n_estimators = 10, criterion = 'entropy', random_state = 0)
forest.fit(X_train,y_train)
#Print the training accuracy for each model
print('[0]Logistic Regression Training Accuracy:', log.score(X_train, y_train))
print('[1]K Neighbors Training Accuracy:', knn.score(X_train, y_train))
print('[2]SVC Linear Training Accuracy:', svc_lin.score(X_train, y_train))
print('[3]SVC RBF Training Accuracy:', svc_rbf.score(X_train, y_train))
print('[4]Gaussian NB Training Accuracy:', gauss.score(X_train, y_train))
print('[5]Decision Tree Training Accuracy:', tree.score(X_train, y_train))
print('[6]Random Forest Training Accuracy:', forest.score(X_train, y_train))
return log,knn,svc_lin,svc_rbf,gauss,tree,forest
```
2. **訓練集資料成果**
```
# Get the train all of the models
model = models(X_train, y_train)
```

:::info
可以發現 'Decision Tree' 與 'Random Forest' 訓練不錯,訓練資料集準確率高達 95 % 以上。
:::
3. 預測**測試資料集、混淆矩陣與 Accuracy 分數**
```
# Show the confusion matrix and accuracy for all of the models of the test data
from sklearn.metrics import confusion_matrix
for i in range( len(model) ):
cm = confusion_matrix(y_test, model[i].predict(X_test))
# Extract TN, FP, FN, TP
TN, FP, FN, TP = confusion_matrix(y_test, model[i].predict(X_test)).ravel()
test_score = (TP + TN) / (TN + TP + FP + FN)
print(cm)
print('Model[{}] Testing Accuracy = "{}"' .format(i, test_score))
print()
```


:::info
可以發現 'Decision Tree' 測試資料集還有 79 % 的 準確率。
:::
## 額外觀察
進一步觀察 Decision model,重要特徵(欄位)排序,也就是那些影響結果最多
```
# Get feature impornt
decisionTree = model[5]
importances = pd.DataFrame({'feature': train_data.iloc[:, 1:8].columns, 'importance': np.round(decisionTree.feature_importances_, 3)} )
importances = importances.sort_values('importance', ascending = False).set_index('feature')
importances
# visualize the importance
importances.plot.bar()
```


## 玩玩看 Can You Survive?
```
# Pclass int64
# Sex int32 male,female [1,0]
# Age float64
# SibSp int64
# Parch int64
# Fare float64
# Embarked int32 S,C,Q [2,0,1]
# My survival
my_survival = [[2, 0, 22, 1, 2, 14.45, 1]]
# Scaling my survival
my_survival_scaled = sc.transform(my_survival)
#Print prediction of my survival using Decision Tree Classifier
pred = model[5].predict(my_survival_scaled)
print(pred)
if pred == 0:
print('Oh no! You did not make it')
else:
print('Nice! You Survived')
```

## 結論
藉由 Kaggle 上鐵達尼號**train.csv**,我們一起跑過簡單基礎的資料分析及資料處理,希望文中的程式碼以及解釋,能幫助大家初步認識資料分析並快速上手,同時希望能使讀者瞭解一個機器學習專案的基本構思及流程,通常一個機器學習專案,我們會耗費絕大部分的時間在資料分析與特徵工程上。
最後,對於鐵達尼號生存預測比賽有興趣或是想再進行優化,不妨試著至Kaggle實際提出資料與觀看討論區,或是其他網站查詢相信你將會獲得多元的收穫與經驗!
## Reference
[Titanic Survival Prediction Using Machine Learning](https://www.youtube.com/watch?v=rODWw2_1mCI)
## Further Reading
[Kaggle Titanic - Machine Learning from Disaster 討論區](https://www.kaggle.com/c/titanic/discussion)
[Kaggle競賽-鐵達尼號生存預測(Top 3%)](https://yulongtsai.medium.com/https-medium-com-yulongtsai-titanic-top3-8e64741cc11f)
[Predicting the Survival of Titanic Passengers](https://towardsdatascience.com/predicting-the-survival-of-titanic-passengers-30870ccc7e8)
:::warning
若有興趣關注我的文章可按左邊訂閱!
可以給我讚賞 給我鼓勵。
IG->duck_tech_ 新創立帳號 歡迎追蹤
會不定期分享 LeetCode 挑戰 |網頁全端與資料科學學習筆記 | 自我成長經驗 :love_letter:
:::
:::success
針對本文的內容,若讀者們有發現任何的錯誤或疑問,非常歡迎您至IG私訊給予建議及討論,讓我們一同來學習成長!
:::