matplotlib.animation.FuncAnimation
の不思議な挙動
(コードサンプルは @yubais 様のこちらの記事中にあるfuncanimation.py
を参考にしました.)
例えば以下のようなコードを考えます.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
def update(i):
plt.cla() # 現在描写されているグラフを消去
rand = np.random.randn(100) # 100個の乱数を生成
plt.plot(rand) # グラフを生成
print(f"i={i}") # 現在のiの値をターミナルに表示
ani = animation.FuncAnimation(fig, update, interval=100)
plt.show()
コードの解説が必要な場合は以下をクリックしてください.
コード解説
update
を定義. i
はupdate
が呼ばれるたびにインクリメントされながら内部で渡されるカウンタ.fig
, コールバック関数にupdate
を設定し, 100msごとにフレームを進めるよう設定してアニメーションをスタート
さて, 上記のコードを実行してターミナルを眺めると
i=0
i=0
i=1
i=2
i=3
(以下略)
となり, update
がi=0で2回呼ばれていることが分かります. 今回私はi=0のときだけ処理を分けたいと思っていたためこれが問題となり, 公式ドキュメントを調べてみました.
調査結果と解決法
Matplotlib Documentation 3.3.3によると, matplotlib.animation.FuncAnimation
にはinit_func
という引数が存在し, これは
A function used to draw a clear frame. If not given, the results of drawing from the first item in the frames sequence will be used. This function will be called once before the first frame.
(拙訳) 初期フレームを描くための関数です. もし明示的に与えられない場合, 1枚目のフレームが初期フレームを描くために使われます. この関数は1枚目のフレームが描画される前に1度だけ呼ばれます.
とあります. つまりinit_func
を与えない場合, コールバック関数がi=0のもとで初期フレームを描くために1回, 実際にアニメーションフレームを描き始めたときに1回と計2回呼ばれるため, 初期フレームが2回描画されるのは正常な動作だということです.
したがって, 今回の私のようにコールバック関数をi=0で一度しか呼んでほしくない場合には
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
def init():
pass # 何もしない
fig = plt.figure()
def update(i):
plt.cla() # 現在描写されているグラフを消去
rand = np.random.randn(100) # 100個の乱数を生成
plt.plot(rand) # グラフを生成
print(f"i={i}") # 現在のiの値をターミナルに表示
ani = animation.FuncAnimation(fig, update, init_func=init, interval=100)
plt.show()
とすればよさそうです.
一見するとinit()がなぜ定義されているのか分かりにくいような気がしますが, とりあえずこれで解決できたのでよしとします.