graphvizで独立グラフを描画する手順を紹介します
偏相関行列と独立グラフ
相関が観測される原因には以下の2つがあります
- 因果関係がある場合
- 因果関係がある共通の要因がある場合
後者の影響を除いてから相関係数を求めるのが偏相関で、偏相関が高い要因同士を繋いで図示するのが独立グラフです。詳しくは以下を参照してください。
偏相関係数の意味と式の導出
https://mathtrain.jp/partialcor
1. graphvizのインストール
確認しきれてませんが多分以下で入ると思います
graphvizのpythonラッパーをpipでインストールします
pip install graphviz
graphvizの本体をインストールしてjupyter notebookから使えるようにします
conda install -c conda-forge python-graphviz
2. グラフの描き方
以下のようにnode()でノードを定義し、edge()で連結を定義して書くことができます。render()を実行すると一旦graphvizソースコードを書き出し、それに基づいてpngまたはpdfでグラフを書き出します。cleanup=Trueだと画像ファイルを書き出した後に以下ではpngで書き出ししてから
無向グラフ
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'))
有向グラフ
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. データの準備
今回はサンプルデータとしてirisを使います
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')
4. 相関行列の作成
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()
5. 偏相関行列の作成
こちらのコードをお借りしました。
Hatena Blog はしくれエンジニアもどきのメモ
もう少していねいに検定して有意にならない相関を差し引かないやり方もあるようですが、ここでは一律で引いたものになります。
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()
6. グラフを描画
適当に設定した閾値より相関係数の絶対値が大きいところを連結して無向グラフを描画します
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)
相関行列から作ったグラフ
偏相関行列から作ったグラフ
まとめ
相関係数が低いのでこれだけで結論するのはちょっと難しそうですが、これが正しいとするとガクの長さや幅は花弁の長さや幅と相関してるだけでアヤメの種類とは直接相関していないという事のようです。相関行列を眺めるよりグラフにした方がイメージがわきやすいのが良いですね。
レッツトライ