10
15

More than 3 years have passed since last update.

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

Posted at

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

偏相関行列と独立グラフ

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

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

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

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

1. graphvizのインストール

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

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

terminal
pip install graphviz

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

terminal
conda install -c conda-forge python-graphviz

2. グラフの描き方

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

無向グラフ

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

有向グラフ

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

3. データの準備

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

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

4. 相関行列の作成

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

5. 偏相関行列の作成

こちらのコードをお借りしました。
Hatena Blog はしくれエンジニアもどきのメモ

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

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

6. グラフを描画

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

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

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

ダウンロード (7).png

まとめ

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

レッツトライ

10
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
15