Graphical lassoを用いた多変数間の相関分析を爆速で試す

# モチベーション
データを分析していると、たまに以下のような状況に遭遇します。

- 種類の多いデータの関係性を知りたい
- 割とノイズが激しいけど、他のデータと比較したい
- 見せかけの相関は除外したい
- 本当に関係があるものは僅かしかない、という前提をおきたい
- とりあえず何かしらの手法を試したい

こんな時はとりあえず **Graphical lasso** を使ってみよう、というお話です。

# Graphical lasso とは
ざっくりいえば、変数間の関係をグラフ化する手法です。
多変量ガウス分布を前提とした手法ですので、結構色々なところで使える気がします。
詳しくは[この本](https://www.amazon.co.jp/%E7%95%B0%E5%B8%B8%E6%A4%9C%E7%9F%A5%E3%81%A8%E5%A4%89%E5%8C%96%E6%A4%9C%E7%9F%A5-%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%83%97%E3%83%AD%E3%83%95%E3%82%A7%E3%83%83%E3%82%B7%E3%83%A7%E3%83%8A%E3%83%AB%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-%E4%BA%95%E6%89%8B-%E5%89%9B/dp/4061529080)が非常にわかりやすく解説してくれます。理論に興味がある方は是非お手に。

# 実装
今回実装したものは、推定された精度行列を相関行列に変換して、グラフで出力するプログラムになります。
まだまだ改良の余地はありますが、データ分析を進める上での一つの指標にはなると思います。

```python:テストデータ用意
# テストデータを用意する。
from sklearn.datasets import load_boston
boston = load_boston()
X = boston.data
feature_names = boston.feature_names
```


```python:メイン処理
import pandas as pd
import numpy as np
import scipy as sp
from sklearn.covariance import GraphicalLassoCV
import igraph as ig

# 同じ特徴量の中で標準化する。
X = sp.stats.zscore(X, axis=0)

# GraphicalLassoCV を実行する。
model = GraphicalLassoCV(alphas=4, cv=5)
model.fit(X)

# グラフデータ生成する。
grahp_data = glasso_graph_make(model, feature_names, threshold=0.2)

# グラフを表示する。
grahp_data
```

```python:グラフ生成関数
def glasso_graph_make(model, feature_names, threshold):
    # 分散共分散行列を取得する。
    # -> 参考URL：https://scikit-learn.org/stable/modules/generated/sklearn.covariance.GraphicalLassoCV.html
    covariance_matrix = model.covariance_

    # 分散共分散行列を相関行列に変換する。
    diagonal = np.sqrt(covariance_matrix.diagonal())
    correlation_matrix = ((covariance_matrix.T / diagonal).T) / diagonal
    
    # グラフ表示のために対角成分が0の行列を生成する。
    correlation_matrix_diag_zero = correlation_matrix - np.diag( np.diag(correlation_matrix) )
    df_grahp_data = pd.DataFrame( index=feature_names, columns=feature_names, data=correlation_matrix_diag_zero.tolist() )

    # グラフ生成準備
    grahp_data = ig.Graph()
    grahp_data.add_vertices(len(feature_names))
    grahp_data.vs["feature_names"] = feature_names
    grahp_data.vs["label"] = grahp_data.vs["feature_names"]
    visual_style = {}
    edge_width_list = []
    edge_color_list = []

    # グラフ生成
    for target_index in range(len(df_grahp_data.index)):
        for target_column in range(len(df_grahp_data.columns)):
            if target_column >= target_index:
                grahp_data_abs_element = df_grahp_data.iloc[target_index, target_column]
                if abs(grahp_data_abs_element) >= threshold:
                    edge = [(target_index, target_column)]
                    grahp_data.add_edges(edge)
                    edge_width_list.append(abs(grahp_data_abs_element)*10)
                    if grahp_data_abs_element > 0:
                        edge_color_list.append("red")
                    else:
                        edge_color_list.append("blue")

    visual_style["edge_width"] = edge_width_list
    visual_style["edge_color"] = edge_color_list

    return ig.plot(grahp_data, **visual_style, vertex_size=50, bbox=(500, 500), vertex_color="skyblue", layout = "circle", margin = 50)
```

## 結果
![data.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594267/812b2ba8-5690-6ee6-02de-8746c73e9460.png)

しきい値をかなり低めに設定しているせいか、直接相関がそれなりに見つかっていますね。
試しに相関が一番強そうな変数同士を見てみます。
相関係数が最も高いのは、RAD(高速道路へのアクセスしやすさ)とTAX($10,000あたりの不動産税率の総計)です。すなわち、高速道路にアクセスしやすいと税金が高いということですね。
正直、このデータだけではなんとも言えないのですが、まるで見当違いな結果でもなさそうです。


# 最後に
とりあえず試せる環境は最高ですね。
結果については、もう少しデータ数が多く、ノイジーなデータであった方がわかりやすかったかもしれません。どこかにないですかね。

今回は Graphical lasso を用いて変数間の関係性をグラフ化するところまで行いましたが、その先にはグラフ構造に着目した変化検知手法などもあり、まだまだ勉強のし甲斐がある分野だと感じています。

# 注意および免責事項
本記事の内容はあくまで私個人の見解であり、所属する組織の公式見解ではありません。
本記事の内容を実施し、利用者および第三者にトラブルが発生しても、筆者および所属組織は一切の責任を負いかねます。