18. Matplotlib: グラフの描画#

import matplotlib.pyplot as plt
import numpy as np

18.1. プロット#

# 2019年の東京の最高気温 (h) と最低気温 (l) の月ごとの平均
X = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
H = [10.3,11.6,15.4,19.0,25.3,25.8,27.5,32.8,29.4,23.3,17.7,12.6]
L = [1.4,3.3,6.2,9.2,15.3,18.5,21.6,25.2,21.7,16.4,9.3,5.2]

Matplotlibを利用する上で基本となるオブジェクトは以下の3つ。

  • plt: matplotlib.pyplotモジュールの別名(慣習でpltがよく用いられる)

  • fig: グラフ全体を管理するFigureオブジェクト

  • ax: ひとつのプロットエリアを管理するAxesオブジェクト

axオブジェクトのplot関数に横軸と縦軸の値を渡すとプロットされる。デフォルトでは、各点が実線で結ばれる。

fig, ax = plt.subplots()
ax.plot(X, H)
plt.show()
_images/ed42f60d7aaf4da32114931102d41f8fb3ae89c882746d54a040710d065b3b3e.png

plot関数の第3引数(fmtキーワード)に'.'を指定すると、データが点としてプロットされる(マーカーとして「点」を指定したことに相当する)。

fig, ax = plt.subplots()
ax.plot(X, H, '.')
plt.show()
_images/a3001487c6937a5bd4d7b193cf5a269ecb1a78793febd8849cd694c3a7249bea.png

plot関数の第3引数(fmtキーワード)に'o-'を指定すると、データが大きめの丸としてプロットされ、その間が線で結ばれる(マーカーとして「丸」、線として「実線」を指定したことに相当する)。

fig, ax = plt.subplots()
ax.plot(X, H, 'o-')
plt.show()
_images/8c99a1f75d7f954ca9072ae15f408a3bbfae17066b8f842f71d7d95fcbdeeb7d.png

点を四角(s)、線を破線(--)、色を赤(r)でプロットする例。plot関数の第3引数(fmtキーワード)はマーカー、線、色の順番で指定する。。

fig, ax = plt.subplots()
ax.plot(X, H, 's--r')
plt.show()
_images/a4c8de4279397a01050815d1ee00ec75f3305a7f837c0ffb1d3965cb7f9ddbda.png

最高気温(H)と最低気温(L)をひとつの領域にプロットする例。

fig, ax = plt.subplots()
ax.plot(X, H, 'o-r')
ax.plot(X, L, 'x-b')
plt.show()
_images/db0e8dc53716848d2ac34c95310f84d94b795e4d0f82b8d2a3303ddf9526a339.png

fmt引数の代わりに、マーカーをmarker引数、線スタイルをls引数、色をcolor引数で指定する例。指定できるマーカー線スタイルはドキュメントを参照のこと。lsキーワード引数はlinestyleキーワード引数の省略形である。

fig, ax = plt.subplots()
ax.plot(X, H, marker='o', ls='-', color='r')
ax.plot(X, L, marker='x', ls='-', color='b')
plt.show()
_images/db0e8dc53716848d2ac34c95310f84d94b795e4d0f82b8d2a3303ddf9526a339.png

subplots関数の引数でグラフを2つのプロットエリアに縦に分割し、最高気温と最低気温をプロットする例。

fig, (ax1, ax2) = plt.subplots(2, 1)
ax1.plot(X, H, 'o-r')
ax2.plot(X, L, 'x-b')
plt.show()
_images/8fbb083138575065294c76fe54b9ba9e35cfa488cc68d01f01c2aa9c6e6c5cd5.png

グラフを2つのプロットエリアに縦に分割したとき、横軸を共有する例。

fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True)
ax1.plot(X, H, 'o-r')
ax2.plot(X, L, 'x-b')
plt.show()
_images/ce44ea51047dcf3d49ebbee140172560b87f06febe5ddd7036d22e81696b27ac.png

グラフを2つのプロットエリアに横に分割し、最高気温と最低気温をプロットする例。

