[Matplotlib超入門:OOインタフェース編]グラフを実際に描画しながら2つのインタフェースの違いを見てみようPythonデータ処理入門

pyplotインタフェースとOOインタフェースの違いがどこにあるのかを、幾つかのグラフを描画しながら実際に見てみましょう。また、グラフの装飾についても説明します。

» 2025年11月14日 05時00分 公開
[かわさきしんじDeep Insider編集部]
「Pythonデータ処理入門」のインデックス

連載目次

本シリーズと本連載について

 本シリーズ「Pythonデータ処理入門」は、Pythonの基礎をマスターした人を対象に以下のような、Pythonを使ってデータを処理しようというときに便利に使えるツールやライブラリ、フレームワークの使い方の基礎を説明するものです。

 【Matplotlib超入門:OOインタフェース編】は【Matplotlib超入門:pyplot編】に続きオブジェクト指向インタフェースを用いて、グラフを描画する方法を紹介する連載です。pyplot編と同様、以下のバージョンのPythonとMatplotlibを使用しています。

  • Python 3.13
  • Matplotlib 3.10.3

 なお、以下ではMatplotlibのpyplotインタフェースやNumPy、pandasをインポートする以下の行を実行してあるものとします。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

お約束のコード

 前回はMatplotlibのオブジェクト指向インタフェース(以下、OOインタフェース)を紹介しました。特に、その基盤となるFigure、Axes、Artistという3つのオブジェクトを紹介し、OOインタフェースを用いたグラフの簡単な描画を見ました。さらに、幾つかの関数とメソッドについても紹介しました。今回は同じグラフをpyplotインタフェースとOOインタフェースで書いてみながら、両者の違いがどこにあるのかを確認してみます。

グラフを1つ描いてみる

 まずは簡単な折れ線グラフを1つだけ描いてみることにしましょう。pyplotインタフェースで折れ線グラフを描く例を以下に示します。

x = np.arange(5)
y = x ** 2

plt.plot(x, y, color='red')
plt.title('y = x ^ 2')
plt.xlabel('x axis')
plt.ylabel('y axis')
plt.show()

pyplotインタフェースを使ってy = x2のグラフを描く

 これはpyplotインタフェースを使ってy = x2のグラフを描くコードです。実行結果は次のようになります。

pyplotインタフェースを使って描いたy = x ** 2のグラフ pyplotインタフェースを使って描いたy = x2のグラフ

 同じことを、OOインタフェースを使うと次のようになります(このコードを実行した結果は上と変わらないので画像は省略します)。

x = np.arange(5)
y = x ** 2

fig, ax = plt.subplots()

ax.plot(x, y, color='red')
ax.set_title('y = x ^ 2')
ax.set_xlabel('x axis')
ax.set_ylabel('y axis')
plt.show()

OOインタフェースを使ってy = x2のグラフを描く

 その違いは主に以下の点にあります。

  • pyplotインタフェースはそのままplotなどの関数を呼び出しているが、OOインタフェースはpyplot.subplots関数でFigureオブジェクトとAxesオブジェクトを作成してから、Axesオブジェクトが持つメソッドを呼び出している

 これはpyplotインタフェースではどのAxesオブジェクトにグラフを描くか(現在、アクティブなAxesオブジェクトがどれか)はpyplotインタフェースの内部で管理されているのに対して、OOインタフェースではコードを書く側がどのAxesオブジェクトを描画対象とするのかを明示的に指定する必要があることから生まれる差です。

 グラフを1つ描くだけであれば、見た目の差はあまりありません。pyplotインタフェースではplot関数を呼ぶだけでグラフが描けます。しかし、OOインタフェースではpyplot.subplots関数などを使ってAxesオブジェクトを取得しないとグラフを描画できません。そのため、OOインタフェースの方が手間がかかるといえば手間がかかります。すなわち、簡単なグラフの描画ならpyplotインタフェースを使うのがよいということでもあります。

グラフを2つ描いてみる

 では、次にグラフを2つ描いてみます。先ほどと同じくまずはpyplotインタフェースの例から見てください。

x = np.arange(5)
y = x ** 2

plt.figure(figsize=(10, 4))

plt.subplot(1, 2, 1)
plt.plot(x, y, color='red')
plt.title('plot')
plt.xlabel('x axis')
plt.ylabel('y axis')

