2
1

More than 1 year has passed since last update.

Python Matplotlibで変数が超多い時にペアプロットを描きたい

Last updated at Posted at 2021-10-03

Matplotlibで変数が超多い時にペアプロットを描きたい

ペアプロット

ペアプロット(散布図行列)を描いて各説明変数間の相関性を確認し、100近い説明変数から次元削減できたらなと思いました。

Seabornで結果が返ってこない!

単純に

import seaborn as sns
sns.pairplot(X)

としてやって、アヤメの分類なんかでペアプロットを出力しているサンプルはWeb上にいっぱいあります。しかしながら説明変数が100近くある現在取り組んでいる問題では、いつもは川底の石ほど冷たいMacBook Airが岩盤浴の床のごとく熱い状態で何時間経っても結果が返ってきませんでした。
そもそもそんなに変数がある状況なら、相関係数行列なりPCA使うなりして次元削減する方が賢いのでは?と思いながらも、変数間の関係性を示す最も古典的な手法が出力できない状態は健全ではないため、出力できるようにしました。

前提条件と考え方

  • 元々のデータはCSVで保存してあり、Pandasデータフレームとして読み込みクロスバリデーションをかけています。
  • 今回の説明変数の数が92個でした。
  • 92個=4x23個と捉えて、4つの23x23の変数行列について全組み合わせで散布図もしくはヒストグラムを描き、画像で保存
  • 出来上がった画像を繋ぎ合わせて、ペアプロットを完成させる

試した環境

  • Computer: Apple MacBook Air Late 2020 M1 (Apple Silicon ARM)
  • OS: Mac OS Big Sur:version 11.6
  • Anaconda:version 1.10.0
  • Jupyter Lab:version 2.2.6
  • Python:version 3.8.8

分割したペアプロットグラフ画像の生成

import pandas as pd
import numpy as np
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()

X = x_train
#(0,1)で正規化
X.loc[:,:] = scaler.fit_transform(X)

matrix_size = 23 #X.shape[1] = 92 = 23 * 4
matrix_num = 4

#16分割した画像1枚の大きさ
width = 100
height = 100

for i in range(matrix_num):
    for j in range(matrix_num):
        fig, axes = plt.subplots(matrix_size, matrix_size, figsize=(width, height))
        fig.patch.set_facecolor('white')
        #画像1枚中のグラフを描く変数たち
        element_i = X.iloc[:, i * matrix_size : (i + 1) * matrix_size]
        element_j = X.iloc[:, j * matrix_size : (j + 1) * matrix_size]
        #画像1枚中のグラフを描く変数たちの名前
        col_name_i = element_i.columns
        col_name_j = element_j.columns

        element_i.to_numpy()
        element_j.to_numpy()

        for m in range(matrix_size):
            for n in range(matrix_size):
                axes[m][n].set_title(col_name_i[m] +' vs '+ col_name_j[n] , loc='center')
                axes[m][n].set_ylabel(col_name_j[n])
                #画像を16分割しているので、i=jかつ各画像でm=nの時はヒスト
                #それ以外は散布図
                if i == j and m == n:
                    axes[m,n].set_xlim(0, 1)
                    axes[m,n].set_ylim(0, 100) #ヒストの時のy軸は訓練データ数より多い数
                    axes[m,n].hist(element_i.iloc[:,m])
                else:
                    axes[m,n].set_xlim(0, 1)
                    axes[m,n].set_ylim(0, 1)
                    axes[m,n].scatter(element_i.iloc[:,m],element_j.iloc[:,n])
        figname = 'hoge_i'+str(i)+'_j'+str(j)+'.png'
        fig.savefig(figname, bbox_inches='tight', pad_inches=0)

中間結果1:上記コードでi=jの時の出力
hoge_i0_j0.png
中間結果2:上記コードでi≠jの時の出力
hoge_i0_j1.png

画像を合成してペアプロットを完成させる

参考にしたサイト:

import numpy as np
import cv2
import glob
from PIL import Image
from natsort import natsorted

files = glob.glob("/hogehogehoge/hoge" + "/*.png")
# 空のリストを準備
d = []

# natsortedで自然順(ファイル番号の小さい順)に1個づつ読み込む
for i in natsorted(files):
    img = Image.open(i)    # img は'JpegImageFile' object
    img = np.asarray(img)  # np.asarrayで img を ndarray に変換
    d.append(img)          # d にappend で img を追加

# 画像の高さ方向と幅方向を結合
#4枚ずつ繋げていく
img_x = np.vstack((np.hstack(d[0:4]),
                   np.hstack(d[4:8]),
                   np.hstack(d[8:12]),
                   np.hstack(d[12:16]),
                  ))
img_x = cv2.cvtColor(img_x, cv2.COLOR_BGR2RGB)
cv2.imshow('img_after', img_x)
cv2.imwrite('/hogehogehoge/hoge/hoge/result.png', img_x)

cv2.waitKey(0)
cv2.destroyAllWindows()

結果

92x92=8464枚の圧巻のグラフ(ヒストグラム92枚、散布図8372枚)が完成しました。

image.png

2
1
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
2
1