【Python】Matplotlibでグラフのリアルタイム(アニメーション)描画を実装する


投稿日 2021年2月5日 >> 更新日 2023年3月1日

今回はPython外部ライブラリのMatplotlibを使用してグラフのリアルタイム(アニメーション)描画を実装していきます。

通常グラフは静止画として描画されますが、グラフを静止させずに動かすことである時点からの進行具合を一度のプロットで確認することができます。

グラフを動画のように描画する機能には、Matplotlibのpyplot.pauseメソッドやAnimationクラスを使用することにより実行することができます。

Animationクラスを実装した記事は以下をご参照ください。

この記事では簡単にリアルタイムな描画ができるpyplot.pauseメソッドを中心に実装していきたいと思います。

実行環境&使用ライブラリ

実行環境
Windows Subsystem for Linux
Python 3.6.9
pip 9.0.1
jupyter notebook
使用ライブラリ ランセンス
matplotlib==3.1.1 PSF

単純な棒グラフの描画

まずは棒グラフで基本的なグラフの描画を実装します。

データは日本と米国の人口を表しており、各国の最高値をプロットします。

静止画ですので、コード末尾に「plt.show」メソッドで図をブロックングします。


import matplotlib.pyplot as plt
import time
# jupyter notebookの場合
%matplotlib inline

country = ['JPN', 'USA'] # 国名
population = [130, 300] # ミリオン単位の国別人口(約)

plt.figure(figsize=(10, 5)) # 図の幅と高さの指定
plt.bar(country, population, color=['b', 'r'], width=0.2) # 棒グラフとして描画
plt.show()

デフォルト値のプロットは少し表現が少ないので、下の軸(X軸)の間隔やテキストなど加えて表現力を与えます。


import matplotlib.pyplot as plt
import time
# jupyter notebookの場合
%matplotlib inline

country = ['JPN', 'USA'] # 国名
population = [130, 300] # 国別の人口(約)

fontsize=15 # 図に挿入されるテキストの大きさ

plt.figure(figsize=(10, 5)) # 図の幅と高さの指定
plt.bar(country, population, color=['b', 'r'], width=0.2) # 棒グラフとして描画

""" 追記 """
plt.title("Population comparison between Japan and the United States", fontsize=fontsize) # 図のタイトル
plt.text(country[0], population[0]+5, "{} Million".format(population[0]), fontsize=fontsize) # グラフ内テキストを挿入(日本軸)
plt.text(country[1], population[1]+5, "{} Million".format(population[1]), fontsize=fontsize) # グラフ内テキストを挿入(米国)
plt.xlabel("Country", fontsize=fontsize) # X軸のタイトル
plt.ylabel("Population", fontsize=fontsize) # Y軸のタイトル
plt.xlim(-1, 2) # X軸の範囲を指定
plt.ylim(0, 330) # Y軸の範囲を指定
plt.grid() # グラフ内にメモリに合わせた線を表示
""" ここまで """

plt.show()