fig, (ax1, ax2) = plt.subplots(1, 2)
ax1.plot(X, H, 'o-r')
ax2.plot(X, L, 'x-b')
plt.show()
_images/feedbcdf4d51744aed50156187f21048ff12db8cf0f578e34d727c85281d8c67.png

グラフを2つのプロットエリアに横に分割したとき、縦軸を共有する例。

fig, (ax1, ax2) = plt.subplots(1, 2, sharey=True)
ax1.plot(X, H, 's-r')
ax2.plot(X, L, 'o--b')
plt.show()
_images/0586da252583a6dae368938824cba979737d277cb6c45b368aaaae8d6331278c.png

plot関数で直線 \(y = 0.5x + 2\) を描画する例(2つの点 \((0, 2), (10, 7)\) を直線で結ぶ)。

fig, ax = plt.subplots()
x = np.array([0, 10])
ax.plot(x, 0.5 * x + 2, '-')
ax.set_xlim(x)
ax.grid()
plt.show()
_images/35927abcffe8a73d4a38703729240fcd53e11ac50db139ece250bb01605448e0.png

plot関数で正弦関数(\(\sin\))の軌跡をプロットする例。

t = np.arange(0, 360, 1)
s = np.sin(2 * np.pi * (t / 360))

fig, ax = plt.subplots()
ax.plot(t, s)
ax.grid()
ax.xaxis.set_ticks(np.arange(0, 400, 45))
plt.show()
_images/21bd6b01b30f670cc63cb6b918920b6191a69e009bd402a5397fff1931242e49.png

グラフをファイルに保存する。

fig.savefig('graph.png')

18.2. 軸やラベルの設定・変更#

何も設定していないグラフ。

fig, ax = plt.subplots()
ax.plot(X, H, 'o-r')
ax.plot(X, L, 'x-b')
plt.show()
_images/db0e8dc53716848d2ac34c95310f84d94b795e4d0f82b8d2a3303ddf9526a339.png

set_xlim関数とset_ylim関数で、横軸と縦軸の範囲を指定。

fig, ax = plt.subplots()
ax.plot(X, H, 'o-r')
ax.plot(X, L, 'x-b')
ax.set_xlim(1, 12)
ax.set_ylim(0, 40)
plt.show()
_images/c3f7c3c2811bd450634741dda8a2b76b3f6479726bf0959477ec58eb71165b08.png

plot関数のlabel引数と、legend関数で凡例を表示(凡例の表示位置は、legend関数のloc引数で制御できる)。

fig, ax = plt.subplots()
ax.plot(X, H, 'ro-', label="Highest")
ax.plot(X, L, 'bx-', label="Lowest")
ax.legend(loc='upper left')
plt.show()
_images/89b2a5711d36a3a1c8dfd790e1a5516f62b9813daf75a81a6a904772094f492f.png

set_title関数、set_xlabel関数、set_ylabel関数を用いて、グラフや軸のタイトルを表示。

fig, ax = plt.subplots()
ax.plot(X, H, 'o-r')
ax.plot(X, L, 'x-b')
ax.set_title('Average temprature')
ax.set_xlabel('Month')
ax.set_ylabel('Temprature (C)')
plt.show()
_images/3953fe93c84610e4dd38bff3c031f930c85dc1e9c6d2468fd32bd9ef1ec83a91.png

grid関数でグリッドを表示。

fig, ax = plt.subplots()
ax.plot(X, H, 'o-r')
ax.plot(X, L, 'x-b')
ax.grid()
plt.show()
_images/37a90396123c669dce6bafc450a6d817121c30ca508d418d441f25d71b57e0ae.png

全てを指定してグラフを完成させたバージョン(xaxis.set_ticks関数で横軸のグリッドの幅を設定)。

fig, ax = plt.subplots()
ax.plot(X, H, 'ro-', label="Highest")
ax.plot(X, L, 'bx-', label="Lowest")
ax.legend(loc='upper left')
ax.set_xlim(1, 12)
ax.set_ylim(0, 40)
ax.set_title('Average temprature')
ax.set_xlabel('Month')
ax.set_ylabel('Temprature (C)')
ax.xaxis.set_ticks(x)
ax.grid()
plt.show()
_images/60c8fb4d524b63298ba2958da15a381b25ade071207b6afa537194df58d2ae12.png

