今回はPython外部ライブラリのMatplotlibを使用してグラフ内にアニメーションを描画していきたいと思います。
グラフ描画ライブラリのMatplotlibでは、静止画像の他にリアルタイムもしくわアニメーションといった描画を実装することができます。
アニメーションのようにグラフ内のデータをリアルタイムで動かす機能として幾つか存在しますが、インターネット内で良く試されているのは「plt.claメソッド+plt.pauseメソッド」「ArtistAnimationクラス」「FuncAnimationクラス」だと思われます。
他にもMatplotlibの機能として別のリアルタイム描画の実装を見かけましたが上記の3つが最も多かった印象です。
その3つの機能で最も簡単にアニメーションできるのが「plt.claメソッド+plt.pauseメソッド」を使用した方法です。
「plt.claメソッド+plt.pauseメソッド」での実装はfor文内に描画したいグラフ「plt.claメソッド+プロット+plt.pauseメソッド」のように挟むことでデータを順次更新しながら描画させていくので簡単ですが、「gif」ファイルや「mp4」ファイルなどに保存することができません。
# 「plt.claメソッド+plt.pauseメソッド」の例
import matplotlib.pyplot as plt
data = []
for i in range(10):
plt.cla() # 描画されたデータをリセット
data.append(i)
plt.xlim(0, 10) # x軸固定
plt.ylim(0, 10) # y軸固定
plt.plot(data, data) # 描画
plt.pause(1) # 描画されたグラフを一時的に留める
上記の詳しい実装は以下の記事をご参照ください。
残り2つのアニメーション機能ではオブジェクトを設定してからアニメーションしていくので、少し手間ですが「gif」「mp4」のような動画ファイルを作成することができます。
そして「ArtistAnimationクラス」と「FuncAnimationクラス」の違いは、シンプルな実装→「ArtistAnimationクラス」と複雑な実装が可能→「FuncAnimationクラス」であるという個人的な結果となりました。
FuncAnimationクラスについては以下の記事をご参照ください。
ArtistAnimationクラスでの実装は「plt.claメソッド+plt.pauseメソッド」での実装と同じようなコーディングで実装できるので分かりやすいですが、FuncAnimationクラスではパラメータ引数が多くロジックを理解するのが少し面倒です。
なのでここでは「ArtistAnimationクラス」を中心に実装していきたいと思います。
実行環境 |
---|
Windows Subsystem for Linux |
Python 3.6.9 |
pip 9.0.1 |
jupyter notebook |
ffmpeg |
使用ライブラリ | ライセンス |
---|---|
matplotlib==3.1.1 | PSF |
Pillow==6.1.0 | HPND |
ArtistAnimationクラスのパラメータ引数は以下のようになっています。
ArtistAnimation(
fig=figureオブジェクト, # 図
artists=artist_listオブジェクト, # プロットオブジェクトが格納されたリストオブジェクト
interval=200, # 処理速度のミリ秒
repeat_delay=None, # repeatがTrueの場合、処理が終わった後の遅延ミリ秒
repeat=True, # 繰り返す場合
blit=False # 描画を最適化するための制御
)
「fig」「artists」は必須引数で、それ以外はデフォルト値として設定されています。
必須引数の「fig、artists」と「interval」を設定してシンプルなアニメーションを描画していきます。
import matplotlib.pyplot as plt
from matplotlib.animation import ArtistAnimation
# jupyter notebook環境の場合
%matplotlib
y = [0] * 30
x_data = []
y_data = []
artist_list = []
fig, ax = plt.subplots()
for index, i in enumerate(y):
ax.set_xlim(0, 30)
ax.set_ylim(-1, 1)
x_data.append(index)
y_data.append(i)
line, = ax.plot(x_data, y_data) # line2Dオブジェクトを取得
# line = axplot(x_data, y_data) # listオブジェクトとして取得=>[line2Dオブジェクト]
artist_list.append([line]) # line2Dをlistオブジェクトとして追加
# artist_list.append(line) # listオブジェクトの場合はそのまま渡す
ani = ArtistAnimation(
fig=fig, # 図
artists=artist_list, # 出来上がったグラフのリストを渡す
interval=100,
)
プロットオブジェクトを格納しているfor文内では、「line,」と「line」としてプロットオブジェクトを代入している2行があります。
for index, i in enumerate(y):
...
...
line, = ax.plot(x_data, y_data) # line2Dオブジェクトを取得
# line = axplot(x_data, y_data) # listオブジェクトとして取得=>[line2Dオブジェクト]
artist_list.append([line]) # line2Dをlistオブジェクトとして追加
# artist_list.append(line) # listオブジェクトの場合はそのまま渡す
変数末尾に「,」を付けるか付けないかでプロットオブジェクトの返り値が異なります。
# 「,」を付けた場合
line, = ax.plot([0], [0])
print(type(line))
# <class 'matplotlib.lines.Line2D'>
# 付けない場合
line = ax.plot([0], [0])
print(type(line))
print(type(line[0]))
# <class 'list'>
# <class 'matplotlib.lines.Line2D'>
artists引数に渡す「artist_list」ではリストオブジェクトとして追加する必要があるので、プロットオブジェクトをそのまま追加してしまうと「TypeError: 'Line2D' object is not iterable」というエラーになってしまいます。
line, = ax.plot([0], [0])
artist_list(line)
ani = ArtistAnimation(fig, artist_list)
TypeError: 'Line2D' object is not iterable
ArtistAnimationクラスでのartists引数に着目したソースコードは以下となっています。
# ArtistAnimationクラス内の1部抜粋
for f in self.new_frame_seq(): # artists=[[i, i, i], [k, k, k]]
for artist in f: # [i, i, i]
...
結果的にartists引数に渡すリストは、[[ax.plot, ax.text, etc], [ax.plot, ax.text, etc]]のような形になっている必要があります。
この後は図にタイトル、グラフ内にテキストの書き込みを実装していくのでその辺も参考にしてみてください。
尚、棒グラフや円グラフといった複数のプロット、複数のグラフを描画したアニメーションは以下で実装しています。
プロットするデータを変更し、ラインに変化を付けたアニメーションを実装してみます。
タイトルですが、通常なら「ax.set_title()」を使用するところ「ax.text()」で位置を定めてタイトル化させています。
import matplotlib.pyplot as plt
from matplotlib.animation import ArtistAnimation
# jupyter notebook環境の場合
%matplotlib
y = [0] * 30
# yの10, 11, 12, 13, 14, 15, 16番目の要素を変更
y[10], y[11], y[12], y[13], y[14], y[15], y[16] = 0.05, -0.05, 0.05, -0.05, 0.05, -0.05, 0.4
x_data = []
y_data = []
artist_list = []
fig, ax = plt.subplots()
for index, i in enumerate(y):
ax.set_xlim(0, 30)
ax.set_ylim(-1, 1)
x_data.append(index)
y_data.append(i)
line, = ax.plot(x_data, y_data)
title = ax.text(0, 1.05, 'ArtistAnimation: {}'.format(index), fontsize=15) # 追加
artist_list.append([line, title]) # リスト内にtitleを追加
ani = ArtistAnimation(
fig=fig,
artists=artist_list,
interval=100,
)
グラフにタイトルを表示させる場合は「ax.set_title()」で問題無いのですが、上図のようにArtistAnimationで更新させようとすると上手く表示されません。
なので「ax.text(x軸, y軸, テキスト)」でグラフ上部にテキストを定め表示させています。
「title」変数でも使用しているMatplotlibのTextオブジェクトを少し操作したアニメーションです。
それ以外は特に変更していません。
import matplotlib.pyplot as plt
from matplotlib.animation import ArtistAnimation
# jupyter notebook環境の場合
%matplotlib
y = [0] * 30
y[10], y[11], y[12], y[13], y[14], y[15], y[16] = 0.05, -0.05, 0.05, -0.05, 0.05, -0.05, 0.4
faces = ['(^.^)y-~', '\(0o0)/ -'] # 追加
numbers = [10, 12, 14, 16] # 追加
x_data = []
y_data = []
artist_list = []
fig, ax = plt.subplots()
for index, i in enumerate(y):
ax.set_xlim(0, 30)
ax.set_ylim(-1, 1)
x_data.append(index)
y_data.append(i)
line, = ax.plot(x_data, y_data)
title = ax.text(0, 1.05, 'ArtistAnimation: {}'.format(index), fontsize=15)
textvar = ax.text(0, 0, None) # 追加
""" ------- ここから追加 --------- """
if index >= 10: # リストの10番目の要素からテキストを描画する
index -= 9 # 顔文字のポジションをずらす
if index in numbers: # numbers = [10, 12, 14, 16]
textvar.set_position((index, y_data[index]+0.1))
textvar.set_text(faces[1]) # \(0o0)/ -
else:
textvar.set_position((index, y_data[index]+0.05))
textvar.set_text(faces[0]) # (^.^)y-~
""" ------ ここまで追加 ----------"""
artist_list.append([line, textvar, title]) # textvarを追記
ani = ArtistAnimation(
fig=fig,
artists=artist_list,
interval=100,
)
描画したアニメーションをgifファイルへ保存するには、画像処理ツールの「ImageMagick」、もしくわPython外部ライブラリの「pillow」が必要となります。
ここではpillowをインストールしてgifファイルの保存を行います。
$ pip install pillow
インストールされていれば、以下の2通りの方法でgifファイルへ保存することができます。
# アニメーションオブジェクト
ani = ArtistAnimation()
# 方法1
ani.save("animation_1.gif", writer="pillow")
# 方法2
from matplotlib.animation import PillowWriter
ani.save("animation_2.gif", writer=PillowWriter)
保存の詳細に関しては公式ドキュメントをご参照ください。
mp4ファイルへ保存するには、動画・音声処理ライブラリの「FFmpeg」ツールをインストールします。
私の実行環境はWindows Subsystem for Linuxなので、Linuxコマンドでのインストールとなります。
※各OS環境のインストール方法で行ってください。
$ sudo apt install ffmpeg
インストールされていれば、以下の2通りの方法でmp4ファイルへ保存することができます。
# アニメーションオブジェクト
ani = ArtistAnimation()
# 方法1
ani.save("animation_1.mp4", writer="ffmpeg")
# 方法2
from matplotlib.animation import FFMpegWriter
ani.save("animation_2.mp4", writer=FFMpegWriter)
画質などの調整もできるので、保存の詳細に関しては公式ドキュメントをご参照ください。
FuncAnimationの実装はArtistAnimationと違って複雑な設定を行える機能です。
複雑さ故に説明が長くなってしまうので、宜しければ以下の記事をご参照ください。
今回は以上となります。
最後までご覧いただきありがとうございました。