# 十三、樹和森林
> 作者:[Chris Albon](https://chrisalbon.com/)
>
> 譯者:[飛龍](https://github.com/wizardforcel)
>
> 協議:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
## Adaboost 分類器

```py
# 加載庫
from sklearn.ensemble import AdaBoostClassifier
from sklearn import datasets
# 加載數據
iris = datasets.load_iris()
X = iris.data
y = iris.target
```
最重要的參數是`base_estimator`,`n_estimators`和`learning_rate`。
* `base_estimator`是用于訓練弱模型的學習算法。 這幾乎總是不需要改變,因為到目前為止,與 AdaBoost 一起使用的最常見的學習者是決策樹 - 這個參數的默認參數。
* `n_estimators`是迭代式訓練的模型數。
* `learning_rate`是每個模型對權重的貢獻,默認為`1`。 降低學習率將意味著權重將增加或減少到很小的程度,迫使模型訓練更慢(但有時會產生更好的表現得分)。
* `loss`是`AdaBoostRegressor`獨有的,它設置了更新權重時使用的損失函數。 這默認為線性損失函數,但可以更改為`square`或`exponential`。
```py
# 創建 adaboost 決策樹分類器對象
clf = AdaBoostClassifier(n_estimators=50,
learning_rate=1,
random_state=0)
# 訓練模型
model = clf.fit(X, y)
```
## 決策樹分類器

```py
# 加載庫
from sklearn.tree import DecisionTreeClassifier
from sklearn import datasets
# 加載數據
iris = datasets.load_iris()
X = iris.data
y = iris.target
# 創建使用 GINI 的決策樹分類器對象
clf = DecisionTreeClassifier(criterion='gini', random_state=0)
# 訓練模型
model = clf.fit(X, y)
# 生成新的觀測
observation = [[ 5, 4, 3, 2]]
# 預測觀測的類別
model.predict(observation)
# array([1])
# 查看三個類別的預測概率
model.predict_proba(observation)
# array([[ 0., 1., 0.]])
```
## 決策樹回歸

```py
# 加載庫
from sklearn.tree import DecisionTreeRegressor
from sklearn import datasets
# 加載只有兩個特征的數據
boston = datasets.load_boston()
X = boston.data[:,0:2]
y = boston.target
```
決策樹回歸的工作方式類似于決策樹分類,但不是減少基尼雜質或熵,而是測量潛在的分割點,它們減少均方誤差(MSE)的程度:

其中  是目標的真實值, 是預測值。
```py
# 創建決策樹回歸器對象
regr = DecisionTreeRegressor(random_state=0)
# 訓練模型
model = regr.fit(X, y)
# 生成新的觀測
observation = [[0.02, 16]]
# 預測觀測的值
model.predict(observation)
# array([ 33.])
```
## 特征的重要性

```py
# 加載庫
from sklearn.ensemble import RandomForestClassifier
from sklearn import datasets
import numpy as np
import matplotlib.pyplot as plt
# 加載數據
iris = datasets.load_iris()
X = iris.data
y = iris.target
# 創建決策樹分類器對象
clf = RandomForestClassifier(random_state=0, n_jobs=-1)
# 訓練模型
model = clf.fit(X, y)
# 計算特征重要性
importances = model.feature_importances_
# 對特征重要性降序排序
indices = np.argsort(importances)[::-1]
# 重新排列特征名稱,使它們匹配有序的特征重要性
names = [iris.feature_names[i] for i in indices]
# 創建繪圖
plt.figure()
# 創建繪圖標題
plt.title("Feature Importance")
# 添加條形
plt.bar(range(X.shape[1]), importances[indices])
# 添加特征名稱作為 x 軸標簽
plt.xticks(range(X.shape[1]), names, rotation=90)
# 展示繪圖
plt.show()
```

## 使用隨機森林的特征選擇
通常在數據科學中,我們有數百甚至數百萬個特征,我們想要一種方法來創建僅包含最重要特征的模型。 這有三個好處。 首先,我們使模型更易于解釋。 其次,我們可以減少模型的方差,從而避免過擬合。 最后,我們可以減少訓練模型的計算開銷(和時間)。 僅識別最相關特征的過程稱為“特征選擇”。
數據科學工作流程中,隨機森林通常用于特征選擇。 原因是,隨機森林使用的基于樹的策略,自然按照它們如何改善節點的純度來排序。 這意味著所有樹的不純度的減少(稱為[基尼不純度](https://en.wikipedia.org/wiki/Decision_tree_learning#Gini_impurity))。 不純度減少最多的節點出現在樹的開始處,而不純度減少最少的節點出現在樹的末端。 因此,通過在特定節點下修剪樹,我們可以創建最重要特征的子集。
在這個教程中,我們將要:
1. 準備數據集
2. 訓練隨機森林分類器
3. 識別最重要的特征
4. 創建新的“有限特征的”數據集,僅僅包含那些特征
5. 在新數據集上訓練第二個分類器
6. 將“全部特征的”分類器的準確率,和“有限特征的”分類器比較
注:還有其他重要定義,但在本教程中,我們將討論限制為基尼重要性。
```py
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.feature_selection import SelectFromModel
from sklearn.metrics import accuracy_score
```
本教程中使用的數據集是著名的[鳶尾花數據集](https://en.wikipedia.org/wiki/Iris_flower_data_set)鳶尾花數據包含來自三種鳶尾`y`和四個特征變量`X`的 50 個樣本。
```py
# 加載鳶尾花數據集
iris = datasets.load_iris()
# 創建特征名稱列表
feat_labels = ['Sepal Length','Sepal Width','Petal Length','Petal Width']
# 從特征中創建 X
X = iris.data
# 從目標中創建 y
y = iris.target
# 查看特征
X[0:5]
'''
array([[ 5.1, 3.5, 1.4, 0.2],
[ 4.9, 3\. , 1.4, 0.2],
[ 4.7, 3.2, 1.3, 0.2],
[ 4.6, 3.1, 1.5, 0.2],
[ 5\. , 3.6, 1.4, 0.2]])
'''
# 查看目標數據
y
'''
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])
'''
# 將數據分為 40% 的測試和 60% 的訓練集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=0)
# 創建隨機森林分類器
clf = RandomForestClassifier(n_estimators=10000, random_state=0, n_jobs=-1)
# 訓練分類器
clf.fit(X_train, y_train)
# 打印每個特征的名稱和基尼重要性
for feature in zip(feat_labels, clf.feature_importances_):
print(feature)
'''
('Sepal Length', 0.11024282328064565)
('Sepal Width', 0.016255033655398394)
('Petal Length', 0.45028123999239533)
('Petal Width', 0.42322090307156124)
'''
```
上面的得分是每個變量的重要性得分。 有兩點需要注意。 首先,所有重要性得分加起來為 100%。 其次,“花瓣長度”和“花瓣寬度”遠比其他兩個特征重要。結合起來,“花瓣長度”和“花瓣寬度”的重要性約為 0.86!顯然,這些是最重要的特征。
```py
# 創建一個選擇器對象,
# 該對象將使用隨機森林分類器來標識重要性大于 0.15 的特征
sfm = SelectFromModel(clf, threshold=0.15)
# 訓練選擇器
sfm.fit(X_train, y_train)
'''
SelectFromModel(estimator=RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
max_depth=None, max_features='auto', max_leaf_nodes=None,
min_impurity_split=1e-07, min_samples_leaf=1,
min_samples_split=2, min_weight_fraction_leaf=0.0,
n_estimators=10000, n_jobs=-1, oob_score=False, random_state=0,
verbose=0, warm_start=False),
prefit=False, threshold=0.15)
'''
# 打印最重要的特征的名稱
for feature_list_index in sfm.get_support(indices=True):
print(feat_labels[feature_list_index])
'''
Petal Length
Petal Width
'''
# 轉換數據來創建僅包含最重要特征的新數據集
# 注意:我們必須將變換應用于訓練 X 和測試 X 數據。
X_important_train = sfm.transform(X_train)
X_important_test = sfm.transform(X_test)
# 為最重要的特征創建新的隨機森林分類器
clf_important = RandomForestClassifier(n_estimators=10000, random_state=0, n_jobs=-1)
# 在包含最重要特征的新數據集上訓練新分類器
clf_important.fit(X_important_train, y_train)
'''
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
max_depth=None, max_features='auto', max_leaf_nodes=None,
min_impurity_split=1e-07, min_samples_leaf=1,
min_samples_split=2, min_weight_fraction_leaf=0.0,
n_estimators=10000, n_jobs=-1, oob_score=False, random_state=0,
verbose=0, warm_start=False)
'''
# 將全部特征的分類器應用于測試數據
y_pred = clf.predict(X_test)
# 查看我們的全部特征(4 個特征)的模型的準確率
accuracy_score(y_test, y_pred)
# 0.93333333333333335
# 將全部特征的分類器應用于測試數據
y_important_pred = clf_important.predict(X_important_test)
# 查看我們有限特征(2 個特征)的模型的準確率
accuracy_score(y_test, y_important_pred)
# 0.8833333333333333
```
從準確率得分可以看出,包含所有四個特征的原始模型準確率為 93.3%,而僅包含兩個特征的“有限”模型準確率為 88.3%。 因此,為了精確率的低成本,我們將模型中的特征數量減半。
## 在隨機森林中處理不平衡類別
```py
# 加載庫
from sklearn.ensemble import RandomForestClassifier
import numpy as np
from sklearn import datasets
# 加載數據
iris = datasets.load_iris()
X = iris.data
y = iris.target
# 通過移除前 40 個觀測,生成高度不平衡的類別
X = X[40:,:]
y = y[40:]
# 創建目標向量,表示類別是否為 0
y = np.where((y == 0), 0, 1)
```
當使用`RandomForestClassifier`時,有用的設置是`class_weight = balanced`,其中類自動加權,與它們在數據中出現的頻率成反比。具體來說:

其中  是類  的權重, 是觀測數, 是類  中的觀測數, 是類的總數。
```py
# 創建決策樹分類器對象
clf = RandomForestClassifier(random_state=0, n_jobs=-1, class_weight="balanced")
# 訓練模型
model = clf.fit(X, y)
```
## 隨機森林分類器
```py
# 加載庫
from sklearn.ensemble import RandomForestClassifier
from sklearn import datasets
# 加載數據
iris = datasets.load_iris()
X = iris.data
y = iris.target
# 創建使用熵的隨機森林分類器
clf = RandomForestClassifier(criterion='entropy', random_state=0, n_jobs=-1)
# 訓練模型
model = clf.fit(X, y)
# 創建新的觀測
observation = [[ 5, 4, 3, 2]]
# 預測觀測的類別
model.predict(observation)
```
```py
array([1])
```
## 隨機森林分類器示例
本教程基于 Yhat 2013 年的[ Python 中的隨機森林]教程(http://blog.yhat.com/posts/random-forests-in-python.html)。 如果你想要隨機森林的理論和用途的總結,我建議你查看他們的指南。 在下面的教程中,我對文章末尾提供的隨機森林的簡短代碼示例進行了注釋,更正和擴展。 具體來說,我(1)更新代碼,使其在最新版本的 pandas 和 Python 中運行,(2)編寫詳細的注釋,解釋每個步驟中發生的事情,以及(3)以多種方式擴展代碼。
讓我們開始吧!
### 數據的注解
本教程的數據很有名。 被稱為[鳶尾花數據集](https://en.wikipedia.org/wiki/Iris_flower_data_set),它包含四個變量,測量了三個鳶尾花物種的各個部分,然后是帶有物種名稱的第四個變量。 它在機器學習和統計社區中如此著名的原因是,數據需要很少的預處理(即沒有缺失值,所有特征都是浮點數等)。
```py
# 加載鳶尾花數據集
from sklearn.datasets import load_iris
# 加載 sklearn 的隨機森林分類器
from sklearn.ensemble import RandomForestClassifier
# 加載 pandas
import pandas as pd
# 加載 numpy
import numpy as np
# 設置隨機種子
np.random.seed(0)
# Create an object called iris with the iris data
iris = load_iris()
# 創建帶有四個特征變量的數據幀
df = pd.DataFrame(iris.data, columns=iris.feature_names)
# 查看前五行
df.head()
```
| | sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) |
| --- | --- | --- | --- | --- |
| 0 | 5.1 | 3.5 | 1.4 | 0.2 |
| 1 | 4.9 | 3.0 | 1.4 | 0.2 |
| 2 | 4.7 | 3.2 | 1.3 | 0.2 |
| 3 | 4.6 | 3.1 | 1.5 | 0.2 |
| 4 | 5.0 | 3.6 | 1.4 | 0.2 |
```py
# 添加帶有物種名稱的新列,我們要嘗試預測它
df['species'] = pd.Categorical.from_codes(iris.target, iris.target_names)
# 查看前五行
df.head()
```
| | sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | species |
| --- | --- | --- | --- | --- | --- |
| 0 | 5.1 | 3.5 | 1.4 | 0.2 | setosa |
| 1 | 4.9 | 3.0 | 1.4 | 0.2 | setosa |
| 2 | 4.7 | 3.2 | 1.3 | 0.2 | setosa |
| 3 | 4.6 | 3.1 | 1.5 | 0.2 | setosa |
| 4 | 5.0 | 3.6 | 1.4 | 0.2 | setosa |
```py
# 創建一個新列,每列生成一個0到1之間的隨機數,
# 如果該值小于或等于.75,則將該單元格的值設置為 True
# 否則為 False。這是一種簡潔方式,
# 隨機分配一些行作為訓練數據,一些作為測試數據。
df['is_train'] = np.random.uniform(0, 1, len(df)) <= .75
# 查看前五行
df.head()
```
| | sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | species | is_train |
| --- | --- | --- | --- | --- | --- | --- |
| 0 | 5.1 | 3.5 | 1.4 | 0.2 | setosa | True |
| 1 | 4.9 | 3.0 | 1.4 | 0.2 | setosa | True |
| 2 | 4.7 | 3.2 | 1.3 | 0.2 | setosa | True |
| 3 | 4.6 | 3.1 | 1.5 | 0.2 | setosa | True |
| 4 | 5.0 | 3.6 | 1.4 | 0.2 | setosa | True |
```py
# 創建兩個新的數據幀,一個包含訓練行,另一個包含測試行
train, test = df[df['is_train']==True], df[df['is_train']==False]
# 顯示測試和訓練數據幀的觀測數
print('Number of observations in the training data:', len(train))
print('Number of observations in the test data:',len(test))
'''
Number of observations in the training data: 118
Number of observations in the test data: 32
'''
# 創建特征列名稱的列表
features = df.columns[:4]
# 查看特征
features
'''
Index(['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)',
'petal width (cm)'],
dtype='object')
'''
# train['species'] 包含實際的物種名稱。
# 在我們使用它之前,我們需要將每個物種名稱轉換為數字。
# 因此,在這種情況下,有三種物種,它們被編碼為 0, 1 或 2。
y = pd.factorize(train['species'])[0]
# 查看目標
y
'''
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2])
'''
# 創建隨機森林分類器。按照慣例,clf 表示“分類器”
clf = RandomForestClassifier(n_jobs=2, random_state=0)
# 訓練分類器,來接受訓練特征
# 并了解它們與訓練集 y(物種)的關系
clf.fit(train[features], y)
'''
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
max_depth=None, max_features='auto', max_leaf_nodes=None,
min_impurity_split=1e-07, min_samples_leaf=1,
min_samples_split=2, min_weight_fraction_leaf=0.0,
n_estimators=10, n_jobs=2, oob_score=False, random_state=0,
verbose=0, warm_start=False)
'''
```
好哇! 我們做到了! 我們正式訓練了我們的隨機森林分類器! 現在讓我們玩玩吧。 分類器模型本身存儲在`clf`變量中。
如果你一直跟著,你會知道我們只在部分數據上訓練了我們的分類器,留出了剩下的數據。 在我看來,這是機器學習中最重要的部分。 為什么? 因為省略了部分數據,我們有一組數據來測試我們模型的準確率!
讓我們現在實現它。
```py
# 將我們訓練的分類器應用于測試數據
# (記住,以前從未見過它)
clf.predict(test[features])
'''
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 1, 1, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2])
'''
```
你在上面看到什么? 請記住,我們將三種植物中的每一種編碼為 0, 1 或 2。 以上數字列表顯示,我們的模型基于萼片長度,萼片寬度,花瓣長度和花瓣寬度,預測每種植物的種類。 分類器對于每種植物有多自信? 我們也可以看到。
```py
# 查看前 10 個觀測值的預測概率
clf.predict_proba(test[features])[0:10]
'''
array([[ 1\. , 0\. , 0\. ],
[ 1\. , 0\. , 0\. ],
[ 1\. , 0\. , 0\. ],
[ 1\. , 0\. , 0\. ],
[ 1\. , 0\. , 0\. ],
[ 1\. , 0\. , 0\. ],
[ 1\. , 0\. , 0\. ],
[ 0.9, 0.1, 0\. ],
[ 1\. , 0\. , 0\. ],
[ 1\. , 0\. , 0\. ]])
'''
```
有三種植物,因此`[1, 0, 0]`告訴我們分類器確定植物是第一類。 再舉一個例子,`[0.9, 0.1, 0]`告訴我們,分類器給出植物屬于第一類的概率為90%,植物屬于第二類的概率為 10%。 因為 90 大于 10,分類器預測植物是第一類。
現在我們已經預測了測試數據中所有植物的種類,我們可以比較我們預測的物種與該植物的實際物種。
```py
# 為每個預測的植物類別
# 創建植物的實際英文名稱
preds = iris.target_names[clf.predict(test[features])]
# 查看前五個觀測值的預測物種
preds[0:5]
'''
array(['setosa', 'setosa', 'setosa', 'setosa', 'setosa'],
dtype='<U10')
'''
# 查看前五個觀測值的實際物種
test['species'].head()
'''
7 setosa
8 setosa
10 setosa
13 setosa
17 setosa
Name: species, dtype: category
Categories (3, object): [setosa, versicolor, virginica]
'''
```
看起來很不錯! 至少對于前五個觀測。 現在讓我們看看所有數據。
[混淆矩陣](https://en.wikipedia.org/wiki/Confusion_matrix)可能令人混淆,但它實際上非常簡單。 列是我們為測試數據預測的物種,行是測試數據的實際物種。 因此,如果我們選取最上面的行,我們可以完美地預測測試數據中的所有 13 個山鳶尾。 然而,在下一行中,我們正確地預測了 5 個雜色鳶尾,但錯誤地將兩個雜色鳶尾預測為維吉尼亞鳶尾。
混淆矩陣的簡短解釋方式是:對角線上的任何東西都被正確分類,對角線之外的任何東西都被錯誤地分類。
```py
# 創建混淆矩陣
pd.crosstab(test['species'], preds, rownames=['Actual Species'], colnames=['Predicted Species'])
```
| Predicted Species | setosa | versicolor | virginica |
| --- | --- | --- | --- |
| Actual Species | | | |
| setosa | 13 | 0 | 0 |
| versicolor | 0 | 5 | 2 |
| virginica | 0 | 0 | 12 |
雖然我們沒有像 OLS 那樣得到回歸系數,但我們得到的分數告訴我們,每個特征在分類中的重要性。 這是隨機森林中最強大的部分之一,因為我們可以清楚地看到,在分類中花瓣寬度比萼片寬度更重要。
```py
# 查看特征列表和它們的重要性得分
list(zip(train[features], clf.feature_importances_))
'''
[('sepal length (cm)', 0.11185992930506346),
('sepal width (cm)', 0.016341813006098178),
('petal length (cm)', 0.36439533040889194),
('petal width (cm)', 0.5074029272799464)]
'''
```
## 隨機森林回歸
```py
# 加載庫
from sklearn.ensemble import RandomForestRegressor
from sklearn import datasets
# 加載只有兩個特征的數據
boston = datasets.load_boston()
X = boston.data[:,0:2]
y = boston.target
# Create decision tree classifer object
regr = RandomForestRegressor(random_state=0, n_jobs=-1)
# 訓練模型
model = regr.fit(X, y)
```
## 在隨機森林中選擇特征重要性
```py
# 加載庫
from sklearn.ensemble import RandomForestClassifier
from sklearn import datasets
from sklearn.feature_selection import SelectFromModel
# 加載數據
iris = datasets.load_iris()
X = iris.data
y = iris.target
# 創建隨機森林分類器
clf = RandomForestClassifier(random_state=0, n_jobs=-1)
```
數字越大,特征越重要(所有重要性得分總和為1)。 通過繪制這些值,我們可以為隨機森林模型添加可解釋性。
```py
# 創建選擇重要性大于或等于閾值的特征的對象
selector = SelectFromModel(clf, threshold=0.3)
# 使用選擇器生成新的特征矩陣
X_important = selector.fit_transform(X, y)
# 查看特征的前五個觀測
X_important[0:5]
'''
array([[ 1.4, 0.2],
[ 1.4, 0.2],
[ 1.3, 0.2],
[ 1.5, 0.2],
[ 1.4, 0.2]])
'''
# 使用最重要的特征訓練隨機森林
model = clf.fit(X_important, y)
```
## 泰坦尼克比賽和隨機森林
```py
import pandas as pd
import numpy as np
from sklearn import preprocessing
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV, cross_val_score
import csv as csv
```
你可以在 [Kaggle](https://www.kaggle.com/c/titanic) 獲取數據。
```py
# 加載數據
train = pd.read_csv('data/train.csv')
test = pd.read_csv('data/test.csv')
# 創建特征列表,我們最終會接受他們
features = ['Age', 'SibSp','Parch','Fare','male','embarked_Q','embarked_S','Pclass_2', 'Pclass_3']
```
### 性別
在這里,我們將性別標簽(`male`,`female`)轉換為虛擬變量(`1`,`0`)。
```py
# 創建編碼器
sex_encoder = preprocessing.LabelEncoder()
# 使編碼器擬合訓練數據,因此它知道 male = 1
sex_encoder.fit(train['Sex'])
# 將編碼器應用于訓練數據
train['male'] = sex_encoder.transform(train['Sex'])
# 將編碼器應用于測試數據
test['male'] = sex_encoder.transform(test['Sex'])
# 使用單熱編碼,將編碼的特征轉換為虛擬值
# 去掉第一個類別來防止共線性
train_embarked_dummied = pd.get_dummies(train["Embarked"], prefix='embarked', drop_first=True)
# 使用單熱編碼
# 將“已編碼”的測試特征轉換為虛擬值
# 去掉第一個類別來防止共線性
test_embarked_dummied = pd.get_dummies(test["Embarked"], prefix='embarked', drop_first=True)
# 將虛擬值的數據幀與主數據幀連接起來
train = pd.concat([train, train_embarked_dummied], axis=1)
test = pd.concat([test, test_embarked_dummied], axis=1)
# 使用單熱編碼將 Pclass 訓練特征轉換為虛擬值
# 去掉第一個類別來防止共線性
train_Pclass_dummied = pd.get_dummies(train["Pclass"], prefix='Pclass', drop_first=True)
# 使用單熱編碼將 Pclass 測試特征轉換為虛擬值
# 去掉第一個類別來防止共線性
test_Pclass_dummied = pd.get_dummies(test["Pclass"], prefix='Pclass', drop_first=True)
# 將虛擬值的數據幀與主數據幀連接起來
train = pd.concat([train, train_Pclass_dummied], axis=1)
test = pd.concat([test, test_Pclass_dummied], axis=1)
```
### 年齡
`Age`特征的許多值都缺失,并且會妨礙隨機森林進行訓練。 我們解決這個問題,我們將用年齡的平均值填充缺失值(一個實用的操作)。
```py
# 創建填充器對象
age_imputer = preprocessing.Imputer(missing_values='NaN', strategy='mean', axis=0)
# 將填充器對象擬合訓練數據
age_imputer.fit(train['Age'].reshape(-1, 1))
# 將填充器對象應用于訓練和測試數據
train['Age'] = age_imputer.transform(train['Age'].reshape(-1, 1))
test['Age'] = age_imputer.transform(test['Age'].reshape(-1, 1))
# 創建填充器對象
fare_imputer = preprocessing.Imputer(missing_values='NaN', strategy='mean', axis=0)
# 將填充器對象擬合訓練數據
fare_imputer.fit(train['Fare'].reshape(-1, 1))
# 將填充器對象應用于訓練和測試數據
train['Fare'] = fare_imputer.transform(train['Fare'].reshape(-1, 1))
test['Fare'] = fare_imputer.transform(test['Fare'].reshape(-1, 1))
# 創建包含參數所有候選值的字典
parameter_grid = dict(n_estimators=list(range(1, 5001, 1000)),
criterion=['gini','entropy'],
max_features=list(range(1, len(features), 2)),
max_depth= [None] + list(range(5, 25, 1)))
# 創建隨機森林對象
random_forest = RandomForestClassifier(random_state=0, n_jobs=-1)
# 創建網格搜索對象,使用 5 倍交叉驗證
# 并使用所有核(n_jobs = -1)
clf = GridSearchCV(estimator=random_forest, param_grid=parameter_grid, cv=5, verbose=1, n_jobs=-1)
# 將網格搜索嵌套在 3 折 CV 中來進行模型評估
cv_scores = cross_val_score(clf, train[features], train['Survived'])
# 打印結果
print('Accuracy scores:', cv_scores)
print('Mean of score:', np.mean(cv_scores))
print('Variance of scores:', np.var(cv_scores))
# 在整個數據集上重新訓練模型
clf.fit(train[features], train['Survived'])
# 預測在測試數據集中的幸存者
predictions = clf.predict(test[features])
# 獲取乘客 ID
ids = test['PassengerId'].values
# 創建 csv
submission_file = open("submission.csv", "w")
# 寫入這個 csv
open_file_object = csv.writer(submission_file)
# 寫入 CSV 標題
open_file_object.writerow(["PassengerId","Survived"])
# 寫入 CSV 的行
open_file_object.writerows(zip(ids, predictions))
# 關閉文件
submission_file.close()
```
## 可視化決策樹
```py
# 加載庫
from sklearn.tree import DecisionTreeClassifier
from sklearn import datasets
from IPython.display import Image
from sklearn import tree
import pydotplus
# 加載數據
iris = datasets.load_iris()
X = iris.data
y = iris.target
# 創建決策樹分類器對象
clf = DecisionTreeClassifier(random_state=0)
# 訓練模型
model = clf.fit(X, y)
# 創建 DOT 數據
dot_data = tree.export_graphviz(clf, out_file=None,
feature_names=iris.feature_names,
class_names=iris.target_names)
# 繪制圖形
graph = pydotplus.graph_from_dot_data(dot_data)
# 展示圖形
Image(graph.create_png())
```

```py
# 創建 PDF
graph.write_pdf("iris.pdf")
# 創建 PNG
graph.write_png("iris.png")
# True
```