【Python】4変数以上の可視化ってどうするの？

#この記事の目的
データ分析において, 沢山の変数の関係性を一度に確認したい場合はどうすればいいのでしょうか？
```pairplot```が代表的だとおもいますが, もっとパッ！とまとめられないかと思っていたら最近, **Sankey Diagram**というものを知ったので描いてみました. 

**追記：**
<font color="red">記事最後の追記部分を先に読んでください.</font>


# Plotly Sankey Diagram の使い方

```Plotly``` を使えばかけるそうなので, まずは[公式サイト](https://plot.ly/python/sankey-diagram/)のサンプルコードを丸写しして, 動くか確認してみます. 

```python
import plotly.graph_objects as go

fig = go.Figure(data=[go.Sankey(
    node = dict(
      pad = 15,
      thickness = 20,
      line = dict(color = "black", width = 0.5),
      label = ["A1", "A2", "B1", "B2", "C1", "C2"],
      color = "blue"
    ),
    link = dict(
      source = [0, 1, 0, 2, 3, 3], # indices correspond to labels, eg A1, A2, A2, B1, ...
      target = [2, 3, 3, 4, 4, 5],
      value = [8, 4, 2, 8, 4, 2]
  ))])

fig.update_layout(title_text="Basic Sankey Diagram", font_size=10)
fig.show()

```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540956/4ffb97bd-cc9d-a7ca-02b7-c7cd721c06d5.png)


動きました！
マウスを重ねるとその部分の詳細まで表示されるのが良いですね！

```matplotlib```や```seaborn```に比べてコードが長くて大変ですが, 重要なのは以下の部分です. 

```python
label = ["A1", "A2", "B1", "B2", "C1", "C2"],
```


```python
source = [0, 1, 0, 2, 3, 3], 
target = [2, 3, 3, 4, 4, 5],
value = [8, 4, 2, 8, 4, 2]
```

例えば, 上のSankey Diagram図で, ```source:A1, target:B2, 2.00```となっているのは, 下図linkの中の3つのリストのオレンジ部分が対応しています. 
「```label[0]```から```label[3]```に```2```だけ流れる」ということですね.

![image01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540956/d96dc671-3df9-d971-bde3-d1481488b744.png)

このようにノードの始点と終点, そしてそこを流れるフローの量を指定するリストを作成できれば, Sankey Diagramは描けます！

# データフレームからSankey Diagram を描く
ということで, 本題のデータフレームからSankey Diagram作成に入っていきます. 

先に結果をお見せすると, 今回は, タイタニック号のデータを用いて以下のような図を作成しました. 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540956/fd9b811c-c749-ec3d-901a-1e30f4da4680.png)


# 解説
ライブラリを読み込みます. 

```python
import numpy as np
import pandas as pd
import plotly.graph_objects as go
```
データをダウンロードしてきます. 

```python
!wget https://web.stanford.edu/class/archive/cs/cs109/cs109.1166/stuff/titanic.csv
```

データを読み込み, 今回はカテゴリ変数と整数値の変数だけ表示するので, 変数名を絞ります. 

```python
filename = "/content/titanic.csv"
df = pd.read_csv(filename, encoding='utf-8')

cate_list = ["Survived", "Pclass", "Sex", "Siblings/Spouses Aboard", "Parents/Children Aboard"]

n = len(cate_list)
```


次に```label_list```を作成します. 


```python
label_list = []
source_list = []
target_list = []
value_list = []

for cate in cate_list:
    tmp_label_list=[]
    for v in df[cate].unique():
        lab = "{0}={1}".format(cate, v)
        tmp_label_list.append(lab)
        tmp_label_list.sort()
    label_list.extend(tmp_label_list)
```

link情報の3つのリストを作成します. 

```python
for i in range(n-1):
    source_cate = cate_list[i]
    target_cate = cate_list[i+1]

    for sc in df[source_cate].unique():
        for tc in df[target_cate].unique():

            v = sum((df[source_cate]==sc) & (df[target_cate]==tc))
            source_lab = "{0}={1}".format(source_cate, sc)
            target_lab = "{0}={1}".format(target_cate, tc)

            source_list.append(source_lab)
            target_list.append(target_lab)
            value_list.append(v)
```

