LoginSignup
2
0

More than 3 years have passed since last update.

MatplotlibのFuncAnimationのコールバック関数が初期値で2回実行される

Last updated at Posted at 2021-01-25

matplotlib.animation.FuncAnimationの不思議な挙動

(コードサンプルは @yubais 様のこちらの記事中にあるfuncanimation.pyを参考にしました.)

例えば以下のようなコードを考えます.

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()

コードの解説が必要な場合は以下をクリックしてください.


コード解説
  1. 描画領域を生成しfigとする
  2. アニメーションを1フレーム進めるごとに呼ばれ, 実際に描画を行うコールバック関数updateを定義. iupdateが呼ばれるたびにインクリメントされながら内部で渡されるカウンタ.
  3. アニメーションを生成する対称の描画領域にfig, コールバック関数にupdateを設定し, 100msごとにフレームを進めるよう設定してアニメーションをスタート

さて, 上記のコードを実行してターミナルを眺めると

i=0
i=0
i=1
i=2
i=3
(以下略)

となり, updatei=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で一度しか呼んでほしくない場合には

funcanimation.py
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()がなぜ定義されているのか分かりにくいような気がしますが, とりあえずこれで解決できたのでよしとします.

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0