plt.subplot(1, 2, 2)
plt.bar(x, y, color='blue')
plt.title('bar')
plt.xlabel('x axis')
plt.ylabel('y axis')

plt.show()

pyplotインタフェースで2つのグラフを描画する

 ここでは、最初にpyplot.figure関数でグラフのサイズを指定しています。その後、pyplot.subplot関数で現在のFigureオブジェクトに2つのAxesオブジェクトを追加して、1つ目のAxesオブジェクトに折れ線グラフを描いています(plt.plot関数呼び出し)。その後、2つ目のAxesオブジェクトに棒グラフを描いています(plt.bar関数呼び出し)。

 実行結果は次の通りです。

pyplotインタフェースで描いた2つのグラフ pyplotインタフェースで描いた2つのグラフ

 なお、上のコードはグラフを描くコードをダラダラと記述していますが、次のようにfor文にまとめることも可能です。

x = np.arange(5)
y = x ** 2

plt.figure(figsize=(10, 4))

plot_funcs = [plt.plot, plt.bar]
colors = ['red', 'blue']
titles = ['plot', 'bar']

for idx, (func, color, title) in enumerate(zip(plot_funcs, colors, titles), 1):
    plt.subplot(1, 2, idx)
    func(x, y, color=color)
    plt.title(title)
    plt.xlabel('x axis')
    plt.ylabel('y axis')

plt.show()

グラフを描くコードをfor文にまとめた

 ここでは個々のグラフで変化する要素(グラフ描画に使う関数、描画色、グラフのタイトル)をそれぞれリストに格納しているのがポイントです。そして、zip関数でそれらの組(関数、描画色、タイトル)を反復するとともに、enumerate関数で描画対象のAxesオブジェクトを指定するためのインデックスをループ変数idxに代入しています。

 グラフが2つだけなら1つ目のようなコードでも構わないでしょうが、多数のグラフをまとめて描きたいのであれば、このような書き方が(pyplotインタフェースでも)できることは覚えておくとよいでしょう。

 一方、OOインタフェースを使うとどうなるでしょう。最初にpyplotインタフェースと同様にグラフを描くコードをダラダラと描いてみます(画像は省略)。

x = np.arange(5)
y = x ** 2

fig, ax = plt.subplots(1, 2, figsize=(10, 4))

ax[0].plot(x, y, color='red')
ax[0].set_title('plot')
ax[0].set_xlabel('x axis')
ax[0].set_ylabel('y axis')

ax[1].bar(x, y, color='blue')
ax[1].set_title('bar')
ax[1].set_xlabel('x axis')
ax[1].set_ylabel('y axis')

plt.show()

「ax[0]」「ax[1]」のようにAxesオブジェクトを明示的に指定してグラフを描画

 pyplotインタフェースとの違いは、OOインタフェースではやはり「ax[0]」「ax[1]」のようにAxesオブジェクトを明示的に指定して、グラフを描いているところです。では、for文にまとめたバージョンはどうなるでしょうか。

x = np.arange(5)
y = x ** 2

fig, ax = plt.subplots(1, 2, figsize=(10, 4))

plot_funcs = [ax[0].plot, ax[1].bar]
colors = ['red', 'blue']
titles = ['plot', 'bar']

for i, (func, color, title) in enumerate(zip(plot_funcs, colors, titles)):
    func(x, y, color=color)
    ax[i].set_title(title)
    ax[i].set_xlabel('x axis')
    ax[i].set_ylabel('y axis')

plt.show()

for文を使ったバージョンにOOインタフェース版のコードを変更したもの

 plot_funcsリストに「ax[0].plot」「ax[1].bar」とAxesオブジェクトのメソッドを格納しているところと、「ax[i]」とインデックスを使ってタイトルやラベルの設定を行うAxesオブジェクトを指定しているところを除けば、pyplotインタフェースのコードとよく似ています。

 これでもよいのですが、enumerate関数を使って、「ax[i]」のような形でAxesオブジェクトを指定したり、plot_funcsリストに「ax[0].plot」のようにインデックス付きでAxesオブジェクトのメソッドを格納したりしている点があまりキレイではないと感じる人もいるでしょう。

 もっとキレイな書き方もできるので、紹介だけしておきましょう。

from matplotlib.axes import Axes

x = np.arange(5)
y = x ** 2

