【Python】matplotlibの手動で描画更新
matplotlibでアニメーションほど早い更新周期ではないものの,
イベント(たとえばグラフをダブルクリック)でグラフを更新(変化)させたい場合など,
グラフ更新の関数を実行してあげる必要がある。
最も一般的なのは
canvas.draw()
しかしこれがすごく遅い。
Figureクラスの中の関数のためかfigure全体を更新してしまうためらしい。
色々試した結果,
① bg = canvas.copy_from_bbox(self.ax.bbox) # bboxのエリアを背景(というか戻したい状態)として保存
② canvas.restore_region(bg) # 戻したい状態へ描画
③ ax.draw_artist(artist) # 描画したいartist(LineやPatch)を登録
④ canvas.blit(ax.bbox) # bboxのエリアだけ描画更新 以降②へ戻る
※ ④ canvas.blitよりcanvas.update()の方がメモリ使用が少なく早い情報もあるが
ケースによるのとqt4aggでしか動かなかった。
でやると更新したい場所のみ変更するのでmatplotlibとは思えないほど早い。
bboxのエリアを前に保存した状態に戻す手順とその効果はペイントみたいに感じる。
参考にしたサイトにあったcanvas.draw()と他のもっと早い手順との速度比較を試してみた。
import matplotlib.pyplot as plt import numpy as np import time from scipy.stats._continuous_distns import t_gen def case1(): fig, ax = plt.subplots() t_start = time.time() num_plot = 0 while time.time() - t_start < 1: ax.clear() ax.plot(np.random.randn(100)) plt.pause(0.001) num_plot += 1 return num_plot def case2(): fig, ax = plt.subplots() line, = ax.plot(np.random.randn(100)) t_start = time.time() num_plot = 0 while time.time() - t_start < 1: line.set_ydata(np.random.randn(100)) plt.pause(0.001) num_plot += 1 return num_plot def case3(): fig, ax = plt.subplots() line, = ax.plot(np.random.randn(100)) fig.canvas.draw() fig.show() t_start = time.time() num_plot = 0 while time.time() - t_start < 1: line.set_ydata(np.random.randn(100)) fig.canvas.draw() fig.canvas.flush_events() # <-これがないと画面に描画されない。 num_plot += 1 return num_plot def case4(): fig, ax = plt.subplots() line, = ax.plot(np.random.randn(100)) fig.canvas.draw() fig.show() t_start = time.time() num_plot = 0 while time.time() - t_start < 1: line.set_ydata(np.random.randn(100)) ax.draw_artist(ax.patch) ax.draw_artist(line) #fig.canvas.update() fig.canvas.blit(ax.bbox) fig.canvas.flush_events() num_plot += 1 return num_plot def case5(): fig, ax = plt.subplots() fig.canvas.draw() bg = fig.canvas.copy_from_bbox(ax.bbox) line, = ax.plot(np.random.randn(100)) fig.show() t_start = time.time() num_plot = 0 while time.time() - t_start < 1: line.set_ydata(np.random.randn(100)) fig.canvas.restore_region(bg) ax.draw_artist(line) #fig.canvas.update() fig.canvas.blit(ax.bbox) fig.canvas.flush_events() num_plot += 1 return num_plot def case6(): fig, ax = plt.subplots() fig.canvas.draw() bg = fig.canvas.copy_from_bbox(ax.bbox) line, = ax.plot(np.random.randn(100)) fig.show() t_start = time.time() num_plot = 0 while time.time() - t_start < 1: line.set_ydata(np.random.randn(100)) fig.canvas.restore_region(bg) ax.draw_artist(line) fig.canvas.update() fig.canvas.flush_events() num_plot += 1 return num_plot if __name__ == "__main__": print("case1: " + str(case1()) + "fps") print("case1: " + str(case2()) + "fps") print("case3: " + str(case3()) + "fps") print("case4: " + str(case4()) + "fps") print("case5: " + str(case5()) + "fps") print("case6: " + str(case6()) + "fps")
Case1: 最も原始的。Lineを書いては消して。Pauseを使うことで画面を更新。
Case2: Case1からLineをいちいち消すのではなくLine1のデータを書き換えて更新
Case3: Case2 + canvas.draw() これが基準になる
Case4: 背景を白で描画する(ax.draw_artist(ax.patch))ことで前の描画をクリア。再描画はcanvas.blit()を使う
Case5: Case4から前描画クリアをcanvas.restore_region()に変更
Case6: Case5から再描画をcanvas.update()に変更
いろんなケースを試したけど要は
・Case3基準に対してCase5がどれだけ速いか。
・Case5とCase6どっちが速いか。
結果は
case1: 5fps
case1: 7fps
case3: 44fps
case4: 227fps
case5: 218fps
case6: 183fps
Case3に対してCase5は約5倍速い。
Case5よりCase6の方が約1.2倍速い結果となった。
意外にCase4が遅くない。。