自分のキャリアをあれこれ考えながら、Pythonで様々なデータを分析していくブログです

Pythonでグラフを描画できる「Matplotlib」への理解を深める

Python
Python

MatplotlibはPythonでグラフを描画したい場合に利用するライブラリです。

例えば下記のようなグラフが数行で描けます。

例: 正弦波 (sine wave)の描画
# 参考: https://matplotlib.org/stable/plot_types/basic/plot.html

import matplotlib.pyplot as plt
import numpy as np

# 0秒 ~ 10秒の間で100個の値を作成する
time = np.linspace(0, 10, 100)

# 振幅
amplitude = np.sin(time)

# 描画
fig, ax = plt.subplots()
ax.plot(time, amplitude, linewidth=2.0)
plt.show()
Out[0]

綺麗なグラフが出力されました。

簡単ですが、コピペばかりで中身をあまり理解せずに使っている方も多いのではないでしょうか? (私はそうです 笑)

流石に画像分析など様々なシーンでMatplotlibを使うことが予想されるので、きちんと理解しておいた方がいいと思い記事にまとめようと思ったのが最初の動機です。

ちなみに、他にもSeabornというライブラリもPythonでの描画によく使われますが、こちらもベースはMatplotlibになっているようです。

Seaborn ... builds on top of matplotlib
引用: https://seaborn.pydata.org/tutorial/introduction#an-introduction-to-seaborn

Matplotlibを理解すればSeabornのこともより深く理解することが可能かも知れません。

スポンサーリンク

Matplotlibでの2つの記述方法

よくPythonでグラフを書くときに本やブログのコードを参照しますが、Matplotlibの記述方法が実は2通りあるようです。

1つ目が「pyplot方式」で、2つ目が「オブジェクト指向方式」です。

先ほどの正弦波のグラフ例はオブジェクト指向方式での描画になります。

pyplot方式だと下記のようになると思います。

例: 正弦波 (sine wave)の描画 pyplot version
# pyplot方式で描く
import matplotlib.pyplot as plt
plt.figure()
plt.plot(time,amplitude,linewidth=2)

つい最近まで2種類の書き方があることを知らなかったので、人によってコードの記載方法が違い混乱することが多々ありました。

「pyplot方式」は暗黙的な記載方法で数値計算ソフトウェアであるMATLABの文法を真似たインターフェースです。

matplotlib.pyplotのメソッド(function)を通して描画できるのでシンプルで分かりやすい方法です。一方で、複数の単位の違うグラフを同じスケールで描画するといったことは苦手で柔軟性に欠ける場合があります。(※ 参考:matplotblog -pyplot-vs-object-oriented-interface-)

「オブジェクト指向方式」は明示的な記載方法です。FigureやAxesのメソッドを操作することにより自由に描画が出来ますが、文法やmatplotlibの概念に慣れるまで扱いずらいかも知れません。

詳細は公式サイトのコードスタイルというセクションやmatplotlib-application-interfaces-apisにまとめられています。

個人的にはMatplotlibを知る上で知っておいた方がいい重要な項目の1つだと思っています。

スポンサーリンク

Matplotlibのコンポーネントの基本構成

Matplotlibを理解するにはまずどんなコンポーネントで描画されているか理解する必要があります。

Artistについて

どうやらグラフ上に見えている全ての要素はArtistと呼ばれるクラスのサブクラスのようです。Artistを組み合わせて、canvas上に描画することで図表やグラフを作成しているようです。

class matplotlib.artist.Artist
Abstract base class for objects that render into a FigureCanvas.
Typically, all visible elements in a figure are subclasses of Artist.
引用: https://matplotlib.org/stable/api/artist_api.html

図: Inheritance Diagramsの一部抜粋 from matplotlib.org/stable/api/artist_api.html

Artistから様々なコンポーネントが継承されていることが分かりますね。

Artistは大きく分類すると「プリミティブ型」と「コンテナ型」という2種類あります。プリミティブ型はLine2D(折れ線)やText(x軸のラベルや図のタイトル)などグラフ上に描画して見せたいオブジェクトのことで、コンテナ型はFigure、Axes、Axisなどプリミティブ型のオブジェクトを配置する場所を指しているみたいです。