18.3. グラフの装飾#

グラフのサイズ(figsize)の変更(サイズ指定の単位はインチ)。

fig, ax = plt.subplots(figsize=(8, 6))
ax.plot(X, H, 'o-r')
ax.plot(X, L, 'x-b')
ax.grid()
plt.show()
_images/5ad0d359e4ac054bcfb38612130047d0ce6ae8425e7bfcb7938e2611d901ba44.png

グラフの解像度(dpi)を変更してグラフを拡大。

fig, ax = plt.subplots(dpi=120)
ax.plot(X, H, 'o-r')
ax.plot(X, L, 'x-b')
ax.grid()
plt.show()
_images/5db9c25b94eb40923457fa2a17a1dc28316d0f71d230fb166cfe64c45149457a.png

matplotlibでデフォルトで用いられるパレットの色(tab color)を指定する例。

fig, ax = plt.subplots()
ax.plot(X, H, 'o-', color='tab:red')
ax.plot(X, L, 'x-', color='tab:blue')
ax.grid()
plt.show()
_images/df7f1aab1919cc11bd2b2c7f8de995c230ebe0fb0e0f0b44f78005427395d285.png

線の太さ(lw)を指定する例。なお、lwキーワード引数はlinewidthキーワード引数の省略形である。

fig, ax = plt.subplots()
ax.plot(X, H, 'o-', lw=2, color='tab:red')
ax.plot(X, L, 'x-', lw=0.5, color='tab:blue')
ax.grid()
plt.show()
_images/e4748796c66ff7b544d2b5b0cad7b03f23697b48aa913b5435d98f939f503c1a.png

axhline関数で水平な補助線を描画する例。

fig, ax = plt.subplots()
ax.plot(X, H, 'o-', color='tab:red')
ax.plot(X, L, 'x-', color='tab:blue')
ax.axhline(30, color='tab:brown', ls='--')
ax.axhline(10, color='tab:green', ls='--')
ax.grid()
plt.show()
_images/8a377bd55ba7927c942ec3fa7cd66fafca2f89f93e80e05b8e38f42a2778e61c.png

axvline関数で垂直な補助線を描画する例

fig, ax = plt.subplots()
ax.plot(X, H, 'o-', color='tab:red')
ax.plot(X, L, 'x-', color='tab:blue')
ax.axvline(4, color='tab:orange', ls=':')
ax.axvline(10, color='tab:orange', ls=':')
ax.grid()
plt.show()
_images/e970ccb6557c2a998ec9d6394146a1fc57e6b33052aa6d9ab438f1e203be6da0.png

fill_between関数で2つの系列データの間を塗りつぶす例。

fig, ax = plt.subplots()
ax.fill_between(X, L, H)
ax.grid()
plt.show()
_images/114faadf976fa1fc76ec7377715cf1328858d6010043b271d02a392ea0d89578.png

text関数で座標を指定してテキストを書き込む例(ha='center'は指定された横軸の位置が中心になるように調整する)。

fig, ax = plt.subplots()
ax.plot(X, H, 'o-', color='tab:red')
ax.plot(X, L, 'x-', color='tab:blue')
ax.text(5, 30, 'Highest temprature', ha='center', color='tab:red')
ax.text(10, 5, 'Lowest temprature', ha='center', color='tab:blue')
ax.grid()
plt.show()
_images/66b5afd222a0750efeaed8dd210417e600f3d72e71addf88d91cb77dc00d8f1e.png

annotate関数でプロットされる値を書き込む例。

fig, ax = plt.subplots()
ax.plot(X, H, 'o-', color='tab:red')
ax.plot(X, L, 'x-', color='tab:blue')
for x, hi, lo in zip(X, H, L):
    hs = f'{hi:.1f}'
    ls = f'{lo:.1f}'
    ax.annotate(hs, (x, hi), textcoords='offset points', xytext=(0,10), ha='center', va='bottom')
    ax.annotate(hs, (x, lo), textcoords='offset points', xytext=(0,-10), ha='center', va='top')