各種の設定内容はコード内にコメントアウト(「#」)として説明しているのでそちらを確認してみてください。

pauseメソッドによるリアルタイム(アニメーション)描画

先ほど作成した棒グラフを使用してリアルタイムな描画を実装していきます。

静止画の描画では、1つのグラフだけが作成されてブロッキングされているので、棒をリアルタイムに動かすにはデータ量を増やしながら何枚もグラフをプロットする必要があります。

パラパラ漫画のように表示させることで棒の進行具合がリアルタイムで確認できます。

なのでfor文を使って順番にPopulationデータを更新していきます。

そして途中でプロットがブロックされないように「plt.show」メソッドを削除し、代わりに「plt.pause(秒間隔)」メソッドで一時的にグラフを停止させておきます。

plt.pauseメソッドはPython標準ライブラリのtime.sleepのように扱うことができます。

for文を実行中にグラフがデータ量分プロットされてしまうので、「plt.cla」メソッドでグラフをクリア(かたす)しながら回します。


import matplotlib.pyplot as plt

# jupyter notebookの場合
%matplotlib

country = ['JPN', 'USA'] # 国名
population = [131, 301] # 国別の人口(約)
jpn = list(range(population[0])) + [130] * 170 # データの数を米国に合わせた日本のデータ
usa = range(population[1]) # 米国のデータ
print(len(jpn), len(usa)) # 日本と米国のデータを揃える
# 301 301

fontsize=15 # 図に挿入されるテキストの大きさ

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

for j, u in zip(jpn, usa):
    plt.cla() # 描画されたグラフをクリアにする
    plt.bar(country, [j, u], width=0.3, color=['b', 'r'])
    plt.title("Population comparison between Japan and the United States", fontsize=fontsize)
    plt.text(country[0], j+5, "{} Million".format(j), fontsize=fontsize)
    plt.text(country[1], u+5, "{} Million".format(u), fontsize=fontsize)
    plt.xlabel("Country", fontsize=fontsize)
    plt.ylabel("Population", fontsize=fontsize)
    plt.xlim(-1, 2)
    plt.ylim(0, 330)
    plt.grid()
    plt.pause(0.1) # 一時グラフを停止する(引数には間隔を指定)

# 終わったらグラフを閉じる場合
#plt.close()

プロットする間隔はpauseメソッドの引数に秒間隔(1=1秒)を指定することで進行速度を調整することができます。

各メソッドの説明は以下のドキュメントをご参照ください。

複数のグラフによるリアルタイム描画

図の中に複数のグラフを作成するには、「plt.subplots(行, 列)」メソッドや「plt.Figure.add_subplot(行, 列, index)」メソッドを使用します。

複数グラフの実装は以下の記事で説明しているので宜しければご参照ください。

作成したグラフの中身は、先ほどの棒グラフと新しく円グラフを設定して描画させることにします。

円グラフの設定は以下の記事をご参照ください。

複数のグラフを作成したことにより各種装飾の設定の振る舞いが変わりますが、リアルタイムなプロットの方法は同じようにfor文で回すだけです。


import matplotlib.pyplot as plt

# jupyter notebookの場合
%matplotlib

country = ['JPN', 'USA'] # 国名
population = [131, 301] # 国別の人口(約)
jpn = list(range(population[0])) + [130] * 170 # データの数を米国に合わせた日本のデータ
usa = range(population[1]) # 米国のデータ

fontsize=15
fig, axs = plt.subplots(1, 2, figsize=(10, 5)) # 図の中に2つのサブプロット作る

for j, u in zip(jpn, usa):
    """
    棒グラフのサブプロット
    """
    axs[0].cla()
    axs[0].bar(country, [j, u], width=0.3, color=['b', 'r'])
    axs[0].text(country[0], j+5, "{} Million".format(j), fontsize=fontsize)
    axs[0].text(country[1], u+4, "{} Million".format(u), fontsize=fontsize)
    axs[0].set_xlabel("Country", fontsize=fontsize)
    axs[0].set_ylabel("Population", fontsize=fontsize)
    axs[0].set_xlim(-1, 2)
    axs[0].set_ylim(0, 330)
    axs[0].grid()
    axs[0].set_title("graph bar")

    """
    円グラフ用のサブプロット
    """
    axs[1].cla()
    axs[1].pie([j, u], labels=country, autopct='%1.1f%%', wedgeprops={'width': 0.6, 'linewidth': 4, 'edgecolor': 'w'})
    axs[1].set_title("graph circle")

    # 図のタイトル
    plt.suptitle("Population comparison between Japan and the United States", fontsize=fontsize)
    plt.pause(0.1)

# 終わったらグラフを閉じる場合
#plt.close()

jupyter notebookで表示されない場合

「%matplotlib」と記入することでお使いのOSのGUIが起動しグラフが表示されます。

# jupyter notebook

# 通常のプロットの場合
%matplotlib inline
# plt.pauseで実行する場合
%matplotlib

今回は簡単でシンプルなMatplotlibでのリアルタイム(アニメーション)描画を実装してきましたが、複雑なアニメーション描画を実装したい場合はAnimationクラスを使用するのが良い選択との事です。

余力のある方はぜひ様々な描画を楽しんでみて下さい。

それでは以上となります。

最後までご覧いただきありがとうございました。