There are two types of Artists: primitives and containers. The primitives represent the standard graphical objects we want to paint onto our canvas: Line2D, Rectangle, Text, AxesImage, etc., and the containers are places to put them (Axis, Axes and Figure).
https://matplotlib.org/stable/tutorials/intermediate/artists.html

Backendとは

Matplotlibを使っているユーザーは実に様々な用途でグラフを作成しています。

例えば下記のような目的があるかと思います。

・私みたいにJupyter notebookのアウトプットに表示するため(inline)
・PyQtやPyGObjectといったGUIツールを使ったアプリケーション内に表示するため
・ディープラーニングで作成したモデルの推定結果をjpegやpngで出力し精度を確認するため

個々の利用用途に合わせるため、Matplotlibではbackendという仕組みでFigureの出力方法を切り替えることが出来るようになっています。

Backendは基本的にはMatplotlibによって自動的に選択され、Jupyter notebookでは、inlineというbackendが設定されています。(matplotlib-inlineというライブラリでインストールされています。)

明示的にbackendを設定するにはマジックコマンド(例: %matplotlib inline)やmatplotlib.use('Agg') などで設定する方法がありますので、目的別に変更すると良いと思います。

Without a backend explicitly set, Matplotlib automatically detects a usable backend based on what is available on your system and on whether a GUI event loop is already running. The first usable backend in the following list is selected: MacOSX, QtAgg, GTK4Agg, Gtk3Agg, TkAgg, WxAgg, Agg. The last, Agg, is a non-interactive backend that can only write to files. It is used on Linux, if Matplotlib cannot connect to either an X display or a Wayland display.
引用: https://matplotlib.org/stable/users/explain/backends.html#selecting-a-backend

Jupyter notebookでは、inline backendはグラフを表示するだけですが、notebook backendに変更することによってグラフをインタラクティブに操作するといったことも可能になります。

記事冒頭のコードに%matplotlib notebookを追加
import matplotlib.pyplot as plt
import numpy as np

# backend を notebook に変更
%matplotlib notebook

time = np.linspace(0, 10, 100)
amplitude = np.sin(time)

fig, ax = plt.subplots()
ax.plot(time, amplitude, linewidth=2.0)
plt.show()
Out[0]

ズームや移動が可能になるパネルが追加されています。

上記はスクリーンショットなので操作は出来ませんが実際にjupyter上で実行していただくとグラフが動かせることが分かります。

図を構成しているFigure・Axes・Axisの関係性

Matplotlibの図はFigure・Axes・Axisという3つのメインコンポーネントから構成されています。

それぞれの役割を例えるなら何になるかなと考えていたときに閃いたのが下記になります。

Figureは「額縁(がくぶち)」になります。

額縁の色や太さなどは変更可能で、はみ出さない限り色々な大きさの画用紙を何枚でも配置することが出来ます。

Axesは「画用紙」になります。

画用紙は通常決まった大きさですが、切れば自分でサイズを設定することも出来ます。

最終的にはお絵かきするように、画用紙に図のタイトルなどのテキストや折れ線や棒グラフなどを追加することによってメインビジュアルを構成します。

お絵かきの方法の1つにaxesのヘルパーメソッドを使う方法があります。

具体的にはset_xlabel()やset_title()でラベルやタイトルのテキストArtistを追加し、plot()で2DラインのArtistを追加することが出来ます。

ちなみにAxesは通常2つか3つのAxisオブジェクト(X軸/Y軸/Z軸)を含むようです。

An Axes ... usually includes two (or three in the case of 3D) Axis objects
引用: https://matplotlib.org/stable/tutorials/introductory/quick_start.html#axes

Axisは少々苦しいですが「用紙の方眼や升目」に近いイメージを持ちました。

画用紙に付属しているものという役割で、画用紙に描いた折れ線や棒グラフのサイズを把握することに使えます。(画用紙に方眼はないよねという意見はご勘弁を 笑)

それでは実際にFigureやAxesの動きを見てみたいと思います。