ax.set_ylim(-5, 40)
ax.grid()
plt.show()
_images/57ff5f4ee042805f00f4d768b12d27c460bc434bdbf88446ee3cbaf73f0d4df7.png

annotate関数で指定された位置に矢印を描画し、その起点にテキストを書き込む例。

fig, ax = plt.subplots()
ax.plot(X, H, 'o-', color='tab:red')
ax.plot(X, L, 'x-', color='tab:blue')
ax.annotate(
    'Highest', xy=(8, H[7]), textcoords='offset points', xytext=(-40, 20),
    ha='right', va='bottom', arrowprops=dict(arrowstyle='->'))
ax.set_ylim(0, 40)
ax.grid()
plt.show()
_images/dc79855650c43343a2695c2d28836bf729998334201974dc5ba45eededf30833.png

18.4. 棒グラフ#

bar関数で棒グラフを描画。

fig, ax = plt.subplots()
ax.bar(X, H)
ax.set_title('Average temprature')
ax.set_xlabel('Month')
ax.set_ylabel('Temprature (C)')
plt.show()
_images/ad6516030983f0f2d3b32b74141f18ab0be36780128d92892191b76bda3736cf.png

横軸の値のラベルを指定し、月名を表示する。

months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
fig, ax = plt.subplots()
ax.bar(X, H, tick_label=months)
ax.set_title('Average temprature')
ax.set_xlabel('Month')
ax.set_ylabel('Temprature (C)')
plt.show()
_images/bd215cf208e47b52ddf017b6521addde75751db742a1e23396df34196b16e16e.png

bar関数を繰り返し用い、積み上げ棒グラフを描画。

fig, ax = plt.subplots()
ax.bar(X, H, color="r", label="Highest")
ax.bar(X, L, color="b", label="Lowest", tick_label=months)
ax.legend()
ax.set_title('Average temprature')
ax.set_xlabel('Month')
ax.set_ylabel('Temprature (C)')
plt.show()
_images/c2828f93a58fbfd1be34852e3f4427cd7b6a222df2cf6917611b2c8415a34fdc.png

barh関数を用いて、水平方向の棒グラフを描画。以下のコードでinvert_yaxis関数を呼び出さないと、月が下から上の方向に並ぶ。

fig, ax = plt.subplots()
ax.barh(X, H, tick_label=months)
ax.invert_yaxis() # Arange labels top to bottom.
ax.set_title('Average temprature')
ax.set_xlabel('Temprature (C)')
ax.set_ylabel('Month')
plt.show()
_images/46ca96ac53232faa370a6dac7c88089b5754aaa610159525c84b00acdf1747f2.png

barh関数を繰り返し用い、積み上げ棒グラフを描画。

fig, ax = plt.subplots()
ax.barh(X, H, color="r", label="Highest")
ax.barh(X, L, color="b", label="Lowest", tick_label=months)
ax.legend()
ax.invert_yaxis()
ax.set_title('Average temprature')
ax.set_xlabel('Temprature (C)')
ax.set_ylabel('Month')
plt.show()
_images/936792a25bd944e65115a253bac2d3e47dd5c61e95cbb2983e08503b9184c912.png

18.5. 散布図#

2次元平面上で、\(0 \leq x < 1 \wedge 0 \leq y < 1\)の範囲内でランダムな点を2000個作成し、\(d\)に格納する。

D = np.random.rand(2, 2000)

scatter関数を呼び出し、2次元平面上で\(d\)を散布図として描く(今回はset_aspect関数で縦横比を1:1にしておく)。

fig, ax = plt.subplots()
ax.scatter(D[0], D[1], marker='.')
ax.set_aspect('equal')
plt.show()
_images/5cec11f52fbab87e75266e2234faf02ed17e78d7fd47b290dd453a0f9b50a776.png

