使用Decision Tree預測權力遊戲角色是否存活
===
###### tags: `Data science` `課堂作業`
data 來源 : [Game of Thrones](https://www.kaggle.com/mylesoneill/game-of-thrones?select=battles.csv)
:::warning
這篇用到的資料"character-deaths.csv",檔案中有917個角色,他們每一個人都已經被標註了最後是存活或死亡,因此後面會切割成訓練集與測試集,最後對**測試集資料做預測**。
這篇重點不在於特徵工程或是把準確度提高,主要是熟悉**決策樹的建立、模型評估與決策樹可視化的練習**。
:::
## 資料讀取
```
import pandas as pd
df = pd.read_csv('./archive/character-deaths.csv')
df.head(5)
```

:::info
Name : 角色名字
Allegiances : 家族
Death Year : 死亡年份
Book of Death : 死亡集數
Death Chapter : 死亡章節
Book intro Chapter : 角色登場介紹章節
Gender : 1 -> 男性 0 -> 女性
Nobility : 1 -> 貴族 0 -> 平民
GoT/CoK/SoS/FfC/DwD: 出現在第一、二、三...本書中
:::
## 資料前處理
1. 觀察資料有無空值
```
df.info()
```

:::info
觀察出Death Year,Book of Death, Death Chapter,Book Intro Chapter 有空值,待會處理。
:::
2. 處理空值
- 2-1
首先,Death Year,Book of Death, Death Chapter,對於最後預測的結果來說都是代表一樣的意思,即為死亡,因此我們挑一個欄位,經由多次實驗,最後挑選"Death Year"處理與訓練,**將空值填入0,代表存活**,**有值的地方填入1,代表死亡**。這樣一來資料集變得更簡潔了,減少noise.
```
df['Death Year'] = df['Death Year'].fillna(0)
df['Death Year'][df['Death Year'] > 0] = 1
df
```

- 2-2 Book Intro Chapter
這部分先將空值填入0
```
df['Book Intro Chapter'] = df['Book Intro Chapter'].fillna(0)
df
```

3. Allegiances
Allegiances這個欄位是非數字的,但是模型只善於處理數字,因此我們需要對此欄位做處理。
:::info
categorical variables 又分為兩種,一種是 nominal,另一種是 ordinal
- nominal: 分類的值沒有任何先後順序或等級關係。
e.g. green/red/blue
- ordinal:分類的值是有等級關係的
e.g. high/medium/low
:::
Allegianves 是屬於nominal的,我們可以用一種名為 **One Hot Encoding 的技術**來解決這個問題。我們為列中的每一種分類都分別單獨建一個列(即會有新的欄位產生),用 0 或 1 填充,這些新建的列被稱作 **dummy vaiables**。
```
Allegiances_dummy = pd.get_dummies(df.Allegiances)
df = pd.concat([df, Allegiances_dummy], axis = 1)
df
```

:::info
我們會發現欄位數從本來的 13 增加至 34 個欄位
:::
## 切割訓練集與測試集
訓練集 75 %,測試集 25 %
```
from sklearn.model_selection import train_test_split
X = df.iloc[:,5:]
y = df.loc[:,'Death Year']
X_train,X_test,y_train,y_test = train_test_split(X, y, test_size = 0.25)
```
## 訓練模型
```
from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier(criterion = 'entropy',max_depth= 9, random_state = 10).fit(X_train, y_train)
```
:::info
利用matplotlib畫出學習曲線來確認最優的max-depth
:::
```
import matplotlib.pyplot as plt
from sklearn import tree
y_eff = []
for i in range(10): # 測試的條件數
tree_clf = tree.DecisionTreeClassifier(criterion="entropy"
,random_state = 10
,splitter = "best"
,max_depth = i+1 #測試條件
)
tree_clf = tree_clf.fit(X_train,y_train)
score = tree_clf.score(X_test,y_test)
y_eff.append(score)
plt.plot(range(1,11),y_eff,color="red",label="max_depth")
plt.legend()
plt.show()
```

:::info
因此我們將 max-depth 設為 9
:::
## 評估模型
- 混淆矩陣
```
from sklearn.metrics import confusion_matrix
y_pred = clf.predict(X_test)
confusion_matrix(y_test, y_pred)
```

```
from sklearn.metrics import plot_confusion_matrix
import matplotlib.pyplot as plt
plot_confusion_matrix(clf, X_test,y_test)
```

- accuracy
```
# accuracy
from sklearn.metrics import accuracy_score
accuracy = accuracy_score(y_test, y_pred)
accuracy
```
:::info
(TP + TN) / (TP + TN + FP + FN)
(52 + 123) / (52 + 123 + 32 + 23) 約等於 0.7609
:::

- precision 在所有預測為正樣本中,有多少為正樣本
```
from sklearn.metrics import precision_score
precision_score(y_test, y_pred)
```
:::info
TP / (TP + FP)
52 / (52 + 32) 約等於 0.62
:::

- recall 在所有正樣本當中,能夠預測多少正樣本的比例
```
#recall
from sklearn import metrics
metrics.recall_score(y_test, y_pred)
```
:::info
TP / (TP + FN)
52 / (52 + 23) 約等於 0.69
:::

## 決策樹可視化
以下分別使用 Graphviz 與 使用scikit-learn的 tree.plot_tree 方法將決策數視覺化,Graphviz 好處是可以匯出pdf,若沒有這需求的話就可以使用scikit-learn就好。
1.使用Graphviz將決策樹視覺化
```
from sklearn.tree import export_graphviz
import os
os.environ["PATH"] += os.pathsep + 'C:\Program Files\Graphviz\bin'
```
- 匯出PDF
```
import pydotplus
dot_data=tree.export_graphviz(clf,out_file=None)
graph=pydotplus.graph_from_dot_data(dot_data)
graph.write_pdf('game of thrones.pdf')
```
- 直接將圖畫出來看
```
from IPython.display import Image
#將 Decisson Tree Classifier 放入
dot_data = tree.export_graphviz(clf, out_file=None,
filled=True, rounded=True,
special_characters=True)
graph = pydotplus.graph_from_dot_data(dot_data)
Image(graph.create_png())
```

2. 從scikit-learn 版本21.0開始,可以使用scikit-learn的 tree.plot_tree 方法來利用matplotlib將決策樹視覺化,而不再需要依賴於難以安裝的dot庫。
```
fig, ax = plt.subplots(figsize=(25, 25))
tree.plot_tree(clf, ax=ax, fontsize=12)
plt.show()
```
## 結論
這次的重點在於熟悉決策樹的建立與使用,學習到的觀念與工具大概有以下:
- 資料前處理 -> one hot encoding
- 資料建模 -> scikit-learn DecisionTreeClassifier
- 評估模型 -> confusion matrix、precsion、recall、accuracy
- 決策數可視化 -> Graphviz 、sckit-learn tree.plot_tree(簡單易用)
:::info
這篇主要藉由課堂作業,寫下來的筆記,主要是針對新手觀看,
若有任何疑問或是更好的建議,歡迎大家留言指教討論 :smile:
:::