animatplot で matplotlibによる gif アニメーションを簡単に作る

# 本日は
animatplot というライブラリの紹介です。

## 前置き
可視化ツールとしての matplotlib　は静的なグラフの描画だけでなく、パラメータによって変化するグラフの様子をgifなどのアニメーションで表現することができます。数学的な対象物をアニメーションで視覚化するというのは我々を楽しくさせます。

![test.gif](https://qiita-image-store.s3.amazonaws.com/0/135978/8bd6cb6b-7513-45da-0ae2-8c408b903c93.gif)

楽しい反面、手間がかかります。例えば、アニメーションを実装するには `matplotlib.animation.FuncAnimation` という matplotlib の機能を用いることで実現できます。例えば下記のような実装になります。

```python

import itertools
import logging
logging.basicConfig(level=logging.INFO)
import random

import matplotlib.animation as animation
from matplotlib import pyplot as plt
import numpy as np


def do_somehting(xs, ts):
    for t in ts:
        yield xs, np.sin(2 * np.pi * (xs + t))


class Visualizer():

    def __init__(self, xs, ts):
        self.fig, self.ax = plt.subplots()
        self.iterator = itertools.cycle(do_somehting(xs, ts))

    def init_func(self):
        self.step = 0

    def update(self, *args):
        self.ax.clear()
        logging.info("i={}".format(self.step))
        xs, ys = self.iterator.__next__()
        self.ax.plot(xs, ys)
        self.ax.set_title("step={}".format(self.step))
        self.step += 1


def main():
    xs = np.linspace(0, 1, 50)
    ts = np.linspace(0, 1, 20)
    visualizer = Visualizer(xs, ts)

    ani = animation.FuncAnimation(visualizer.fig,
                                  visualizer.update,
                                  init_func=visualizer.init_func,
                                  frames=100,
                                  interval=100)

    ani.save('test.gif', writer="imagemagick")


if __name__ == '__main__':
    main()

```

このコードを実行して `test.gif` を開くとアニメーションが見られます。

慣れれば上のようなコードを書くことができるのですが、ここまでたどり着くのに結構な学習コストがかかっていますし（こういうアプリを作るのに毎回作り方をググっている）、やるべき手続きが多い(updateメソッドをいちいち書く必要がある)ことから静的な描画の実装に比べるとかなりの手間がかかります。

# そんな私たちに animatplot

[animatplot](https://github.com/t-makaro/animatplot) はこういうアニメーション作成をもっと簡単にさせるインターフェースを提供してくれます。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="en" dir="ltr">animatplot: A <a href="https://twitter.com/hashtag/Python?src=hash&amp;ref_src=twsrc%5Etfw">#Python</a> package for creating animated <a href="https://twitter.com/hashtag/dataviz?src=hash&amp;ref_src=twsrc%5Etfw">#dataviz</a>. Built on top of matplotlib. <a href="https://twitter.com/hashtag/DataScience?src=hash&amp;ref_src=twsrc%5Etfw">#DataScience</a><a href="https://t.co/kexyqDhCLV">https://t.co/kexyqDhCLV</a> <a href="https://t.co/ogktdMfXCj">pic.twitter.com/ogktdMfXCj</a></p>&mdash; Randy Olson (@randal_olson) <a href="https://twitter.com/randal_olson/status/1026861512646905856?ref_src=twsrc%5Etfw">2018年8月7日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#　導入

pip で入ります。

```shell-session
$ pip install animatplot
```

# Example 1

ドキュメントのチュートリアルから引っ張ってきます。

```python
"""
Introduction to animatplot
https://animatplot.readthedocs.io/en/latest/tutorial/getting_started..html#Basic-Animation
"""

import numpy as np
from matplotlib import pyplot as plt
import animatplot as amp


def main():
    xs = np.linspace(0, 1, 50)
    ts = np.linspace(0, 1, 20)
    Xs = np.asarray([xs] * len(ts))
    Ys = [np.sin(2 * np.pi * (xs + t)) for t in ts]
    """
    # you can also write
    Xs, Ts = np.meshgrid(xs, ts)
    Ys = np.sin(2 * np.pi * (Xs + Ts))
    """
    block = amp.blocks.Line(Xs, Ys)
    anim = amp.Animation([block])
    anim.save_gif("sin_curve")
    plt.show()


if __name__ == '__main__':
    main()
```

必要な描画データを `Xs`, `Ys` に突っ込んでおいてそれを `amp.blocks.Line(Xs,Ys)` という形で渡すだけでOKです。

上記のコードを実行すると `sin_curve.gif` というものが得られます。

# Example 2 

なんとプログレスバーのようなものを導入できるようです。

![sin_curve_with_contol.gif](https://qiita-image-store.s3.amazonaws.com/0/135978/7dff7849-3b15-cbb0-353a-9ab12a5609fc.gif)

```python
import numpy as np
from matplotlib import pyplot as plt
import animatplot as amp


def main():
    fig, ax = plt.subplots()
    ax.set_title("sin curve animation")
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    xs = np.linspace(0, 1, 50)
    ts = np.linspace(0, 1, 20)
    Xs, Ts = np.meshgrid(xs, ts)
    Ys = np.sin(2 * np.pi * (Xs + Ts))
    block = amp.blocks.Line(Xs, Ys, axis=ax)
    timeline = amp.Timeline(ts, units='s', fps=20)
    anim = amp.Animation([block], timeline)
    anim.controls()
    anim.save_gif("sin_curve_with_contol")
    plt.show()


if __name__ == '__main__':
    main()

```
多分内部では `matplotlib.widgets.Slider` みたいなものを呼び出しているのでしょう。

[Slider Demo](https://matplotlib.org/gallery/widgets/slider_demo.html)

ちなみに、上記コードを実行するとプログレスバーに連動してグラフが動くグラフが画面に出力されます。Pauseボタンをクリックすると動作を止めることができ、さらに、青色のバーをドラッグすることでパラメータを変化させることができます。

こういった機能の実装を始めるとイベントの登録やそれに対するイベントハンドラを書かなくちゃいけないので、そういった手間を省けるのはとても嬉しいんじゃないかなと思います。

# 他には？

ギャラリーが用意されているのでそれらをみると良いです。

https://animatplot.readthedocs.io/en/latest/gallery.html

animatplotはまだできたばかりのライブラリみたいですが、アニメーションを作る第一歩としては良いライブラリな気がします。
こういったライブラリを使えば気軽にアニメーションを実行できて本質的な所に集中できそうですね。