patches.Wedgeクラスを用いて円弧(中心座標は\((0,0)\)、半径は\(1\)、角度の範囲は0度から90度、alpha引数で透明度を指定)を描き、グラフの上に重ねる。

import matplotlib.patches

fig, ax = plt.subplots()
ax.scatter(D[0], D[1], marker='.')
ax.set_aspect('equal')
circle = matplotlib.patches.Wedge((0, 0), 1, 0, 90, alpha=0.2, color='r')
ax.add_patch(circle)
plt.show()
_images/640834237822543f6f42a70bc2c66489b9eb7f35a95dcbcee63992da43b55dda.png

18.6. 2次元メッシュ・等高線#

\(z = 0.5 (x - 1)^2 + y^2 - 20\)に対して、pcolormesh関数で2次元メッシュを描画する例。

xmin, xmax = -10, 10
ymin, ymax = -10, 10
N = 1000
XX, YY = np.meshgrid(np.linspace(xmin, xmax, N), np.linspace(ymin, ymax, N))
ZZ = 0.5 * (XX - 1) ** 2 + YY ** 2 - 5

fig, ax = plt.subplots(dpi=100)
ax.set_aspect('equal')
ax.set_xlabel('$x$')
ax.set_ylabel('$y$')
mesh = ax.pcolormesh(XX, YY, ZZ, shading='auto')
cbar = fig.colorbar(mesh)
cbar.set_label('$z$')
plt.show()
_images/b4c24059e9dbe5693f47120a5325bf5ff29fbaeb20a44c1d34ae30598ac8709b.png

\(z = 0.5 (x - 1)^2 + y^2 - 20\)に対して、contour関数で等高線を描画する例。

xmin, xmax = -10, 10
ymin, ymax = -10, 10
N = 1000
XX, YY = np.meshgrid(np.linspace(xmin, xmax, N), np.linspace(ymin, ymax, N))
ZZ = 0.5 * (XX - 1) ** 2 + YY ** 2 - 5

fig, ax = plt.subplots(dpi=100)
ax.set_aspect('equal')
ax.set_xlabel('$x$')
ax.set_ylabel('$y$')
cont = ax.contour(XX, YY, ZZ)
cont.clabel(fmt='%1.1f')
plt.show()
_images/36aefa636d5498d66f5cfa071c54a8c65e85e783b114109114bca8ee6599f35c.png

\(z = 0.5 (x - 1)^2 + y^2 - 20\)に対して、2次元メッシュと等高線を描画する例。

xmin, xmax = -10, 10
ymin, ymax = -10, 10
N = 1000
XX, YY = np.meshgrid(np.linspace(xmin, xmax, N), np.linspace(ymin, ymax, N))
ZZ = 0.5 * (XX - 1) ** 2 + YY ** 2 - 5

fig, ax = plt.subplots(dpi=100)
ax.set_aspect('equal')
ax.set_xlabel('$x$')
ax.set_ylabel('$y$')
mesh = ax.pcolormesh(XX, YY, ZZ, shading='auto')
cbar = fig.colorbar(mesh)
cbar.set_label('$z$')
cont = ax.contour(XX, YY, ZZ, colors='white', linestyles='dashed')
cont.clabel(fmt='%1.1f')
plt.show()
_images/1eeddbdb949c1e57028a6ee95fdb3c7cde400f945346478d3c2fb07fc58eda17.png

18.7. アニメーション#

import matplotlib.animation
from IPython.display import HTML

ArtistAnimation関数でアニメーションを作成する例。各コマで描画すべきArtistオブジェクトの集合を要素としたリストを作成し、ArtistAnimation関数に渡す。ArtistオブジェクトについてはArtist tutorialを参照のこと。

D = np.random.rand(2, 100)
C = np.where(D[0] ** 2 + D[1] ** 2 <= 1, 'r', 'b')

fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.set_xlim(0, 1)
ax.set_ylim(0, 1)
circle = matplotlib.patches.Wedge((0, 0), 1, 0, 90, alpha=0.2, color='r')
ax.add_patch(circle)

