matplotlibをオブジェクト指向スタイルで使う その1


このエントリーをはてなブックマークに追加

Matlabスタイルとオブジェクト指向スタイル

matplotlibは、以下のようにmatplotlib.pyplotに属する関数を順番に呼んで使われることが多いです。

import matplotlib.pyplot as plt
plt.plot([1,2,3,4])
plt.ylabel('some numbers')
plt.show()

これはMatlabでのグラフ描画の方法に近いので、Matlabスタイルと呼ぶことにします。

Matlabスタイルではmatplotlibが内部に状態を持ってしまいます。この性質は、インタラクティブな環境でコマンドを打ち込んでグラフを描画するときには都合がよいですが、逆にスクリプト作成時には混乱のもとになります。

スクリプト作成時には、Matlabスタイルではなくオブジェクト指向スタイルを採用するのがお勧めです。オブジェクト指向スタイルの書き方については、matplotlib入門 - りんごがでているの「オブジェクト指向なプロット」に分かりやすく説明されています。以下でも簡単に説明します。

オブジェクト指向スタイルの一例

よくある流れは以下のとおりです。

  • figureを作る
    • ウィンドウに相当
    • 例:fig = plt.figure(1)
  • figureの中にどうグラフを並べるかを決める
    • 例:2行1列
  • figureの中に並べるグラフの数だけaxesを作る
    • 例:ax1 = fig1.add_subplot(211); ax2 = fig1.add_subplot(212)
    • ここでadd_subplotの中の数字は、[行の数(2)] + [列の数(1)] + [何個目のグラフか]
  • 各axesについてグラフを描く
    • 例: ax1.plot([1, 3, 5, 2, 6]); ax2.plot(x, f(x))

figureとaxesの関係を絵で描くと以下のようになります。

  ____________
  | ________  |
  | |       | |
  | | axes1 | |
  | |_______| |
  | ________  |
  | |       | |
  | | axes2 | |
  | |_______| |
  |___________| 
      fig

axesという単語はaxis(軸)の複数形です。グラフの描画領域と思っておけば良さそうです。(あまりよい名前とは思えませんね)

コード例

以下にコード例を載せます。Python3で動作確認しています。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# matplotlibをオブジェクト指向らしく使うサンプル
# 2つのグラフ(axes)を含む図(figure)を、2つ作成。表示して保存
# 参考:
#   http://bicycle1885.hatenablog.com/entry/2014/02/14/023734
#   http://matplotlib.org/examples/pylab_examples/pythonic_matplotlib.html

import matplotlib.pyplot as plt
import numpy as np

def f(x,y):
    return (1 - x / 2 + x**5 + y**3) * np.exp(-x**2 -y**2)

def main():

    t = np.arange(0.0, 1.0, 0.01)

    ####################
    #1つ目のウィンドウ
    ####################
    fig1 = plt.figure(1)

    ################################
    #1つ目のウィンドウ 1つ目のグラフ
    ################################
    ax1 = fig1.add_subplot(211)
    ax1.plot(t, np.sin(2*np.pi*t))
    ax1.grid(True)
    ax1.set_ylim( (-2,2) )
    ax1.set_xlabel('t')
    ax1.set_ylabel('f(t)')
    ax1.set_title('sin function')

    ################################
    #1つ目のウィンドウ 2つ目のグラフ
    ################################
    ax2 = fig1.add_subplot(212)
    ax2.plot(t, np.cos(2*np.pi*t))
    ax2.grid(True)
    ax2.set_ylim( (-2,2) )
    ax2.set_xlabel('t')
    ax2.set_ylabel('f(t)')
    ax2.set_title('cos function')



    ####################
    # 2つ目のウィンドウ
    ####################
    fig2 = plt.figure(2)

    ################################
    # 2つ目のウィンドウ 1つ目のグラフ
    ################################
    # 等高線
    ax1 = fig2.add_subplot(211)
    n = 256
    x = np.linspace(-3, 3, n)
    y = np.linspace(-3, 3, n)
    X,Y = np.meshgrid(x, y)

    ax1.contourf(X, Y, f(X, Y), 8, alpha=.75, cmap=plt.cm.hot)
    C = ax1.contour(X, Y, f(X, Y), 8, colors='black', linewidth=.5)
    ax1.clabel(C, inline=1, fontsize=10)
    ax1.set_title('contour')

    ################################
    # 2つ目のウィンドウ 2つ目のグラフ
    ################################
    ax2 = fig2.add_subplot(212)
    ax2.plot([1, 4, 3, 6, 5, 9])
    ax2.grid(True)
    ax2.set_ylabel('values')
    ax2.set_title('points')



    ################################
    # 2つのウィンドウを表示
    ################################
    fig1.tight_layout()  # グラフの文字がかぶらないようにする
    fig2.tight_layout()  # グラフの文字がかぶらないようにする
    plt.show()

    ################################
    # 2つの画像を保存
    ################################
    fig1.savefig('fig1.png')
    fig2.savefig('fig2.png')


main()

実行結果

以下の2枚のグラフが表示・保存されます。

f:id:minus9d:20150126225608p:plain

f:id:minus9d:20150126225616p:plain

plt.subplots()を使う方法 (2016/4/20追記)

plt.subplots()という関数を使うと、上で説明した方法より簡潔に同じことが実現できます。別記事 matplotlibをオブジェクト指向スタイルで使う その2 - minus9d's diary にまとめました。

参考リンク