偏相関行列を作って独立グラフを描く

graphvizで独立グラフを描画する手順を紹介します

## 偏相関行列と独立グラフ

相関が観測される原因には以下の2つがあります

- 因果関係がある場合
- 因果関係がある共通の要因がある場合

後者の影響を除いてから相関係数を求めるのが偏相関で、偏相関が高い要因同士を繋いで図示するのが独立グラフです。詳しくは以下を参照してください。

偏相関係数の意味と式の導出
https://mathtrain.jp/partialcor

## 1. graphvizのインストール

確認しきれてませんが多分以下で入ると思います

#### graphvizのpythonラッパーをpipでインストールします

```terminal:terminal
pip install graphviz
```
#### graphvizの本体をインストールしてjupyter notebookから使えるようにします

```terminal:terminal
conda install -c conda-forge python-graphviz
```

## 2. グラフの描き方

以下のようにnode()でノードを定義し、edge()で連結を定義して書くことができます。render()を実行すると一旦graphvizソースコードを書き出し、それに基づいてpngまたはpdfでグラフを書き出します。cleanup=Trueだと画像ファイルを書き出した後に以下ではpngで書き出ししてから

#### 無向グラフ

```python:python
from graphviz import Graph

g = Graph(format='png')

g.node('1')
g.node('2')
g.node('3')
g.edge('1', '2')
g.edge('2', '3')
g.edge('3', '1')

g.render(filename='../test', format='png', cleanup=True, directory=None)
display(Image.open('../test.png'))
```
![ダウンロード (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/a2a6d828-66ad-0213-49a1-687f9ec571b7.png)

#### 有向グラフ

```python:python
from graphviz import Digraph

dg = Digraph(format='png')

dg.node('1')
dg.node('2')
dg.node('3')
dg.edge('1', '2')  # 1 -> 2
dg.edge('2', '3')  # 2 -> 3
dg.edge('3', '1')  # 3 -> 1

dg.render(filename='../test', format='png', cleanup=True, directory=None)
display(Image.open('../test.png'))
```
![ダウンロード (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/305ad3aa-b339-e2b7-a331-c1c1a7c0d6a8.png)

## 3. データの準備

今回はサンプルデータとしてirisを使います

```python:python
import numpy as np
import pandas as pd
from sklearn import datasets
import seaborn as sns

iris = datasets.load_iris()
df = pd.DataFrame(np.hstack([iris.data, iris.target.reshape(-1, 1)]), 
                  columns=iris.feature_names + ['label'])
sns.pairplot(df, hue='label')
```
![ダウンロード (8).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/4d55ceeb-d6ff-83ad-1a42-2c1d6c18e175.png)

## 4. 相関行列の作成

```python:python
import matplotlib.pyplot as plt

cm = pd.DataFrame(np.corrcoef(df.T), columns=df.columns, index=df.columns)

sns.heatmap(cm, annot=True, square=True, vmin=-1, vmax=1, fmt=".2f", cmap="RdBu")
plt.savefig("pcor.png")
plt.show()
```
![ダウンロード (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/e59299dd-2857-7171-d36c-236b6022c7d5.png)

## 5. 偏相関行列の作成

こちらのコードをお借りしました。
<a href="https://cartman0.hatenablog.com/entry/2020/01/07/python%E3%81%A7%E5%81%8F%E7%9B%B8%E9%96%A2%E4%BF%82%E6%95%B0%E8%A1%8C%E5%88%97%28pcor%29%E3%82%92%E8%A8%88%E7%AE%97">Hatena Blog はしくれエンジニアもどきのメモ</a>

もう少していねいに検定して有意にならない相関を差し引かないやり方もあるようですが、ここでは一律で引いたものになります。

```python:python
import scipy

def cor2pcor(R):
    inv_cor = scipy.linalg.inv(R)
    rows = inv_cor.shape[0]
    regu_1 = 1 / np.sqrt(np.diag(inv_cor))
    regu_2 = np.repeat(regu_1, rows).reshape(rows, rows)
    pcor = (-inv_cor) * regu_1 * regu_2
    np.fill_diagonal(pcor, 1)
    return pcor

pcor = pd.DataFrame(cor2pcor(cm), columns=cm.columns, index=cm.index)

sns.heatmap(pcor, annot=True, square=True, vmin=-1, vmax=1, fmt=".2f", cmap="RdBu")
plt.savefig("pcor.png")
plt.show()
```
![ダウンロード (5).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/632a9f73-d1ec-30c9-9d3a-73b3d006185d.png)

## 6. グラフを描画

適当に設定した閾値より相関係数の絶対値が大きいところを連結して無向グラフを描画します

```python:python
from graphviz import Graph
from PIL import Image

def draw_graph(cm, threshold):
    edges = np.where(np.abs(cm) > threshold)
    edges = [[cm.index[i], cm.index[j]] for i, j in zip(edges[0], edges[1]) if i > j]

    g = Graph(format='png')
    for k in range(cm.shape[0]):
        g.node(cm.index[k])

    for i, j in edges:
        g.edge(j, i)

    g.render(filename='../test', format='png', cleanup=True, directory=None)
    display(Image.open('../test.png'))

threshold = 0.3
draw_graph(cm, threshold)
draw_graph(pcor, threshold)
```

#### 相関行列から作ったグラフ

![ダウンロード (6).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/1600802a-0c58-f07c-7f39-b114ebf6d47b.png)

#### 偏相関行列から作ったグラフ

![ダウンロード (7).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/2233a07c-f7e4-449b-fa3e-5637e27d1a42.png)

## まとめ

相関係数が低いのでこれだけで結論するのはちょっと難しそうですが、これが正しいとするとガクの長さや幅は花弁の長さや幅と相関してるだけでアヤメの種類とは直接相関していないという事のようです。相関行列を眺めるよりグラフにした方がイメージがわきやすいのが良いですね。

レッツトライ