artists = []
for t in range(D.shape[1]):
    A = []
    A.append(ax.scatter(D[0,:t], D[1,:t], color=C[:t], marker='.'))
    artists.append(A)

ani = matplotlib.animation.ArtistAnimation(fig, artists, interval=10)
html = ani.to_jshtml()
plt.close(fig)
HTML(html)

FuncAnimation関数でアニメーションを作成する例。プロットエリアの初期化と更新を行う関数を実装し、FuncAnimation関数の引数として渡す。

D = np.random.rand(100, 2)

fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.set_xlim(0, 1)
ax.set_ylim(0, 1)

def init():
    circle = matplotlib.patches.Wedge((0, 0), 1, 0, 90, alpha=0.2, color='r')
    return [ax.add_patch(circle),]

def update(d):
    x, y = d
    c = 'r' if x ** 2 + y ** 2 < 1 else 'b'
    return [ax.scatter(x, y, marker='.', color=c),]

ani = matplotlib.animation.FuncAnimation(fig, update, D, init, interval=10, blit=True)
plt.close(fig)
HTML(ani.to_jshtml())

18.8. 日本語の表示方法#

月の名前を日本語表記に変更する。

months = [f'{x}月' for x in X]
months
['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']

また、グラフや軸の名前に日本語を使うと、以下のような文字化けが発生する。

fig, ax = plt.subplots()
ax.bar(X, H, tick_label=months)
ax.set_title('平均気温')
ax.set_xlabel('月')
ax.set_ylabel('温度 (C)')
plt.show()
/usr/lib/python3/dist-packages/IPython/core/pylabtools.py:151: UserWarning: Glyph 24179 (\N{CJK UNIFIED IDEOGRAPH-5E73}) missing from current font.
  fig.canvas.print_figure(bytes_io, **kw)
/usr/lib/python3/dist-packages/IPython/core/pylabtools.py:151: UserWarning: Glyph 22343 (\N{CJK UNIFIED IDEOGRAPH-5747}) missing from current font.
  fig.canvas.print_figure(bytes_io, **kw)
/usr/lib/python3/dist-packages/IPython/core/pylabtools.py:151: UserWarning: Glyph 27671 (\N{CJK UNIFIED IDEOGRAPH-6C17}) missing from current font.
  fig.canvas.print_figure(bytes_io, **kw)
/usr/lib/python3/dist-packages/IPython/core/pylabtools.py:151: UserWarning: Glyph 28201 (\N{CJK UNIFIED IDEOGRAPH-6E29}) missing from current font.
  fig.canvas.print_figure(bytes_io, **kw)
/usr/lib/python3/dist-packages/IPython/core/pylabtools.py:151: UserWarning: Glyph 26376 (\N{CJK UNIFIED IDEOGRAPH-6708}) missing from current font.
  fig.canvas.print_figure(bytes_io, **kw)
/usr/lib/python3/dist-packages/IPython/core/pylabtools.py:151: UserWarning: Glyph 24230 (\N{CJK UNIFIED IDEOGRAPH-5EA6}) missing from current font.
  fig.canvas.print_figure(bytes_io, **kw)
_images/741ad903384049de4ead9b2f9454bcbbc5d151304765d73f31e8f2d424a7f60e.png

この問題に対処するには、japanize-matplotlibモジュールをインストールし、japanize_matplotlibをインポートすればよい(モジュールをインポートするときに"-"が"_"に置き換わることに注意)

!pip install japanize-matplotlib
Defaulting to user installation because normal site-packages is not writeable
Requirement already satisfied: japanize-matplotlib in /home/okazaki/.local/lib/python3.10/site-packages (1.1.3)
Requirement already satisfied: matplotlib in /usr/lib/python3/dist-packages (from japanize-matplotlib) (3.5.1)
import japanize_matplotlib 
fig, ax = plt.subplots()
ax.bar(X, H, tick_label=months)
ax.set_title('平均気温')
ax.set_xlabel('月')
ax.set_ylabel('温度 (C)')
plt.show()
_images/901220a6997bea3463b2ffc1633ec686fb34b354f07f4f57832707084a883bf5.png