WindowsのJupyter notebookでGraphvizを使う

# 初めに
本記事では

* Graphvizを使ったことがない筆者が**jupyter-notebook**で**Graphviz**を使う

という内容になっています。ちなみにネットにはGraphvizの使い方など色々載っています。例えば

* [GraphVizのエラー対処（GraphViz’s executables not found）](https://niwakomablog.com/graphviz-error-handling/)
* [Graphviz をインストールする](http://ruby.kyoto-wu.ac.jp/info-com/Softwares/Graphviz/)

とかです。上記の2つの記事のどちらで設定しても使えるようになります。サイトを見てみてこれでいいかなと感じたらそれでもぜんぜん大丈夫です。
今回は2つの記事を少しミックスして環境を作っていこうと思います。

# Graphvizとは
初めにGraphvizについて軽く説明していきます。以下のサイトを要約した形になっていると思います。

* [テキストデータをグラフ画像に変換するツール「Graphviz」ことはじめ](https://dev.classmethod.jp/articles/graphviz-beginner/)
* [Graphvizとdot言語でグラフを描く方法のまとめ](Graphvizとdot言語でグラフを描く方法のまとめ)

GraphvizとはGraph Visualization Softwareの略語で、グラフを作成する用のツールです。
dot言語と呼ばれるデータ記述言語を使って記述したテキストファイルを画像ファイルとして変換し出力することが出来ます。
機械学習の決定木を描画する時とかにも使われ、「Windows、Mac、Linux」に対応しています。
ちなみにdotとは有向グラフを描画するプログラムのことらしいです。
　　　　　　　　　　　　![fig01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654472/8a1d1b61-1c19-7aaa-aa5b-b959cb46146a.png)
> 出典：[統計的テキスト解析(6)～語のネットワーク分析～](https://www1.doshisha.ac.jp/~mjin/R/61/61.html)

上の図が有向グラフと無向グラフの違いです。言葉で説明すると、ある頂点から別の頂点への特定の方向が決められているかいないかです。上の図の矢印があるかないかですね。
つまり、それぞれの頂点の関係性が固定されているかされていないかということです。
例を挙げるとするならば、有向グラフはハイパーリンク、無向グラフは電車の路線図などではないでしょうか。ハイパーリンクはクリックしたら元のページに戻ることができず<font color="gray">（戻るボタンは考えないで下さい笑）</font>、電車はそれぞれの隣り合った駅を自由に行き来することができます。

## Graphvizインストール
今回試したインストール方法は2つあります。ちなみに公式サイトのダウンロードのページに行くと上記サイトで紹介されていたものとは違いました。

* 公式サイトからダウンロードする方法
* ~~zipファイルを落とす方法~~　zipファイル先のリンクが死んでました

この2つの方法です。
### 公式サイトからダウンロード
まず、[こちら](http://www.graphviz.org/)の公式サイトのリンクをクリックしてください。
次にダウンロードをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654472/423664ff-e0a6-60d0-62b5-66055161f165.png)
WindowsのところのGraphviz Windows packagesをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654472/22714ede-0044-4ff2-dc6e-d1e7bc2595ab.png)
githubのページに飛ぶので「2.38yaml」というファイルを選択し
Urlのところに貼ってあるリンクを検索したら勝手にダウンロードされます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654472/e6ed0e2b-7d1e-a64d-3c47-9e8ffce8e595.png)
あとはインストーラーを起動してインストールを開始してください。[こちら](https://niwakomablog.com/graphviz-error-handling/)のサイトを参考にしてもいいかもしれません。「everyone」のところにチェックを付けるかどうかはパソコンのアカウントの数で判断してみてください。

~~### zipファイルを落とす
zipファイルを落とすのは[こちら](http://ruby.kyoto-wu.ac.jp/info-com/Softwares/Graphviz/)のサイトから落とせます。落としたら、そのファイルを展開するだけで大丈夫です。~~

# 実装
今回はタイタニック号の生き残りのデータセットを使って決定木のグラフの可視化をしていきます。このコードは[こちら](https://qiita.com/5sigma_AAA/items/0c23907da9330681147b#%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%81%AE%E5%AE%9F%E8%A3%85)の方のコードを参考にしています。jupyter風に分けて書いていこうと思います。

まずは必要ライブラリの読み込みです。(userの部分は各自のアカウント名です)

```.py
from sklearn.tree import DecisionTreeClassifier 
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_curve, auc, accuracy_score

import pandas as pd

from sklearn.tree import export_graphviz
import pydotplus 
from io import StringIO
from IPython.display import Image

# データの読み込み 
train = pd.read_csv("/Users/user/jupyter/train.csv") 
```

基本的に一般のデータセットは欠損値があるそうなので確認

```.py
train.isnull().sum()
```

> PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
Age            177
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          687
Embarked         2

Age、Cabin、Embarkedに欠損値があるようです。今回はCabinは使わないので無視します。
Ageの欠損値にはAge全体の平均を補完し、Embarkedには最頻値の"S"を補完します。

```.py
train["Age"] = train["Age"].fillna(train["Age"].median())
train["Embarked"] = train["Embarked"].fillna("S")
```

学習する際には、int型にしないといけないので文字列の'Sex'と'Embarked'を数字に変換します。今回は手作業で数字を割り当てていますが、ダミー変数を使っても変換可能です。

```.py
#カテゴリ変数の変換
train['Sex'] = train['Sex'].apply(lambda x: 1 if x == 'male' else 0)
train['Embarked'] = train['Embarked'].map( {'S': 0 , 'C':1 , 'Q':2}).astype(int)
```

使わないクラスを消します。

```.py
train = train.drop(['Cabin','Name','PassengerId','Ticket'],axis =1)
```

データの前処理が終わったので一旦欠損値がないか確認します。

```.py
train.isnull().sum()
```

> Survived    0
Pclass      0
Sex         0
Age         0
SibSp       0
Parch       0
Fare        0
Embarked    0

欠損値をなくすことができました。次にtrain用のデータセットとtest用のデータセットを分けていきます。テストの画像の振り分けは全体の3割にしました。

```.py
#学習データとテストデータに分割
train_X = train.drop('Survived',axis = 1)
train_y = train.Survived
(train_X , test_X , train_y , test_y) = train_test_split(train_X, train_y , test_size = 0.3 , random_state = 0)
```

モデルを構築し、学習を行います。深さは3層にしました。

```.py
#train
model = DecisionTreeClassifier(max_depth=3,random_state = 0)
model.fit(train_X , train_y)

#accuracy
pred = model.predict(test_X)
fpr, tpr, thresholds = roc_curve(test_y , pred,pos_label = 1)
auc(fpr,tpr)
print("accuracy=",accuracy_score(pred,test_y)
```

> accuracy=0.8208955223880597

ここからGraphvizで描画していきます。

```.py
# 文字列をファイルオブジェクトのように扱うための処理をする
dot_data = StringIO() 

export_graphviz( 
    model, out_file=dot_data, 
    feature_names=train_X.columns,
    class_names=["Death", "Survival"]
) 

graph = pydotplus.graph_from_dot_data(dot_data.getvalue()) 
#graphvizをダウンロードしたディレクトリの絶対パスを指定 
graph.progs = {'dot': u"C:\\Users\\user\\anaconda3\\bin\\release\\bin\\dot.exe"}
# ノートブックに可視化 
Image(graph.create_png()) 
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654472/6cf69ad9-4316-3ef9-60f6-ea2857e3e4cb.png)

決定木を描画することができました。

# 最後に
今回はjupyterを使って行いましたが、もちろん他のIDEでも可能です。ちなみに、一番苦労したのは公式サイトからインストールファイルを見つけることでした。