まずはFigureだけだとどうなるのか確認したあと、FigureにAxesを追加するという流れで確認していきます。

Figureのみだとどう表示されるか
import matplotlib.pyplot as plt
fig = plt.figure()
plt.show()
Out[0]
Figure size 640x480 with 0 Axes

Figure sizeとAxesが0個という表示のみでグラフは何も出力されませんでした。

額縁だけだと何も表示されないようです。

ここで、画用紙を追加してみましょう。

AxesをFigureに追加する
import matplotlib.pyplot as plt

# 額縁の色を赤に太さを2.0にする
fig = plt.figure(edgecolor='red',linewidth=2)

# 画用紙を追加する (add_axesで場所と大きさを指定)
# add_axes(rect=(left bottom width height))
fig.add_axes(rect=(0.1,0.1,0.5,0.5))
fig.add_axes(rect=(0.7,0.1,0.5,0.5))

# 描画
plt.show()
Out[0]

画用紙2枚(Axes)を額縁(Figure)の中に入れてみました。

画用紙にはやはり方眼(Axis)も含まれているようです。

今回はFigureにAxesを追加するというイメージで確認していきたかったのでadd_axesメソッドを使いましたが、FigureとAxes両方のオブジェクトが一緒に作れるplt.subplotsメソッドの方がよく見かけます。(書くコードの量が少なくなるので動きが分かっていればsubplotsメソッドの方がいいですね)

Figure・Axes・Axisの設定例

実際にグラフを描いて色々設定してみました。ほとんどaxesの作業が多いですね 笑

下記のページにてグラフを構成する各要素がどのようにコードで記載されているか分かりやすく紹介されています。

かなり参考になりますので、リンク先の図を見ながら理解を深めるのが良さそうです。

Anatomy of a figure — Matplotlib 3.8.2 documentation
リッチなグラフの作成例
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import AutoMinorLocator
from matplotlib import rcParams
rcParams['font.family'] = 'Hiragino Sans' # Macの場合
#rcParams['font.family'] = 'Meiryo' # Windowsの場合
#rcParams['font.family'] = 'VL PGothic' # Linuxの場合

fig = plt.figure() # fig,ax=plt.subplots()でもいい。
fig.add_axes(rect=(0,0,0.77,0.77))
ax = fig.get_axes()[0]

# Axesのヘルパーメソッドで各要素(Artist)をAxesに追加
ax.set_title("タイトル", color="black")
ax.set_ylabel("Yラベル")
ax.set_xlabel("Xラベル")
x = np.linspace(0, 1, 100)
y = x
ax.plot(x,y,label="line1")
ax.legend()
ax.grid("on")
ax.spines['bottom'].set_visible(False)
ax.yaxis.set_minor_locator(AutoMinorLocator(4))
plt.show()
Out[0]

FigureにAxesを追加する方法3選

FigureにAxesを追加する方法が1つだけではなかったのでまとめておこうと思います。

  1. fix,ax = plt.subplot() を使う方法
  2. plt.figure()でFigureクラスのインスタンスを作成し、ax1 = add_subplot(111)でaxesを追加する方法
  3. plt.figure()でFigureクラスのインスタンスを作成し、add_axes()でaxesを追加する方法
1. fig,ax = plt.subplots() を使う方法
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
plt.show()
Out[0]

2. add_subplot()でaxesを追加する方法
import matplotlib.pyplot as plt
fig = plt.figure()
fig.add_subplot()
plt.show()
Out[0]

3. add_axes()でaxesを追加する方法
import matplotlib.pyplot as plt
fig = plt.figure()
fig.add_axes(rect=(0,0,0.77,0.77))
plt.show()
Out[0]

公式サイトではfig, ax = plt.subplots()でfigとaxが作成されていることが多いと思います。

スポンサーリンク

まとめ

今までよく分からず使っていたMatplotlibですが、どう構成されているか理解することによってモヤモヤ感がなくなりました。

MatplotlibはPythonによるグラフ描画の基礎になる知識だと思うので、この記事の内容をベースに自分のスキルを拡張していきたいなと思っています。

タイトルとURLをコピーしました