Matplotlib で 複数グラフを自由自在に配置する！

# Matplotlib で複数のグラフを自由自在に配置する

今回、作成したは以下の図。
<img width="813" alt="Screen Shot 2017-12-24 at 13.29.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/167957/0bfc2675-8381-00f6-fb19-16b45ab02487.png">

上記のような図をmatplotlibで作成するのは意外と難しい。
まず、思いつくのはsubplotを持ちいることである。しかしながら、subplot では同じアスペクトの図しか並べることができない。subplot2grid を利用すると多少は複雑な配置も可能にはなるが、アスペクトの制約はつきまとう。そこで今回は、 add_axesとbbox_inchesを駆使して、上記の図を作成した。

まずは、1パネル分のスクリプト

```py
# coding: UTF-8
import sys
import numpy as np
import matplotlib 
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib import rcParams
rcParams['font.family'] = 'Arial'

if __name__ == "__main__":
    fig = plt.figure(figsize=(3,3)) 
    #add_axes([x軸の開始位置, y軸の開始位置, x軸の長さ(全体に対する比率), y軸の長さ(全体に対する比率)])
    ax1 = fig.add_axes([0.10,0.10,0.60,0.60]) 
    ax2 = fig.add_axes([0.71,0.10,0.20,0.60])#左のヒストグラム図
    ax3 = fig.add_axes([0.10,0.71,0.60,0.20])#上のヒストグラム図

    ax1.spines['top'].set_visible(False)
    ax1.spines['right'].set_visible(False)
    ax1.tick_params(top="off",right="off",direction="out",labelsize=10,pad=5)
    ax1.tick_params(right="off")
    
    ax2.spines['top'].set_visible(False)
    ax2.spines['right'].set_visible(False)
    ax2.spines['bottom'].set_visible(False)
    ax2.set_xticks([]) 
    ax2.set_yticks([])
    
    ax3.spines['top'].set_visible(False)
    ax3.spines['right'].set_visible(False)
    ax3.spines['left'].set_visible(False) 
    ax3.set_xticks([])
    ax3.set_yticks([])
   
    data = np.random.randn(2000,2000)
    ax1.scatter(data.T[0],data.T[1],s=5)
    ax2.hist(data.T[1],range=(-2,2),bins=100,orientation="horizontal") 
    ax3.hist(data.T[1],range=(-2,2),bins=100) 
    ax1.set_xlim(-2,2) 
    ax1.set_ylim(-2,2) 
    ax3.set_xlim(-2,2)
    
    fig.savefig("test.pdf",bbox_inches="tight")
```
Seabornをつかわなくても、こんな風にadd_axesを使えば散布図とヒストグラムを組み合わせた joint plot を作ることができる。ただし、最後にbbox_inches="tight"をつけないとlabel等が少しはみ出ることがある(左図)。つけるとはみ出た部分まで表示してくれる(右図)。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/167957/3e9feed0-f328-ac0a-c32f-a2feeab126e9.png)![image.png](https://qiita-image-store.s3.amazonaws.com/0/167957/5a3593e9-87db-dfb3-4db7-5103c2ca639c.png)

厳密には bbox_inches=tight を指定すると、枠外にはみ出しているいないに関わらず、全てのobjectをきっちり表示してくれるようになる。__枠外にはみ出しているいないに関わらず、全てのobjectをきっちり表示してくれるようになる。__

つまり、add_axesにおいてobjectを 0≤x≤1、0≤y≤1 の枠の中に納める必要なんてものは実はどこにもない、ということで、先のパネルを複数枚並べたいのであれば、もうあとは簡単。add_axes で指定した x,y 座標の始点を並べたい方向に1.0 ずらしてしまえばいい。たとえば、5x5個ならべたいのであれば、以下のようにfor文で回して、、、

```py
# coding: UTF-8
import sys
import numpy as np
import matplotlib 
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib import rcParams
rcParams['font.family'] = 'Arial'

if __name__ == "__main__":
    fig = plt.figure(figsize=(3,3)) 
    for i in range(5):
        for j in range(5):
            ax1 = fig.add_axes([j + 0.10, 5 - i + 0.10,0.60,0.60]) 
            ax2 = fig.add_axes([j + 0.71, 5 - i + 0.10,0.20,0.60])
            ax3 = fig.add_axes([j + 0.10, 5 - i + 0.71,0.60,0.20])

            ax1.spines['top'].set_visible(False)
            ax1.spines['right'].set_visible(False)
            ax1.tick_params(top="off",right="off",direction="out",labelsize=10,pad=5)
            ax1.tick_params(right="off")
            
            ax2.spines['top'].set_visible(False)
            ax2.spines['right'].set_visible(False)
            ax2.spines['bottom'].set_visible(False)
            ax2.set_xticks([]) 
            ax2.set_yticks([])
            
            ax3.spines['top'].set_visible(False)
            ax3.spines['right'].set_visible(False)
            ax3.spines['left'].set_visible(False) 
            ax3.set_xticks([])
            ax3.set_yticks([])
        
            data = np.random.randn(2000,2000)
            ax1.scatter(data.T[0],data.T[1],s=5)
            ax2.hist(data.T[1],range=(-2,2),bins=100,orientation="horizontal") 
            ax3.hist(data.T[1],range=(-2,2),bins=100) 
            ax1.set_xlim(-2,2) 
            ax1.set_ylim(-2,2) 
            ax3.set_xlim(-2,2)
            ax3.set_title(str(1+i*5+j),fontsize=10)
            
    fig.savefig("test.pdf",bbox_inches="tight")
```
とすればOK。