matplotlib グラフ作成Tips (1) グリッド＆グラフの横に表

## 1. はじめに
最近仕事で作ったグラフを紹介します．

作品は以下の画像です．
このグラフの作図上のポイントは以下の３点です．

+ グラフの右側に表形式でプロット点を記載している
+ グリッドを補助目盛りにも入れている
+ 右下に複数行の文字列をボックスの中に表示している

![fig_RHV.png](https://qiita-image-store.s3.amazonaws.com/0/129300/061a33c1-cd71-5ea0-2292-969f4d3477b4.png)

## 2. グラフ作成の解説
### (1) モジュールのインポートする
+ numpy と matplotlib pyplot はセットで読み込む
+ グリッドを制御するため ticker を読み込む
+ 線形補間を行うため scipy interpolate を読み込む

```python:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as tick
from scipy import interpolate
```

### (2) 作図用データを作成
後ほど図に表示するための数値 s_gross と s_effec を計算しておく

```python:
_el=np.array([48,58,59,60,61,62,64,66,68,70,72,74,76])
_vv=np.array([0,10138,33792,106516,826754,1621125,3359842,5239851,7223138,9396419,11083948,13273548,15852459])
s_gross=_vv[8]/1e6
s_effec=(_vv[8]-_vv[3])/1e6
```

### (3) 線形補間（おまけ）
データが飛び飛びなので，0.5mピッチで線形補間する

```python:
f=interpolate.interp1d(_el,_vv)
el=np.arange(48,76+0.5,0.5)
vv=f(el)
```

### (4) 表を作成するためのデータをリストに保存

```python:
str1=[]
str2=[]
for i in range(0,len(_el)):
    str1=str1+['{0:6.1f}'.format(_el[i])]
    str2=str2+['{0:,}'.format(_vv[i])]
str1=str1+['(m)']
str2=str2+['Volume (m$^3$)']
str1=str1+['EL.']
str2=str2+['Cumulative']
```

### (5) 他の計算で使うため，0.5mピッチで線形補間したデータをファイルに保存．

```python:
fnameW='inp_RHV.txt'
fw=open(fnameW,'w')
print('{0}'.format(1),file=fw)
for i in range(0,len(el)):
    print('{0:6.1f}{1:12.0f}'.format(el[i],vv[i]),file=fw)
fw.close()
```

### (6) 作図領域の定義
フォントサイズ（fsz），画像の保存ファイル名（fnameF），画像サイズなどを指定．
ここで，グラフ部分を画像領域の左70%としてax1で，表部分を画像領域の右30%としてax2で定義する．

```python:
fsz=16
fnameF='fig_RHV.png'
fig=plt.figure(figsize=(10,6),facecolor='w')
plt.rcParams["font.size"] = fsz
plt.rcParams['font.family'] ='sans-serif'
ax1=fig.add_axes((0.0, 0.0, 0.7, 1.0))
ax2=fig.add_axes((0.7, 0.0, 0.3, 1.0))
```

### (7) ax1の描画
#### a. グリッド描画
ここで，グリッドの描画は以下で行っている．

+ 横軸主目盛り間隔をdv=1，縦軸主目盛り間隔をde=2とする．
+ 横軸補助目盛り間隔をdv/2=0.5，縦軸補助目盛り間隔をde/2=1とする
+ gridで主目盛り・補助目盛り双方を灰色の実線で描画する．

```python:
ax1.xaxis.set_major_locator(tick.MultipleLocator(dv))
ax1.yaxis.set_major_locator(tick.MultipleLocator(de))
ax1.xaxis.set_minor_locator(tick.MultipleLocator(dv/2))
ax1.yaxis.set_minor_locator(tick.MultipleLocator(de/2))
ax1.grid(which='both',color='#999999',linestyle='solid')
```

#### b. グラフ右下の枠内への文字列の表示

+ 文字列s1およびs2を定義
+ 文字列s1とs2を```\n```で連結する（これで２行表示となる）
+ propsで文字列を書き込む枠の形式を定義
+ textで文字列描画．この際transformを用いグラフ領域の相対座標（左下が[0,0]，右上が[1,1]）で文字列描画位置を指定する


```python:
s1='Gross storage     : {0:4.2f} mil.m$^3$'.format(s_gross)
s2='Effective storage : {0:4.2f} mil.m$^3$'.format(s_effec)
textstr=s1+'\n'+s2
props = dict(boxstyle='round', facecolor='#ffffff', alpha=1)
ax1.text(0.95, 0.05, textstr, transform=ax1.transAxes, fontsize=fsz,va='bottom',ha='right', bbox=props)
```

### (8) ax2に表を描画する
まず描画範囲を指定し，axis('off')で座標軸を描画しないようにする．


```python:
xmin=0; xmax=5
ymin=0; ymax=len(str1)
ax2.axis('off')
ax2.set_xlim(xmin,xmax)
ax2.set_ylim(ymin,ymax)
```

リストに格納した文字列を描画後，以下により横線と縦線を描画．
複数の横線や縦線を引くにはhlines, vlines が便利．


```python:
seqy=np.array([ymax,ymax-2,ymin])
seqx=np.array([2])
plt.hlines(seqy, xmin+0.3, xmax-0.3, colors="k", linestyle="solid")
plt.vlines(seqx, ymin, ymax, colors="k", linestyle="solid")
```



## 3. プログラム全文．

```python:
# =======================================
# Reservoir Capacity Curve
# =======================================
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as tick
from scipy import interpolate

# latest (28 August 2017)
_el=np.array([48,58,59,60,61,62,64,66,68,70,72,74,76])
_vv=np.array([0,10138,33792,106516,826754,1621125,3359842,5239851,7223138,9396419,11083948,13273548,15852459])
s_gross=_vv[8]/1e6
s_effec=(_vv[8]-_vv[3])/1e6

f=interpolate.interp1d(_el,_vv)
el=np.arange(48,76+0.5,0.5)
vv=f(el)

str1=[]
str2=[]
for i in range(0,len(_el)):
    str1=str1+['{0:6.1f}'.format(_el[i])]
    str2=str2+['{0:,}'.format(_vv[i])]
str1=str1+['(m)']
str2=str2+['Volume (m$^3$)']
str1=str1+['EL.']
str2=str2+['Cumulative']


fnameW='inp_RHV.txt'
fw=open(fnameW,'w')
print('{0}'.format(1),file=fw)
for i in range(0,len(el)):
    print('{0:6.1f}{1:12.0f}'.format(el[i],vv[i]),file=fw)
fw.close()


fsz=16
fnameF='fig_RHV.png'
fig=plt.figure(figsize=(10,6),facecolor='w')
plt.rcParams["font.size"] = fsz
plt.rcParams['font.family'] ='sans-serif'
ax1=fig.add_axes((0.0, 0.0, 0.7, 1.0))
ax2=fig.add_axes((0.7, 0.0, 0.3, 1.0))

vmin=0.0;vmax=16.0;dv=1.0
emin=48.0;emax=78.0;de=2.0
ax1.set_xlabel('Volume ($\\times 10^6\: m^3$)')
ax1.set_ylabel('Elevation (EL.m)')
ax1.set_xlim(vmin,vmax)
ax1.set_ylim(emin,emax)

ax1.xaxis.set_major_locator(tick.MultipleLocator(dv))
ax1.yaxis.set_major_locator(tick.MultipleLocator(de))
ax1.xaxis.set_minor_locator(tick.MultipleLocator(dv/2))
ax1.yaxis.set_minor_locator(tick.MultipleLocator(de/2))
ax1.grid(which='both',color='#999999',linestyle='solid')

ax1.plot(_vv/1e6,_el,'ko', clip_on=False)
ax1.plot(vv/1e6,el,'k',clip_on=False)
ax1.plot([vmin,vmax],[60,60],color='#ff0000')
ax1.plot([vmin,vmax],[68,68],color='#ff0000')
ax1.text(11.5,60.1,'MOL (EL.60.00)',color='#ff0000',ha='left',va='bottom',fontsize=fsz)
ax1.text(11.5,68.1,'FSL (EL.68.00)',color='#ff0000',ha='left',va='bottom',fontsize=fsz)
s1='Gross storage     : {0:4.2f} mil.m$^3$'.format(s_gross)
s2='Effective storage : {0:4.2f} mil.m$^3$'.format(s_effec)
textstr=s1+'\n'+s2
props = dict(boxstyle='round', facecolor='#ffffff', alpha=1)
ax1.text(0.95, 0.05, textstr, transform=ax1.transAxes, fontsize=fsz,va='bottom',ha='right', bbox=props)

xmin=0; xmax=5
ymin=0; ymax=len(str1)
ax2.axis('off')
ax2.set_xlim(xmin,xmax)
ax2.set_ylim(ymin,ymax)
for i in range(len(str2)):
    if len(str2)-2<=i:
        ax2.text(1.0,i+0.2,str1[i],ha='center',va='bottom',fontsize=fsz)
        ax2.text(3.5,i+0.2,str2[i],ha='center',va='bottom',fontsize=fsz)
    else:
        ax2.text(xmin+0.5,i+0.2,str1[i],ha='left',va='bottom',fontsize=fsz)
        ax2.text(xmax-0.5,i+0.2,str2[i],ha='right',va='bottom',fontsize=fsz)

seqy=np.array([ymax,ymax-2,ymin])
seqx=np.array([2])
plt.hlines(seqy, xmin+0.3, xmax-0.3, colors="k", linestyle="solid")
plt.vlines(seqx, ymin, ymax, colors="k", linestyle="solid")

plt.savefig(fnameF, dpi=200, bbox_inches="tight", pad_inches=0.1)
plt.show()
```

## 4. もう一つの方法（プログラム全文）
```plt``` だけで書いてしまう．個人的にはこちらのほうが好きです．


```py
# =======================================
# Reservoir Capacity Curve
# =======================================
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as tick
from scipy import interpolate


def drawfig1(_el,_vv,el,vv,fsz):
    xmin=0.0;xmax=16.0;dx=1.0
    ymin=48.0;ymax=78.0;dy=2.0
    plt.xlabel('Volume ($\\times 10^6\: m^3$)')
    plt.ylabel('Elevation (EL.m)')
    plt.xlim(xmin,xmax)
    plt.ylim(ymin,ymax)
    plt.xticks(np.arange(xmin,xmax+dx,dx))
    plt.yticks(np.arange(ymin,ymax+dy,dy))
    plt.gca().xaxis.set_minor_locator(tick.MultipleLocator(dx/2))
    plt.gca().yaxis.set_minor_locator(tick.MultipleLocator(dy/2))
    plt.grid(which='both',color='#999999',linestyle='solid')
    plt.plot(_vv/1e6,_el,'ko', clip_on=False)
    plt.plot(vv/1e6,el,'k-',clip_on=False)
    plt.plot([xmin,xmax],[60,60],color='#ff0000')
    plt.plot([xmin,xmax],[68,68],color='#ff0000')
    plt.text(11.5,60.1,'MOL (EL.60.00)',color='#ff0000',ha='left',va='bottom',fontsize=fsz)
    plt.text(11.5,68.1,'FSL (EL.68.00)',color='#ff0000',ha='left',va='bottom',fontsize=fsz)
    s_gross=_vv[8]/1e6
    s_effec=(_vv[8]-_vv[3])/1e6
    s1='Gross storage     : {0:4.2f} mil.m$^3$'.format(s_gross)
    s2='Effective storage : {0:4.2f} mil.m$^3$'.format(s_effec)
    textstr=s1+'\n'+s2
    props = dict(boxstyle='round', facecolor='#ffffff', alpha=1)
    xs=xmin+0.95*(xmax-xmin); ys=ymin+0.05*(ymax-ymin)
    plt.text(xs,ys, textstr, fontsize=fsz,va='bottom',ha='right', bbox=props)

    
def drawfig2(str1,str2,fsz):
    xmin=0; xmax=5
    ymin=0; ymax=len(str1)
    plt.axis('off')
    plt.xlim(xmin,xmax)
    plt.ylim(ymin,ymax)
    for i in range(len(str2)):
        if len(str2)-2<=i:
            plt.text(1.0,i+0.2,str1[i],ha='center',va='bottom',fontsize=fsz)
            plt.text(3.5,i+0.2,str2[i],ha='center',va='bottom',fontsize=fsz)
        else:
            plt.text(xmin+0.5,i+0.2,str1[i],ha='left',va='bottom',fontsize=fsz)
            plt.text(xmax-0.5,i+0.2,str2[i],ha='right',va='bottom',fontsize=fsz)
    seqy=np.array([ymax,ymax-2,ymin])
    seqx=np.array([2])
    plt.hlines(seqy, xmin+0.3, xmax-0.3, colors="k", linestyle="solid")
    plt.vlines(seqx, ymin, ymax, colors="k", linestyle="solid")

    
def figcontrol(_el,_vv,el,vv,str1,str2):
    fsz=16
    fnameF='fig_RHV.png'
    fig=plt.figure(figsize=(10,6),facecolor='w')
    plt.rcParams["font.size"] = fsz
    plt.rcParams['font.family'] ='sans-serif'
    plt.axes((0.0, 0.0, 0.7, 1.0)); drawfig1(_el,_vv,el,vv,fsz)
    plt.axes((0.7, 0.0, 0.3, 1.0)); drawfig2(str1,str2,fsz)
    plt.savefig(fnameF, dpi=200, bbox_inches="tight", pad_inches=0.1)
    plt.show()
    

def main():
    # latest (28 August 2017)
    _el=np.array([48,58,59,60,61,62,64,66,68,70,72,74,76])
    _vv=np.array([0,10138,33792,106516,826754,1621125,3359842,5239851,7223138,9396419,11083948,13273548,15852459])
    # linear interpolation with elevation interval of 0.5m
    f=interpolate.interp1d(_el,_vv)
    el=np.arange(48,76+0.5,0.5)
    vv=f(el)
    # makeing list for table drawing
    str1=[]
    str2=[]
    for i in range(0,len(_el)):
        str1=str1+['{0:6.1f}'.format(_el[i])]
        str2=str2+['{0:,}'.format(_vv[i])]
    str1=str1+['(m)']
    str2=str2+['Volume (m$^3$)']
    str1=str1+['EL.']
    str2=str2+['Cumulative']
    # data saving for other programs
    fnameW='inp_RHV.txt'
    fw=open(fnameW,'w')
    print('{0}'.format(1),file=fw)
    for i in range(0,len(el)):
        print('{0:6.1f}{1:12.0f}'.format(el[i],vv[i]),file=fw)
    fw.close()
    # figure control    
    figcontrol(_el,_vv,el,vv,str1,str2)


#==============
# Execution
#==============
if __name__ == '__main__': main()
```


以　上