Help us understand the problem. What is going on with this article?

matplotlibでアニメーション

More than 3 years have passed since last update.

概要

python の matplotlib を使えば手軽に見栄えのいいグラフが作成できます。グラフのアニメーションを作成する機能も matplotlib 標準で提供されているので、ここではその使い方を網羅的にまとめておきます。

アニメーションの作成

大きく次の2つの方法が提供されています。

  • あらかじめ作成したグラフのオブジェクトのリストを渡してアニメーションする(ArtistAnimation)
  • グラフ更新用の関数を定義してそれによりアニメーションする(FuncAnimation)

前者がなぜArtistAnimationかというと、matplotlib ではグラフオブジェクトを artist と呼んでいるからです。ここでもその呼び方を使います。ある程度性能が要求される場合や、アニメーションの中でタイトルを変更したり細かい制御が必要な場合は後者を選択することになります。

それでは、それぞれをコード例とともに紹介します。

ArtistAnimation

ArtistAnimationに渡すのはartistのリストのリスト1です。アニメーションの各フレームをフレーム1, フレーム2, ... であらわすと、


[
  [
    フレーム1で表示するartist_1, フレーム1で表示するartist_2, ...
  ],
  [
    フレーム2で表示するartist_1, フレーム2で表示するartist_2, ...
  ],
  ...
]

となります。matplotlibのグラフの種類や関数ごとにartistを返すものやartistのリストを返すものが混在しているので注意が必要です。たとえば以下の例では、plot関数から戻ってきたartistのリストをそのままappendしています。

import matplotlib.pyplot as plt
from matplotlib.animation import ArtistAnimation
import numpy as np
fig, ax = plt.subplots()
artists = []
x = np.arange(10)
for i in range(10):
    y = np.random.rand(10)
    im = ax.plot(x, y)
    artists.append(im)
anim = ArtistAnimation(fig, artists, interval=1000)
fig.show()

scatterなどartistを返す関数の場合はリストにラッピングしたものをappendします。

import matplotlib.pyplot as plt
from matplotlib.animation import ArtistAnimation
import numpy as np
fig, ax = plt.subplots()
artists = []
x = np.arange(10)
for i in range(10):
    y = np.random.rand(10)
    im = ax.scatter(x, y)
    artists.append([im])
anim = ArtistAnimation(fig, artists, interval=1000)
fig.show()

FuncAnimation

アニメーションのフレームごとに関数が呼ばれます。処理は次の2つのパターンがありえます。

  • 毎回新規にartistを作成する(前回作成したartistを保持して明示的に消してあげる必要がある)
  • 作成済みのartistのデータを更新する

それぞれのコードを紹介します。

作成パターン

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
fig, ax = plt.subplots()
ims = []
x = np.arange(10)

ax.set_xlim(0, 9)
ax.set_ylim(0, 1)

def update_anim(i):
    y = np.random.rand(10)
    if len(ims) > 0:
        im = ims.pop()
        im.remove()
    im, = ax.plot(x, y)
    ims.append(im)

anim = FuncAnimation(fig, update_anim, interval=1000)
fig.show()

データ更新パターン

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
fig, ax = plt.subplots()
artists = []
im, = ax.plot([], [])
x = np.arange(10)
ax.set_xlim(0, 9)
ax.set_ylim(0, 1)

def update_anim(frame):
    y = np.random.rand(10)
    im.set_data(x, y)

anim = FuncAnimation(fig, update_anim, blit=False)
fig.show()

当然データ更新の方が効率的です。

blit=True

また、blitというオプション引数をTrueに指定すれば、コンピュータグラフィックで用いられるblittingという手法をつかってさらに描画を高速化できるようです。この場合関数のなかからblitting対象のartistのリストをリターンする必要があります。

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
fig, ax = plt.subplots()
artists = []
im, = ax.plot([], [])
x = np.arange(10)
ax.set_xlim(0, 9)
ax.set_ylim(0, 1)

def update_anim(frame):
    y = np.random.rand(10)
    im.set_data(x, y)
    return im,

anim = FuncAnimation(fig, update_anim, blit=True)
fig.show()

アニメーションの保存・共有

作成したアニメーションは以下のようにファイルに保存することができます。

gifに書き出し

anim.save('anim.gif', writer='imagemagick', fps=4)

mp4に書き出し

anim.save('anim.mp4', writer='ffmpeg', fps=4)

またjupyter notebook上での作業の場合、 %matplotlib inlineのかわりに%matplotlib nbaggと指定すればユーザがインタラクティブに操作することができるグラフがインラインで表示されます。アニメーションも実行されます。ただし、重いし、nbviewerでは表示されないので、むしろインラインで表示したいだけなら以下のように動画を埋めこむのがおすすめです。

from IPython.display import HTML
HTML(anim.to_html5_video())

脚注


  1. 一般的にはforでループ可能なiterableなオブジェクトです。ここでは一般的な意味でリストと呼びます。 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away