fig, ax = plt.subplots(1, 2, figsize=(10, 4))

plot_funcs = [Axes.plot, Axes.bar]
colors = ['red', 'blue']
titles = ['plot', 'bar']

for a, func, color, title in zip(ax, plot_funcs, colors, titles):
    func(a, x, y, color=color)
    a.set_title(title)
    a.set_xlabel('x axis')
    a.set_ylabel('y axis')

plt.show()

Axesオブジェクトのメソッドをリストに含めて反復することで、描画するグラフを切り替える

 ここでしているのは次の3点です。

  • plot_funcsにAxesクラスのメソッドを格納する。ただし、Axes.plot(x, y, ……)のようには呼び出せないことには注意(通常はax[0].plot(x, y, ……)のように呼び出す必要がある。ただし、以下で述べる形式で同じことが可能)
  • pyplot.subplots関数で作成したAxesオブジェクト(の配列)をzip関数に渡すことでfor文のループ変数aにそれが反復されて代入されるようにした
  • for文の中では「ax[i]」のような使い方をしないで、反復時にループ変数aに渡されたAxesオブジェクトを使って「Axes.plot(a, x, y, ……)」のように呼び出すようにした(これは実質的には「a.plot(x, y, ……)」と同じことをする)

 ちょっとトリッキーなやり方ですが、こうすることでインデックスを使う必要がなくなり、コードがさらにキレイになりました(と筆者は感じています)。なお、pyplotインタフェースを使って同様なことを書くと次のようになります。

x = np.arange(5)
y = x ** 2

plt.figure(figsize=(10, 4))

plot_funcs = [plt.plot, plt.bar]
colors = ['red', 'blue']
titles = ['plot', 'bar']

for i, (func, color, title) in enumerate(zip(plot_funcs, colors, titles), 1):
    plt.subplot(1, 2, i)
    func(x, y, color=color)
    plt.title(title)
    plt.xlabel('x axis')
    plt.ylabel('y axis')

plt.show()

同じことをpyplotインタフェースで行う

 詳しい説明は省略しますが、構造はOOインタフェースとほぼ同様です。Axesオブジェクトがない代わりに、enumerate関数を使っていることと、plot_funcsに格納しているのがpyplotモジュールの関数になっているところが異なっている点といえます。

 なお、OOインタフェースを使う場合、実際には、上で紹介した「Axes.plot(a, x, y, ……)」のような書き方をせずに、辞書を使うなどして描くグラフを切り替えるのがよいでしょう。

x = np.arange(5)
y = x ** 2

fig, ax = plt.subplots(1, 2, figsize=(10, 4))

plot_funcs = {
    'plot': lambda a, x, y, **kw: a.plot(x, y, **kw),
    'bar': lambda a, x, y, **kw: a.bar(x, y, **kw)
}
plot_types = ['plot', 'bar']
colors = ['red', 'blue']

for a, plot_type, color in zip(ax, plot_types, colors):
    plot_funcs[plot_type](a, x, y, color=color)
    a.set_title(plot_type)
    a.set_xlabel('x axis')
    a.set_ylabel('y axis')

plt.show()

辞書と描画するグラフの種類を指定するリストを使う方法

 これについても詳しい説明は省略します。plot_funcsは何のグラフを描きたいかを文字列のキーとして与えると、対応するメソッド呼び出しが行われるようなラムダ式がその値として返されるようになっています。そして、plot_typesは2つあるAxesオブジェクトで何のグラフを描くかを指定しています(ここではグラフのタイトルにもこれを流用しています)。

 ここまでpyplotインタフェースとOOインタフェースの違いを見てきましたが、重要なのはOOインタフェースではプログラマーの側がどのAxesオブジェクトを描画対象とするかを管理するのに対して、pyplotインタフェースではライブラリ任せになるという点です。

グラフに装飾を施してみる

 ここまでpyplotインタフェースとOOインタフェースの違いを見てきましたが、これはちょっとお休みにして、以下ではグラフの装飾について見ていきます。といっても、既にタイトルや軸ラベルは付加してきたので、まずはそれらについてざっくりとまとめておきましょう。

タイトルやラベルを設定してみる

 上ではグラフにタイトルや軸ラベルを設定していました。これらを以下にまとめます。

