モチベーション
センサーとかの値を確認したり,機械学習の学習状況とかそんなんをリアルタイムにグラフにプロットしながら見たいときがある.
pythonにはmatplotlib
っていう便利なグラフ作成ライブラリがあるのでこれを使いたい.
既知の問題とか
普通matplotlib
でグラフを作るときは
import matplotlib.pyplot as plt
"""
plotしたいデータを計算する処理
...
"""
plt.plot(data)
plt.show()
とかやる.
だけど,show()
を呼ぶとプログラムがそこでブロックされてリアルタイムな描画ができない.
調べてみるとshow()
ではなくてdraw()
を使おう!みたいな記事とか,Tkinter(tkinter)
とかQt
とかのGUIライブラリと組み合わせた方法とかがでてくるんだけど,Windows環境だとグラフに一度でも触るとGUIが固まったりしてとてもじゃないけど使えなかったりした.
(たしかMacとかのUnix環境だったら使えたような気がする.)
pause()を使おう!!!
例の如くstackoverflowにそれ関係のやりとりがあった.
参考: real-time plotting in while loop with matplotlib
結論はplt.show()
の代わりにplt.pause(interval)
を使うということ.
下に簡単なsin関数を永遠にplotする例を載っける.
注意点などはコメントに書いたので,読んでください.
# -*- coding: utf-8 -*-
"""
matplotlibでリアルタイムプロットする例
無限にsin関数をplotし続ける
"""
from __future__ import unicode_literals, print_function
import numpy as np
import matplotlib.pyplot as plt
def pause_plot():
fig, ax = plt.subplots(1, 1)
x = np.arange(-np.pi, np.pi, 0.1)
y = np.sin(x)
# 初期化的に一度plotしなければならない
# そのときplotしたオブジェクトを受け取る受け取る必要がある.
# listが返ってくるので,注意
lines, = ax.plot(x, y)
# ここから無限にplotする
while True:
# plotデータの更新
x += 0.1
y = np.sin(x)
# 描画データを更新するときにplot関数を使うと
# lineオブジェクトが都度増えてしまうので,注意.
#
# 一番楽なのは上記で受け取ったlinesに対して
# set_data()メソッドで描画データを更新する方法.
lines.set_data(x, y)
# set_data()を使うと軸とかは自動設定されないっぽいので,
# 今回の例だとあっという間にsinカーブが描画範囲からいなくなる.
# そのためx軸の範囲は適宜修正してやる必要がある.
ax.set_xlim((x.min(), x.max()))
# 一番のポイント
# - plt.show() ブロッキングされてリアルタイムに描写できない
# - plt.ion() + plt.draw() グラフウインドウが固まってプログラムが止まるから使えない
# ----> plt.pause(interval) これを使う!!! 引数はsleep時間
plt.pause(.01)
if __name__ == "__main__":
pause_plot()
これで色々捗る!!!
おまけ
上で載っけたstackoverflowにdrawnow
というパッケージを使った方法も載っているので紹介する.
drawnow
はpip
で入るので
$ pip install drawnow
でOK.
使い方
# -*- coding: utf-8 -*-
"""
matplotlibでリアルタイムプロットする例
無限にsin関数をplotし続ける
"""
from __future__ import unicode_literals, print_function
import numpy as np
import matplotlib.pyplot as plt
from drawnow import drawnow, figure
def drawnow_plot():
# drawnowパッケージに含まれているfigure関数を使って
# figureオブジェクトを作る
fig = figure()
x = np.arange(-np.pi, np.pi, .1)
y = np.sin(x)
def draw():
"""
drawnow関数に渡すplotに必要な処理を記述
plotに必要な変数はこの関数がアクセスできるスコープの変数にしておく
"""
plt.plot(x, y)
while True:
x += .1
y = np.sin(x)
drawnow(draw)
...書いてからソースコード見てみたが,drawnow
は結局中でdraw()
を呼んでるみたいで,Windowsでやると上手く行かなかった(GUIが固まる現象)...
結論
リアルタイムにプロットしたいときはplt.pause()
を使おう!!!