Matplotlib の基礎
タイトル・テキスト・凡例
軸と目盛
色・透明度・線種
矢印・画像・アニメーション
データの可視化
図形の描画
Matplotlibの基本的な使い方
Matplotlib は現代的オブジェクト指向言語 Python のために開発されたオープンソースのグラフ描画用パッケージです。科学計算用ライブラリ NumPy および SciPy と併用することで、ユーザーのもつデータを様々な方法で可視化できるように設計されています。Matplotlib は学術・産業分野で定評のある有料ソフトウェア MATLAB とほぼ同等の機能をもち、かつ無料であることから、様々な研究分野で活用され始めています。
Matplotlib の pyplot モジュールが提供するインターフェースは MATLAB をお手本に設計されています。そのため慣れないうちは、その作図コマンドの暗黙的動作に違和感を覚える Pythonユーザーもいるようです。しかし土台はあくまで Python なので、ユーザーは意識的にオブジェクト指向 (OOP) で明示的なコードを記述することもできます。
当サイトでは基本姿勢として、Matplotlib の骨格である Figure と Axes の階層構造を明示した オブジェクト指向 (OOP) のサンプルコードを掲載します。この記述法は MATLABスタイルに比べるとコードが若干長くなりますが、暗黙的動作を排除することで「どの行のコードで何をしているのか」がわかりやすくなり、結果として「どこを変えればグラフを調整できるのか」ということが明確になります。
当サイトでは Jupyter Notebook 環境を前提に Matplotlib の使い方を解説しますが、マジックコマンドで呼び出さない記法を採用しているので、PyCharm など他の統合開発環境であっても使い方はほとんど同じです。
FigureとAxes
Matplotlib を使ってグラフを描くときには、最初に NumPy と Matplotlib のサブパッケージである pyplot を読み込みんでから、Figure と Axes を用意します。
# NumPyをインポート import numpy as np # matplotlibをインポート import matplotlib.pyplot as plt # Axesを配置する領域を作成 fig = plt.figure(facecolor = "lightgray") # FigureにAxesを追加 ax = fig.add_subplot(111)
上のサンプルコードを実行すると、次のようなグラフが表示されます。[注:Jupyter Notebook を開いて一番最初にグラフを描画させようとすると、<Figure size 640×480 with 1 Axes> のような文だけが表示される場合がありますが、その場合には、もう一度実行するとグラフが描かれます]
灰色に塗り潰された領域が Figure, 内側の白い部分が Axes です。
Figure はウィンドウのようなもので、この中にグラフを描画するための Axes を複数配置することができます。サンプルコードでは Figure の領域がわかりやすいように facecolor (塗り潰しの色) に lightgray (薄い灰色) を指定していますが、デフォルトだと真白なので、Jupyter Notebook の背景色と同化してしまって Figure の範囲がよくわかりません。ちなみに Axes とは「座標軸」を意味する Axis の複数形で、Matplotlib では「グラフ描画領域」という意味で使われています。
Figure は matplotlib.figure.Figureクラスのインスタンスで、普通は Figureオブジェクト (あるいは単に Figure) とよばれます。Axes は matplotlib.axes.Axesクラスのインスタンスで、Axesオブジェクトまたは Axes とよばれます。詳細については、Matplotlib の公式ドキュメント を参照してください。
上のコードでは、Axes は figure.add_subplot()メソッドによって追加されています。このことから、Axes のことをサブプロットとよぶ場合もあります。また、ここでは詳しく書きませんが、Axes は figure.add_axes()メソッドによって追加することもできます。どちらのメソッドを使っても、追加されるのは同じ Axesオブジェクトです。
Axesにデータをプロットする
Axes に1次関数(直線)のグラフをプロットしてみましょう。
import numpy as np import matplotlib.pyplot as plt #サブプロットを描画する領域を作成 fig = plt.figure() # figにAxesを1つ追加 ax = fig.add_subplot(111) # -5~5まで0.1刻みの数値の配列を定義 x = np.arange(-5, 5, 0.1) # グラフに描く関数 y = x # axにグラフを描画 ax.plot(x, y)
このコードを実行すると直線のグラフが表示されます。
上のコードでは、np.arange() を使って -5 から 5 まで 0.1 刻みの数値を要素にもつ配列を定義したあと、y = x によって、x に等しい配列をもう1つ作成しています。そして、axes.plot()メソッドを使って Axes にデータをプロットさせています。これは最低限の設定で描かせたグラフなので、グラフタイトルも軸ラベルもありません。そうした細かい部分の調整を各記事で解説していくことになります。
複数のAxesを配置する
先ほどのサンプルコードで、figure.add_axes()メソッドの引数が「111」となっていましたが、これは「 Figure の 1 行 1 列の 1 番目に Axes を追加する」ことを意味しています。今度は 2 行 2 列の 1, 2, 3 番目に Axes を追加するコードを書いてみます。
# NumPyをインポート import numpy as np # matplotlibをインポート import matplotlib.pyplot as plt #Axesを描画する領域(Figure)を作成 fig = plt.figure(facecolor = "lightgray") # Figureに3つのAxesを追加 ax_1 = fig.add_subplot(221) ax_2 = fig.add_subplot(222) ax_3 = fig.add_subplot(223)
コードを実行すると次のようなグラフが表示されます。
figure.add_axes()メソッドの引数 22x を指定したときに追加される Axes の位置対応は次のようになっています。
pyplot.subplots() を使うと、複数の Axes オブジェクトを一度に生成できます。
第 1 引数 nrows と 第 2 引数 ncols には、それぞれ Axes の行方向の数と列方向の数を渡します。sharex と sharey を指定することで、x 軸と y 軸のラベルを共有できます。たとえば、縦に 3 個、横に 2 個の Axes を埋め込んだ Figure は以下のコードで作成できます。
# In[1] import numpy as np import matplotlib.pyplot as plt # 3行2列のAxesを埋め込んだFigureを生成 # 軸ラベルは共有する fig, ax = plt.subplots(3, 2, sharex="col", sharey="row")
このとき、ax には Axes を要素にもつ配列が格納されます:
# In[2] # axを表示 print(ax)
<matplotlib.axes._subplots.AxesSubplot object at 0x05CDF990>]
[<matplotlib.axes._subplots.AxesSubplot object at 0x05CFDED0>
<matplotlib.axes._subplots.AxesSubplot object at 0x05D1B8F0>]
[<matplotlib.axes._subplots.AxesSubplot object at 0x05D39810>
<matplotlib.axes._subplots.AxesSubplot object at 0x05D597F0>]]
3 行 2 列目の Axes にデータをプロットする場合は、次のようなコードで実現できます。
# In[3] # 正弦波のデータ x = np.linspace(1, 10) y = np.sin(x) # 3行2列目にあるAxesにデータをプロット ax[2, 1].plot(x, y) # Figureを再表示 display(fig)
Figureのクリア(初期化)
Jupyter notebook で Figure と Axes を作成したコードセルを抜けたあと、別のコードセルで Axes.plot() などを使用しても何も表示されないため、Figure オブジェクトや Axes オブジェクトが消えてしまったように錯覚しがちです。
# In[1] # 3×3サイズのFigureオブジェクトを作成 fig = plt.figure(figsize=(3, 3)) # FigureにAxesを追加 ax = fig.add_subplot(111) # Axesの設定 ax.grid() # グリッドを表示 ax.set_title("Axes sample", fontsize=14) # タイトルを表示 ax.set_xlim([-5, 5]) # x軸の範囲 ax.set_ylim([-5, 5]) # y軸の範囲
# In[2] # データを作成 x = np.arange(-5, 6) y = x # データをAxesにプロット ax.plot(x, y)
[<matplotlib.lines.Line2D at 0x3e04b10>]
しかし、実際にはどちらも残っていて、display() で再表示できます (もし本当にオブジェクトが消えてしまったのであれば、plot メソッドの使用時にエラーとなるはずです)。
# In[3] # Figureを再表示 display(fig)
Figure のプロットデータや設定をリセットしたいときは Figure.clf() を使います。
clf() メソッドは Figure に展開されている Axes オブジェクトをすべてクリアします (Figure オブジェクトそのものは消えません)。
# In[4] # Figureをリセット fig.clf() # Figureを再表示 display(fig)
<Figure size 216x216 with 0 Axes>
Axesのクリア(初期化)
Axes.cla() は Axes を白紙の状態に戻します。Axes オブジェクト自体は残したまま、データのプロットや、軸や目盛などの各種設定をリセットします。Jupyter notebook でコードセルを抜けたあと、別のコードセルで Axes オブジェクトを使い回したいときなどに cla() メソッドが使えます。
# In[1] fig = plt.figure(figsize=(3,3)) ax = fig.add_subplot(111) # Axesの中心に大きな点をプロット ax.scatter(0.5, 0.5, s=800, c="darkgreen")
# In[2] # Axesをリセット ax.cla() # Axesの中心に大きな三角形をプロット ax.scatter(0.5, 0.5, s=800, c="firebrick", marker="^") # Figureを再表示 display(fig)
FigureとAxes の書式設定
Figureオブジェクトを作るときに、大きさや塗り潰しの色、枠線の有無などを指定できます。また、Figure に Axes を追加したあとで、subplots_adjust() メソッドを使って隣り合う Axes 同士の余白を調整できます。以下のサンプルコードを参考にカスタマイズしてみてください。
import matplotlib.pyplot as plt # Figureオブジェクトの作成と書式設定 fig = plt.figure( # サイズ figsize=(5, 5), # 塗り潰しの色 facecolor="lightgray", # 枠線の表示 frameon=True, # 枠線の色 edgecolor="black", # 枠線の太さ linewidth=4) # FigureにAxes(サブプロット)を追加 ax1 = fig.add_subplot(221) ax2 = fig.add_subplot(222) ax3 = fig.add_subplot(223) ax4 = fig.add_subplot(224) # Axesオブジェクト(サブプロット)間の余白調整 # wspaceは行方向、hspaceは列方向を調整 fig.subplots_adjust(wspace = 0.4, hspace = 0.4)
最後の行で subplots_adjust() メソッドを使って、隣り合う Axes 間の余白を調整していますが、このコードがないと下の図のように目盛などが詰まって見づらくなってしまいます。
Figureオブジェクトに Axesオブジェクト (サブプロット) を追加するときに、塗り潰しの色や軸の範囲などを設定することができます。これらは Axes 作成後にメソッドで設定することもできます。以下のサンプルコードに Axes の書式設定の例を載せておきます。
import matplotlib.pyplot as plt import numpy as np # Figureオブジェクトの書式設定 fig = plt.figure(figsize = (5, 5)) # FigureにAxes(サブプロット)を追加 ax = fig.add_subplot( #行数と列数、Axes番号 111, # 塗り潰しの色 facecolor="lightgreen", # x軸とy軸の範囲 xlim=[-4,4], ylim=[0,40]) # -4~4, 0.2刻みのデータを用意 x = np.arange(-4, 4, 0.2) # yのデータを用意 y = np.exp(x) # Axesに(x,y)データをプロット ax.plot(x, y)
スタイルシート(plt.style.use)
style モジュールからスタイルシートを読み込めば、プロットスタイルを手軽に変更できます。スタイルシートは、\mpl-data\stylelib フォルダの中に、拡張子 .mplstyle のついたファイルとして格納されているはずです。スタイルの細かな調整には若干面倒な作業が必要なので、よほどこだわりがなければ、この方法でスタイルを決定すれば十分だと思います。
最初に NumPy と Matplotlib を読み込んでおきます。
# In[1] import platform import numpy as np import matplotlib import matplotlib.pyplot as plt # 実行環境 print("実行環境") print(f"Python {platform.python_version()}") print(f"NumPy {np.__version__}") print(f"Matplotlib {matplotlib.__version__}")
実行環境 Python 3.6.9 NumPy 1.18.5 Matplotlib 3.2.2
以下のコマンドで、使用可能なスタイルシートの一覧を表示できます。
# In[2] # 使用可能なスタイルシートの名前一覧 plt.style.available
['Solarize_Light2', '_classic_test_patch', 'bmh', 'classic', 'dark_background', ・・・・・・ 'seaborn-white', 'seaborn-whitegrid', 'tableau-colorblind10']
同じグラフに対して色々なスタイルを適用して変化の様子を見るので、あらかじめグラフを描く関数を用意しておきます。
# In[3] # グラフを並べて描く関数 def test_plot(): fig, ax = plt.subplots(1, 2, figsize=(8,3.5)) np.random.seed(1137) x0 = np.random.randn(1000) x1 = np.linspace(-6, 6) ax[0].hist(x0) for k in range(3): ax[1].plot(x1, x1 + 3*k) plt.show()
最初にデフォルトのスタイルでプロットしてみましょう。
# In[4] # デフォルトのスタイルでプロット test_plot()
plt.style.use(“stylename”) でスタイルシートを変更できますが、その後のプロットすべてに影響を与えます。スタイル・コンテキスト・マネージャーを使用すれば、デフォルト設定を変更せずに一回限りでスタイルを適用できます。スタイルシートに “grayscale” を指定して、白黒の図を描いてみましょう。
# In[5] # 白黒の図を描く with plt.style.context("grayscale"): test_plot()
dark_background は背景を黒く塗り潰したデザインです。
# In[6] # 背景を黒く塗り潰す with plt.style.context("dark_background"): test_plot()
bmh は bayesian bethods for hackers (ハッカーのベイジアン・メソッド) の略です。
# In[7] # Bayesian Methods for Hackers style sheet # ベイジアン・メソッド・スタイル with plt.style.context("bmh"): test_plot()
seaborn 風のスタイルも指定できます。たとえば、seaborn-pastel は淡い色調でグラフがデザインされます。
# In[8] # seabornのパステル調スタイル with plt.style.context("seaborn-pastel"): test_plot()
ggplot は R 言語で使用されているヴィジュアルツールです。
# In[9] # R言語のスタイルシート with plt.style.context("ggplot"): test_plot()
MATLAB スタイル
Figure クラスと Axes クラスのインスタンス生成を宣言せずにデータをプロットする、いわゆる MATLAB スタイル も選択できます。
MATLAB スタイルにおいては、あらゆる操作は matplotlib.pyplot モジュールの関数を介して実行されます。たとえば、pyplot.plot() は基本的かつ汎用性の高いプロット機能を提供します。
単体のグラフを作成する場合、ユーザーは Figure や Axes を準備する必要はありません。これらのオブジェクトは自動生成されます。
# In[1] import numpy as np import matplotlib.pyplot as plt # y=logxのデータ x = np.linspace(1, 10) y = np.log(x) # データをプロット plt.plot(x, y, color="red")
pyplot の関数は、常に現在の Figure と Axes に対してはたらきかけます。
pyplot.subplot() は Figure を作成して Axes を追加する関数ですが、実行するたびにアクティブな Axes は移り変わります。たとえば、以下のコードは Figure に 2 つの Axes を加えますが、プロットは 2 番目の Axes に対して実行されます。
# In[2] # 2行1列のうち1つめのAxesを生成 plt.subplot(2, 1, 1) # 2行1列のうち2つめのAxesを生成 plt.subplot(2, 1, 2) plt.plot(x, y, color="red")
MATLAB スタイルのコードはオブジェクト指向に比べて簡潔ですが、ユーザーは常にアクティブな Figure と Axes を意識しなくてはならず、複数の Axes を扱いにくいというデメリットがあります。
Jupyter notebook を使用している場合、コードセルを抜けて別のコードセルで pyplot.plot() などのデータプロット関数を実行すると、以前のオブジェクトは受け継がれずに、全く新しい Figre と Axes が生成されます。
# In[3] plt.plot(x, y, color="dimgrey")
作成した Figure を別のコードセルで再利用したいと考えるならば、plt.gcf() で現在の Figure オブジェクトを取得して保存しておきます。gcf は get current figure の略です。
# In[4] x = np.linspace(0, 10, 65) y = np.sin(x) plt.plot(x, y, color="forestgreen") fig = plt.gcf()
# In[5] # figを再表示 display(fig)
同様に現在の Axes を取得する関数は pyplot.gca() です。
gca は get current axes の略です。
MATLAB スタイルのコードはバックグラウンドで pyplot.gcf() と pyplot.gca() でアクティブな Figure と Axes を取得しながらプロットを処理します。