12
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

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

1. はじめに

最近仕事で作ったグラフを紹介します.

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

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

fig_RHV.png

2. グラフ作成の解説

(1) モジュールのインポートする

  • numpy と matplotlib pyplot はセットで読み込む
  • グリッドを制御するため ticker を読み込む
  • 線形補間を行うため scipy interpolate を読み込む
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as tick
from scipy import interpolate

(2) 作図用データを作成

後ほど図に表示するための数値 s_gross と s_effec を計算しておく

_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ピッチで線形補間する

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

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

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ピッチで線形補間したデータをファイルに保存.

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で定義する.

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で主目盛り・補助目盛り双方を灰色の実線で描画する.
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で連結する(これで2行表示となる)
  • propsで文字列を書き込む枠の形式を定義
  • textで文字列描画.この際transformを用いグラフ領域の相対座標(左下が[0,0],右上が[1,1])で文字列描画位置を指定する
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')で座標軸を描画しないようにする.

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

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

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. プログラム全文.

# =======================================
# 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 だけで書いてしまう.個人的にはこちらのほうが好きです.

# =======================================
# 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()

以 上

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
12
Help us understand the problem. What are the problem?