So Chigusa

Matplotlib を使いこなす

基本構文

matplotlib を読み込んで、図を描く準備をする部分。

import matplotlib.pyplot as plt

fig = plt.figure()
ax  = fig.add_subplot(1,1,1)

matplotlib.axes.Axes クラスのインスタンスである ax を用いて図を描いたら(下部参照)、図の設定は以下のように可能。

ax.set_title(r'title $\LaTeX$', size=25)
ax.set_xlabel('xlabel', size=25)
ax.set_ylabel('ylabel', size=25)
ax.legend(loc="lower right", fontsize=20)
ax.tick_params(labelsize=20)

最後に PDF に保存する。

plt.tight_layout()
plt.savefig('hoge.pdf', bbox_inches='tight')

LaTeX表記はr'$...$'の形でもともと使用できるが、デフォルトのフォントが気に入らない。 このページを参考にフォントを変更するのがおすすめ。

from matplotlib import rc

rc('mathtext', **{'rm': 'serif',
    'it': 'serif:itelic',
    'bf': 'serif:bold',
    'fontset': 'cm'})

描画データの準備

CSV のような構造のデータが与えられていることをイメージする。 これを numpy で読み込んで使用する場合、

data = np.loadtxt('hoge.dat', skiprows=1, usecols=range(2))
x = data[:,0]
y = data[:,1]

などとする。skiprows オプションを使用することで、好きな数のヘッダー行を読み込み範囲から外せる。 また、pandas を用いる場合には、区切り文字が空白かタブかに応じて、

data = pd.read_csv('hoge.dat')
data = pd.read_table('hoge.dat')

とする。このとき、各データ列のヘッダーに変数名を書いておいて、data['name'] で参照できる。 このデータを numpy リストにも変換できる。

nplist = data.values

基本の1D plot

x,y 両軸のデータのリストをそれぞれ用意して、plot 関数を用いる。

ax.plot(xlist, ylist, color='r', linestyle='-', linewidth=2, label='hoge')

label オプションの文字列がレジェンドとして使用される。軸の範囲の設定は

ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin, ymax)

対数目盛りの設定は

ax.set_xscale('log')
ax.set_yscale('log')

のように可能。軸の目盛りの位置と対応する文字列も必要に応じて指定できる。

ax.set_xticks([0, 0.5, 1, 1.5])
ax.set_yticks([0, 0.5, 1, 1.5])
ax.set_yticklabels(['0.0', '0.5', '1.0', '1.5'])

また、補助線を引きたい時は numpy を使って

x = np.linspace(xmin, xmax, npts)
y = np.linspace(ymin, ymax, npts)

として作ったリストが使える。

等高線

matplotlib で等高線をプロットするには、2次元リストに格納された x,y,z の値が必要。 もし用意されたデータが 1次元で、ただし秩序を保って並んでいるなら、

x,y,z = data[:,1].reshape(ny,nx), data[:,2].reshape(ny,nx), data[:,3].reshape(ny,nx)

などと、ndarray.reshape 関数が使える。 さて、これを用いて等高線プロットは

cs = ax.contour(x, y, z, levels=[z1, z2, z3], colors='b', linestyles='-')
h,_   = cs.legend_elements()
ax.legend([h[0]], ['legend'], fontsize=20)

などとして描ける。レジェンドの他に、等高線の途中に対応する z の値を描くことも出来る。

ax.clabel(cs, fmt='%d')
ax.clabel(cs2, fmt='%.1E')

別の種類のプロットとして、等高線間の塗りつぶしも可能。そのためには

ax.contourf(x, y, z, levels=[zmin,zmax], alpha=0.3, colors='gray')

などとすると、zmin < z < zmax の領域を指定された色で塗りつぶす。 この時、場合によっては背景色を白以外の色にしておくと便利。

ax.set_axis_bgcolor('green')

ヒストグラム

ヒストグラムの描画のためには、1次元のデータリストを用意して、

ax.hist(data, bins=nbin, range=(xmin, xmax), color='m', ec='black', label='legend')

とする。 ec オプションは、python3 で描くとデフォルトの枠線が白色で幸薄い感じになるので必須。 各データを違うウェイトで足し合わせたい時は、データと同じ順序で同じ数のウェイトを格納したリスト weights を用意し、オプションを追加: weights=weights

横軸が log scale のヒストグラムを描きたいときは少々変更が必要で、

ax.hist(prob, bins=np.logspace(-6, 0, 50), color='b', ec='black', log=True)
ax.set_xscale('log')

のような感じ。

図のサイズ、アスペクト比を任意に変更する

以下の修正で十分。

fig = plt.figure(figsize=[4,2.25])

座標軸の描画

以下のサンプルはどこぞからの拾い物。

# removing the default axis on all sides:
for side in ['bottom','right','top','left']:
    ax.spines[side].set_visible(False)

# removing the axis ticks
plt.xticks([]) # labels
plt.yticks([])
ax.xaxis.set_ticks_position('none') # tick markers
ax.yaxis.set_ticks_position('none')

# get width and height of axes object to compute
# matching arrowhead length and width
dps = fig.dpi_scale_trans.inverted()
bbox = ax.get_window_extent().transformed(dps)
width, height = bbox.width, bbox.height

# manual arrowhead width and length
hw = 1./20.*(ymax-ymin)
hl = 1./20.*(xmax-xmin)
lw = 1. # axis line width
ohg = 0.3 # arrow overhang

# compute matching arrowhead length and width
yhw = hw/(ymax-ymin)*(xmax-xmin)* height/width
yhl = hl/(xmax-xmin)*(ymax-ymin)* width/height

# draw x and y axis
ax.arrow(xmin, 0, xmax-xmin, 0., fc='k', ec='k', lw = lw,
         head_width=hw, head_length=hl, overhang = ohg,
         length_includes_head= True, clip_on = False)
ax.arrow(0, ymin, 0., ymax-ymin, fc='k', ec='k', lw = lw,
         head_width=yhw, head_length=yhl, overhang = ohg,
         length_includes_head= True, clip_on = False)

上のコマンドで座標軸を作ったら、そこに ax.plot で関数を描画したり、

phi = patches.Circle(xy=(x, y), radius=r, fc='green')
ax.add_patch(phi)

で円を描いたり出来る。

図への書き込み

プレゼン用に図に星印などつけたい場合は、

ax.plot(x, y, '*', markersize=15, color='black', clip_on=False)

とする。clip_on=False を指定すると、点が図の領域からはみ出た場合も、はみ出た部分を含めて全て描画してくれる。 また、矢印付き、なしの文字の書き込みが

ax.annotate('Text', xy=[x, y], xytext=[xtext, ytext], arrowprops=dict(width=4, color='b'), fontsize=30, color='b')
ax.annotate('Text', xy=(xtext, ytext), fontsize=20)

などで可能。

図を分割

matplotlib.axes.Axes インスタンスを準備する部分を

ax1  = fig.add_subplot(N,x,y)

などと変更する。ここで N は図の総数で、x, yax1 の図の座標に対応する。

Legendの設定色々

以下テンプレート。

ax.legend(loc='upper right', fontsize=15, borderaxespad=0).get_frame().set_alpha(1)

オプションborderaxespadでlegendと図の外周との余白を調整できる。 また、python3系ではデフォルトでlegendが半透明なので、不透明に戻したいときは.get_frame().set_alpha(1)コマンドが役立つ。

漫画風のプロット

plt.xkcd()