以下、 jupyter notebook を notebook と表記します。
TL;DR
notebook で matplotlib の描画coroutineを非同期に回してアニメーションする。
(下のgifはfpsすごい出ている感がありますが、こんなんじゃないです…)
モチベーション
-
matplotlibのアニメーションの使い方を思い出すのが、面倒。 - 描画の動きを大雑把に確認したいだけなので、フレームレートは求めない。(私の環境ではfps10くらいでCPUリソースがかわいそうになるくらい)
- フレームレートを求めるなら、このブログ記事などをご参考に。
- ファイル等に書き出すなら、matplotlibのアニメーション機能を使うべき。
- ブロッキングな処理(
while True: sleep)をやると、kernelの処理がブロックされてしまうので、おそらく描画されない。
環境
-
python- 3.6.5
- 2.7.15 (「python2は小学生にもバカにされる」は割とマジ)
- ライブラリ
pip install jupyter numpy matplotlib
notebook は tornado
notebook のコアは 非同期処理ウェブフレームワークの tornado
→ ここで tornado の ioloop が起動されている。
描画coroutineをioloopに処理させる
add_callback版
※以下、 notebook の .ipynb です。
%matplotlib notebook
import matplotlib.pyplot as plt
import numpy as np
from tornado.ioloop import IOLoop
from tornado import gen
@gen.coroutine
def coro_anim():
fig, ax = plt.subplots()
x = np.arange(0, 2 * np.pi, 0.01)
f = lambda x: np.sin(3 * x)
line, = ax.plot(x, f(x))
while True:
x += 0.1
line.set_data(x, f(x))
ax.set_xlim((min(x), max(x)))
plt.show()
yield gen.sleep(1.0)
IOLoop.current().add_callback(coro_anim)
一見、良さそうに見えますが、何回かセルを走らせていると、coroutineが複数走って、CPUリソースを食っていきます。
再実行時に、 Kernel Restart しても良いと思いますが、ここでは少し粘って、他に使えそうなのを探ります。
PeriodicCallback版
とは言っても、ioloopに入れたcoroutineのkillの仕方が分からなかったので、
初期描画処理をcoroutineの外に出して、 再描画処理だけをcoroutineのまま残して、
PeriodicCallback で interval=1000ms で回しました。
以下も、 .ipynb です。
%matplotlib notebook
import matplotlib.pyplot as plt
import numpy as np
from tornado.ioloop import PeriodicCallback
from tornado import gen
cb = None
fig, ax = plt.subplots()
x = np.arange(0, 2 * np.pi, 0.01)
f = lambda x: np.sin(3 * x)
line, = ax.plot(x, f(x))
@gen.coroutine
def coro_anim():
global x
x += 0.1
line.set_data(x, f(x))
ax.set_xlim((min(x), max(x)))
plt.show()
if cb and cb.is_running():
cb.stop()
cb = PeriodicCallback(coro_anim, 1000)
cb.start()
これでセルを何回走らせたとしても、coroutineを stop してからインスタンスし直しているので、複数のcoroutineが走ってCPUリソースを食いつぶすことはなさそうです。
(実のところ、 Run Cells を魂を込めて連打すると、coroutineが複数できます。)
最悪、coroutineが複数できてしまっても、Kernel Restart で、その kernel で回っているioloopを初期化できるので、問題はなさそうです。
coroutineが複数走っているかの判断
notebook のノートの右上の方に python バージョンが出ているのですが、
その横に ○ があります。 coroutine が処理されているときはここが ● になるので、
これがcoroutineのインターバルより頻繁に点滅していれば、coroutineが複数走っている可能性があります。
描画の焼付きみたいなもの
描画させながらいじってると、下図のような焼付きみたいなものがたまに出ますが、グラフの右下のところをドラッグしてグラフサイズを変更すると、
描画しなおしてくれるので、coroutineを再起動しなくても直せます。
asyncioでの動作
私は標準ライブラリの asycnio の方が馴染みがあり、先に asyncio で書いて動作確認しているので、
asyncio でも書けば普通に動きます。もちろん、 python のバージョンに対してのコード依存性が出てきます。
今回は、 event_loop のことを書き始めると、本題からズレていきそうなので、tornado ということもあり、こちらにしました。