最後に, ```source_list```と```target_list```はindexで指定しなければいけないので, 
```label_list```を参照して変換します. 

```python
source_list = [label_list.index(si) for si in source_list]
target_list = [label_list.index(ti) for ti in target_list]
```

あとは, サンプルと同じコードを実行するだけです. 

```python
fig = go.Figure(data=[go.Sankey(
    node = dict(
      pad = 15,
      thickness = 20,
      line = dict(color = "black", width = 0.5),
      label = label_list,
      color = "blue"
    ),
    link = dict(
      source = source_list,
      target = target_list,
      value = value_list
  ))])

fig.update_layout(title_text="Basic Sankey Diagram", font_size=10)
fig.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540956/b9dec5b7-8624-b3fa-9074-65a16b3e32aa.png)

以上！

# コード一覧

<details>
<summary>一覧を表示</summary><div>


```python
import numpy as np
import pandas as pd

!wget https://web.stanford.edu/class/archive/cs/cs109/cs109.1166/stuff/titanic.csv

filename = "/content/titanic.csv"
df = pd.read_csv(filename, encoding='utf-8')

cate_list = ["Survived", "Pclass", "Sex", "Siblings/Spouses Aboard", "Parents/Children Aboard"]

n = len(cate_list)

label_list = []
source_list = []
target_list = []
value_list = []

for cate in cate_list:
    tmp_label_list=[]
    for v in df[cate].unique():
        lab = "{0}={1}".format(cate, v)
        tmp_label_list.append(lab)
        tmp_label_list.sort()
    label_list.extend(tmp_label_list)


for i in range(n-1):
    source_cate = cate_list[i]
    target_cate = cate_list[i+1]

    for sc in df[source_cate].unique():
        for tc in df[target_cate].unique():

            v = sum((df[source_cate]==sc) & (df[target_cate]==tc))
            source_lab = "{0}={1}".format(source_cate, sc)
            target_lab = "{0}={1}".format(target_cate, tc)

            source_list.append(source_lab)
            target_list.append(target_lab)
            value_list.append(v)

source_list = [label_list.index(si) for si in source_list]
target_list = [label_list.index(ti) for ti in target_list]

fig = go.Figure(data=[go.Sankey(
    node = dict(
      pad = 15,
      thickness = 20,
      line = dict(color = "black", width = 0.5),
      label = label_list,
      color = "blue"
    ),
    link = dict(
      source = source_list,
      target = target_list,
      value = value_list
  ))])

fig.update_layout(title_text="Basic Sankey Diagram", font_size=10)
fig.show()
```

</div></details>

# あとがき
実は, 自分でコードを書いてみて気づいたのですが, この図はある変数の前後の変数との関係性しかわかりません. 
上図の例だと, ```Survived```と```Pclass```や, ```Pclass```と```Sex```の関係性は分かっても, ```Survived```と```Sex```がわかりません. 色などで分ければ, 3変数までは表現できそうですが, それ以上となるとやはり無理そうです. 

(あれ, これって4次元以上の可視化になってなくない...？)

もっとよい方法をご存じの方は, コメントで教えていただけると幸いです. 

# 追記
上でいろいろやっていますが, ドキュメントを読んでいたらもっと簡単な方法がありました.

```python
fig = px.parallel_categories(df, dimensions=cate_list, color='Survived')
fig.show()
```

![plot03.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540956/a08daa4a-8de4-e6e7-2730-68fe12682b03.gif)

変数の順番や、要素の順番まで動かせるのは凄いですね！！

以上！

# 参考

Plotly：[Sankey Diagram in Python](https://plot.ly/python/sankey-diagram/)
Plotly：[basic-parallel-category-diagram-with-plotlyexpress](https://plotly.com/python/parallel-categories-diagram/#basic-parallel-category-diagram-with-plotlyexpress)
CS109：[A Titanic Probability](https://web.stanford.edu/class/archive/cs/cs109/cs109.1166/problem12.html)