メソッド 説明 pyplotインタフェースでの同様な機能
Axes.set_titleメソッド そのAxesに描いたグラフのタイトルを設定する pyplot.title関数
Figure.suptitleメソッド Figure全体のタイトルを設定する pyplot.suptitle関数
Axes.set_xlabelメソッド そのAxesに描いたグラフのX軸のラベルを設定する pyplot.xlabel関数
Axes.set_ylabelメソッド そのAxesに描いたグラフのY軸のラベルを設定する pyplot.ylabel関数
タイトルと軸ラベルに関連するメソッド

 上では使っていなかったFigure.suptitleメソッドもここには含めました。これはFigure全体のタイトル、つまり複数のグラフに対して付ける全体的な名前を設定するために使用します。これに対応するのはpyplot.suptitle関数です。

 これらを使ってタイトルとラベルを付加するコードの例を以下に示します。

x = np.arange(5)
y = x ** 2

fig, ax = plt.subplots(1, 2, figsize=(10, 4))

plot_funcs = {
    'plot': lambda a, x, y, **kw: a.plot(x, y, **kw),
    'bar': lambda a, x, y, **kw: a.bar(x, y, **kw)
}
plot_types = ['plot', 'bar']
colors = ['red', 'blue']

for a, plot_type, color in zip(ax, plot_types, colors):
    plot_funcs[plot_type](a, x, y, color=color)
    a.set_title(plot_type)
    a.set_xlabel('x axis')
    a.set_ylabel('y axis')

fig.suptitle('title for Figure')

plt.show()

辞書と描画するグラフの種類を指定するリストを使う方法

 ここでは先ほどのコードにFigure.suptitleメソッドで全体のタイトルを設定する行を付け加えただけです。これらのメソッドにタイトルまたは軸ラベルを与えると、それらが対応する位置に描かれます。上のコードを実行すると次のような結果になります。

グラフに付けられたタイトルと軸ラベル グラフに付けられたタイトルと軸ラベル

 この他にラベルを表示する位置を指定するlocパラメーターなどもありますが、ここでは説明を省略します。気になる方はpyplot編の「見やすく伝わるグラフに仕上げよう」などを参照してください(以下、同様)。

凡例とグリッドを追加してみる

 グラフに凡例を追加するにはAxes.legendメソッドを、グリッド線を引くにはAxes.gridメソッドを使います。

メソッド 説明 対応するpyplotインタフェースの機能
Axes.legendメソッド 凡例を追加する pyplot.legend関数
Axes.gridメソッド グリッドを表示する pyplot.grid関数
凡例やグリッドを追加するメソッド

 legendメソッドに引数としてテキストを与えると、それが凡例のテキストとして使われます。legendメソッドに引数を渡さなかったときには、グラフを描画するメソッドの呼び出し時にlabelパラメーターに与えた引数の値が凡例として使われます。また、その他の指定方法もありますが、ここでは省略します。凡例を表示する位置はlocパラメーターに'best'/'upper right'/'lower left'などの値を与えます。

 gridメソッドには次のようなパラメーターがあります(一部)。

  • visible:グリッドを表示するかどうかを指定
  • which:主目盛り線('major')と副目盛り線('minor')とそれらの両方('both')のどちらでvisibleに与えた設定を有効にするかを指定。副目盛り線を引くならあらかじめAxes.minorticks_onメソッドを呼び出しておく必要がある
  • axis:X軸方向のグリッド線を引くか('x')、Y軸方向のグリッド線を引くか('y')、あるいはその両者のグリッド線を引くか('both')を指定する

 以下に例を示します。

x = np.arange(5)
y = x ** 2

fig, ax = plt.subplots(1, 2, figsize=(10, 4))

plot_funcs = {
    'plot': lambda a, x, y, **kw: a.plot(x, y, **kw),
    'bar': lambda a, x, y, **kw: a.bar(x, y, **kw)
}
plot_types = ['plot', 'bar']
colors = ['red', 'blue']

for a, plot_type, color in zip(ax, plot_types, colors):
    plot_funcs[plot_type](a, x, y, color=color)
    a.set_title(plot_type)
    a.set_xlabel('x axis')
    a.set_ylabel('y axis')
    # 凡例の追加
    a.legend([plot_type])
    # グリッドの設定
    a.minorticks_on()
    a.grid(True, 'major', color='gray')
    a.grid(True, 'minor', color='lightgray', linestyle='--')

