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

#Matplotlibで変数が超多い時にペアプロットを描きたい
### ペアプロット
https://bellcurve.jp/statistics/glossary/5495.html

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

###Seabornで結果が返ってこない！
単純に

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

###前提条件と考え方
* 元々のデータはCSVで保存してあり、Pandasデータフレームとして読み込みクロスバリデーションをかけています。
* 今回の説明変数の数が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

### 分割したペアプロットグラフ画像の生成
```python
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

#１６分割した画像１枚の大きさ
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')
        #画像１枚中のグラフを描く変数たち
        element_i = X.iloc[:, i * matrix_size : (i + 1) * matrix_size]
        element_j = X.iloc[:, j * matrix_size : (j + 1) * matrix_size]
        #画像１枚中のグラフを描く変数たちの名前
        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])
                #画像を１６分割しているので、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)
```
中間結果１：上記コードでi=jの時の出力
![hoge_i0_j0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1230168/1cad1d41-3729-05f0-0be5-3b2ff296ec31.png)
中間結果２：上記コードでi≠jの時の出力
![hoge_i0_j1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1230168/875dcd00-4e2a-dbc9-0d48-e8395ecf644d.png)

###画像を合成してペアプロットを完成させる
参考にしたサイト：

https://tomomai.com/python-opencv-numpy/

```python
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枚の圧巻のグラフ（ヒストグラム９２枚、散布図8372枚）が完成しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1230168/905fc512-aab2-9bfa-859d-d3d6aadeaf8f.png)