2. Pythonで綴る多変量解析　7-3. 決定木[回帰木]

*   決定木のもう一つの側面、目的変数を数値データとする**回帰木**を行ないます。
*   目的変数がカテゴリデータの**分類木**は、scikit-learn.treeモジュールの**DecisionTreeClassifier**を使いますが、**回帰木**モデルには**DecisionTreeRegressor**を使います。

###⑴ ライブラリのインポート
```python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

from sklearn.tree import DecisionTreeRegressor # 回帰木モデルを作成するクラス
```

###⑵ データの取得と読み込み
```python
from sklearn.datasets import load_boston
boston_dataset = load_boston()
```

*   データは、scikit-learnに付属の「Boston house prices dataset」を使います。
*   アメリカ北東部の大都市ボストンの住宅事情に関する特徴量が13個、サンプル数は506個です。
*   このデータセットの狙いは、与えられた特徴量を使って住宅価格を予測することです。
*   変数名 MEDV(Median valueの略) という住宅の価格がターゲットであり、13個の特徴量は住宅の価格を予測するための説明変数です。
*   説明変数の内容などの詳細はこちら https://qiita.com/y_itoh/items/aaa2056aac0c270ba7d2

**住宅を特徴づける13の説明変数を使って、住宅の価格を予測する回帰木モデルを構築します。**

*   まず、13の説明変数をデータフレームに変換します。

```python
# 説明変数をDataFrameに格納
boston = pd.DataFrame(boston_dataset.data, columns=boston_dataset.feature_names)

print(boston.head()) # 先頭５行を表示
print(boston.columns) # カラム名を表示
print(boston.shape) # 形状を確認
```
![2_7_3_01.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/bc415819-7567-b73c-5126-64f242d52a5f.png)

*   そこへ、目的変数をカラム名`MEDV`として追加します。

```python
# 目的変数を追加
boston['MEDV'] = boston_dataset.target

print(boston.head()) # 先頭５行を表示
print(boston.shape) # 形状を再確認
```
![2_7_3_02.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/501368c8-5a09-1ffd-4d21-9c875bc3e2f0.png)

*   サンプル数は506、13の説明変数に目的変数を加えて14列になっています。
*   このデータセットをランダムに学習用データセットとテスト用データセットの２つのグループに分割します。

###⑶ データの分割
```python
# データセットをNumpy配列に変換
array = boston.values

# 説明変数と目的変数に分ける
X = array[:,0:13]
Y = array[:,13]
```

*   70%のデータを学習用データ、残りの30%のデータをテストデータとします。

```python
# データを分割するモジュールをインポート
from sklearn.model_selection import train_test_split

# データを分割
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.3, random_state=1234)
```

###⑷ 回帰木モデルの構築

*   分類木の場合と同じく、`fit`メソッドに配列 X と Y を渡して学習させますが、この場合 Y は数値型です。
*   オプションの`max_leaf_nodes`で木をどのくらい成長させるかを指定します。ここでは葉ノードの数を最も多くて 20 までとします。

```python
# モデルのインスタンス生成
reg = DecisionTreeRegressor(max_leaf_nodes = 20)

# 学習によりモデル生成
model = reg.fit(X_train, Y_train)
print(model)
```

![2_7_3_03.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/722d295c-d01c-68b2-4274-801a99ee24c5.png)

###⑸ 回帰木モデルの評価

*   得られた回帰木モデルを、予測の妥当性(➀)、モデル自体の汎用性(➁)という２つの方向からテストします。
*   まず、予測の妥当性を確認するため、サンプル506個の中からランダムに1個を抽出し、その特徴量から予測される価格と実際に観測された価格を比較します。

####➀予測の妥当性を確認する

*   元のデータセットXからランダムにidを1個だけ取り出します。

```python
# Python標準の疑似乱数モジュールをインポート
import random
random.seed(1)

# ランダムにidを選定
id = random.randrange(0, X.shape[0], 1)
print(id)
```

![2_7_3_04.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/656f6aa5-ae90-24f8-9844-50c3f10191e4.png)

```python
# 元のデータセットから該当サンプルを抽出
x = X[id]
x = x.reshape(1,13)

# 説明変数から住宅価格を予測
YHat = model.predict(x)

# 当該idの説明変数をDataFrameに変換
df = pd.DataFrame(x, columns = boston_dataset.feature_names)
# 予測値yを追加
df["Predicted Price"] = YHat
```

![2_7_3_05.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/b14a2854-2775-b704-ba86-cb818758a19e.png)

*   実際に観測された住宅価格を取得し、比較してみます。

```python
boston.iloc[id]
```

![2_7_3_06.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/18b8ee61-588f-12ef-ffcf-7ed151b4f1ec.png)

*   実際の価格17.40に対して、予想価格は20.45という結果となりました。
*   次の検証は、モデルの予測値がどのくらい観測値の情報量を説明できているか。

####➁汎用性の指標として決定係数を確認する

*   決定係数$R^2$は、回帰分析において**観測値$y$に対する予測値$\hat{y}$の説明力**を表す指標で、寄与率ともいいます。
*   0から1までの値をとり、$R^2$が1に近いほどモデルが有効であることを意味します。

```python
# 決定係数を算出する関数をインポート
from sklearn.metrics import r2_score
```

*   テスト用の説明変数(X_test)をモデルに渡して予測値を算出します。

```python
YHat = model.predict(X_test)
```

![2_7_3_07.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/2d983fbb-7946-aa4a-6b71-ad04a7effa56.png)

*   これらの予測値とテスト用の目的変数(Y_test)を渡して決定係数を算出します。

```python
r2 = r2_score(Y_test, YHat)
print("R^2 = ", r2)
```

![2_7_3_08.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/316874cd-6a62-b515-63d6-eb4af63c964a.png)

*   テスト用データにおけるモデルの適合度は0.86、上々の結果となっています。

###⑹ 回帰木モデルの可視化
```python
# sklearnのtreeモジュールをインポート
from sklearn import tree

# Notebook内に画像を表示させるモジュール
from IPython.display import Image

# 決定木モデルを可視化するためのモジュール
import pydotplus
```

```python
# 決定木モデルをDOTデータに変換
dot_data = tree.export_graphviz(model,
                                out_file = None,
                                feature_names = boston_dataset.feature_names,
                                class_names = 'MEDV',
                                filled = True)

# ダイアグラムを描画
graph = pydotplus.graph_from_dot_data(dot_data)  

# ダイアグラムを表示
Image(graph.create_png())
```

![2_7_3_09.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502109/9c951ea7-2f8d-bf0d-1998-38d54badeb56.png)

*   先のランダムに抽出されたサンプル（ID:68）のルートを太線で示しました。
*   ちなみに、親ノードおよび第１階層の子ノードの特徴量「LSTAT」は「lower status」の略で、人口に占める下層階級の割合を意味しています。