fig.suptitle('title for Figure')

plt.show()

凡例とグリッドを表示するように設定

 上の例では描画するグラフの種類を指定するのに使っていたplot_funcsの内容を凡例にも流用しています。また、Axes.minorticks_onメソッドを呼び出しているので、主目盛り線と副目盛り線の両方が表示されるようになります(ここでは主目盛り線と副目盛り線を分けて設定していますが、同一の線種でよければ'both'を指定することで1行で設定できます)。

凡例とグリッドが表示されるようになった 凡例とグリッドが表示されるようになった

 上の例では凡例のテキストを直接指定していますが(リストとして渡している点に注意)、グラフ描画時にラベルを指定すると、引数なしでlegendメソッドを呼び出した場合にそのラベルが凡例のテキストとして使われます。

x = np.arange(5)
y = x ** 2

fig, ax = plt.subplots(1, 2, figsize=(10, 4))

plot_funcs = {
    'plot': lambda a, x, y, **kw: a.plot(x, y, **kw),
    'bar': lambda a, x, y, **kw: a.bar(x, y, **kw)
}
plot_types = ['plot', 'bar']
colors = ['red', 'blue']

for a, plot_type, color in zip(ax, plot_types, colors):
    plot_funcs[plot_type](a, x, y, color=color, label=plot_type)
    a.set_title(plot_type)
    a.set_xlabel('x axis')
    a.set_ylabel('y axis')
    # 凡例の追加
    a.legend()
    # グリッドの設定
    a.minorticks_on()
    a.grid(True, 'major', color='gray')
    a.grid(True, 'minor', color='lightgray', linestyle='--')

fig.suptitle('title for Figure')

plt.show()

描画時にラベルを設定しておけば、legendメソッドがそれを自動的に使ってくれる

 1つのAxesに複数の系列のグラフを描画した場合の例も示しておきましょう。

x = np.arange(5)
y = x ** 2
y2 = x ** 3

fig, ax = plt.subplots(1, 2, figsize=(10, 4))

line1, = ax[0].plot(x, y, color='red')
line2, = ax[0].plot(x, y2, color='green')
ax[0].set_title(plot)
ax[0].set_xlabel('x axis')
ax[0].set_ylabel('y axis')
ax[0].legend([line1, line2], ['squared', 'cubed'])

ax[1].bar(x, y, color='blue', label='bar')
ax[1].set_title(bar)
ax[1].set_xlabel('x axis')
ax[1].set_ylabel('y axis')
ax[1].legend()

fig.suptitle('title for Figure')

plt.show()

複数の系列のグラフをプロットする場合

 plotメソッドやbarメソッドは戻り値として、描画されたデータを表現するオブジェクトの配列を返送します。ここではそれらのリストの要素が1つだけなので、それをline1とline2に保存しておきます(「line1,」のようになっているのは、リストの要素を多重代入しているからです。これは「line1, *_ = ax[0].plot(……)」と同様です)。

 そして、legendメソッドにはline1とline2を要素とするリストと、それぞれに対応した凡例用のテキストを含んだリストを渡しています。これにより、以下のような結果が得られます。

複数の系列をグラフにプロットした場合の凡例 複数の系列をグラフにプロットした場合の凡例

 なお、このときにもグラフの描画時にlabelパラメーターにラベルを指定しておけば、legendメソッドに凡例のテキストを指定しなかったときには、そのラベルが自動的に使われます。


 今回はpyplotインタフェースとOOインタフェースの構造の違いを幾つかのグラフを描画しながら検討し、その次にグラフの装飾について説明しました。次回はOOインタフェースを使って、より細かくグラフを管理したり、コードを関数として再利用したりしてみることにしましょう。

「Pythonデータ処理入門」のインデックス

Pythonデータ処理入門

Copyright© Digital Advantage Corp. All Rights Reserved.

アイティメディアからのお知らせ

スポンサーからのお知らせPR

注目のテーマ

4AI by @IT - AIを作り、動かし、守り、生かす
Microsoft & Windows最前線2025
AI for エンジニアリング
ローコード/ノーコード セントラル by @IT - ITエンジニアがビジネスの中心で活躍する組織へ
Cloud Native Central by @IT - スケーラブルな能力を組織に
システム開発ノウハウ 【発注ナビ】PR
あなたにおすすめの記事PR

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。