20160504

MatPlotLibとPyQtGraphの描画速度の比較

新しいソフトでは、多点データ(約1Mプロット位〜)のプロットやスペクトルのデータを取り扱う可能性がある。
画像データを一列の配列データに直して、FFTして、スペクトルを表示するから。
画像データ→ピクセルデータ(m行 n列)→配列データ(1行 m*n列)→abs(fft(配列データ))みたいな。

一昔前の16:9ディスプレイが1920*1080のサイズだった。
(この記事を書いている2011年製のPCが、そのサイズなので、一昔と書いたが、今でも標準かもしれない。ただ、最近使わせてもらったmacのディスプレイがこれより大きくてきれいだったので、今の標準はそっちなのかなぁと言う意味での一昔前)
これに目一杯表示するとデータサイズが、

1920*1080 = 2,073,600 ≒ 2 [M pixels]になる。

そういった作図に関しては、Pythonで作図するための定番モジュール"MatPlotlib"ではなく、"PyQtGraph"を使用する事にしている。
"FIR and FFT"のプログラムでも使ったから分かるけど、早いし。
pyqtgraphは、使い方がよく分からないっていう、最大の難点があるけどそんな事ない。
この記事を読んでもらえば、「・・・PyQtGraph使えないこともない。多分。」っていう状態になれると思う。
上記記事にも書いたが、大体↓このコマンド実行したら、pyqtgraphを使い始められるんじゃないかって程、色々出てくる。
しかもコード付きで。

import pyqtgraph.examples
pyqtgraph.examples.run()


MatplotlibとPyQtGraphを比較します。

・単純に同じグラフを描画する。
この描画は、(それぞれのモジュールのやり方で)各GraphicsItemの準備〜QGraphicsviewへ入れることでGUI上へ表示までとする。(モジュールのインポート等は、含まない。)
matplotlibとpysideの組み合わせが難しかったので、まだ試せなかった。その内挑戦する。

・時間は、こんな感じで表示させた。
#import other_modules
import time
time_1 = time.clock()
'''
code
'''
time_2 = time.clock()
print(str(time_2 - time_1)+" seconds")

こうすると、codeの部分にかかった時間が、端末上に表示されるので、メモる
codeの部分などに、いろいろ入れるという。
もちろん、QtDesignerで書いたコードなどは含めない。

ちなみに、どうなるのかというと。
>>> import time
>>> time_1 = time.clock()
>>> '''
... code
... '''
'\ncode\n'
>>> time_2 = time.clock()
>>> print(str(time_2 - time_1)+" seconds")
0.001606 seconds
となった。

繰り返している内に早くなってくることがあるので、for構文で何度もやった。
時間を自動的にメモするために、この様な感じで書いた。
a = open("pyqtgraph1.txt","wr")
a.write("time, mean time\n")
for i in range(100)
#省略
a.write(str(time_2 - time_1) + ", " + str(b/(i+1))+"\n")
#省略
a.close()





書いていて、途中で面倒くさくなったので、コードだけ書く。
注意
やれば分かると思うけど、matplotlibは、show()する度に消さないといけない。
pyqtgraphは、文字通り100窓になる。(終わったら自動的に消える。)
20160504 pyqtgraph time 01.png

初めはmatplotlib
import time
import matplotlib.pyplot as plt
import pyqtgraph as pg
import numpy as np


x = np.linspace(0,1,1000001)
y = np.sin(np.linspace(0,4*np.pi,1000001))

a = open("matplotlib1.txt","wr")
a.write("time, mean time\n")
b = 0

for i in range(100):

time_1 = time.clock()
plt.plot(x,y)
plt.show()
time_2 = time.clock()

b = b + time_2 - time_1
a.write(str(time_2 - time_1) + ", " + str(b/(i+1))+"\n")

a.close()



次いで、pyqtgraph
import time
import matplotlib.pyplot as plt
import pyqtgraph as pg
import numpy as np


x = np.linspace(0,1,1000001)
y = np.sin(np.linspace(0,4*np.pi,1000001))


a = open("pyqtgraph1.txt","wr")
a.write("time, mean time\n")
b = 0

for i in range(100):

time_1 = time.clock()
c=pg.plot(x,y)
time_2 = time.clock()
c.close()

b = b + time_2 - time_1
a.write(str(time_2 - time_1) + ", " + str(b/(i+1))+"\n")

a.close()

結果
2周期分のsinカーブ100万1点
・matplotlib 0.22861378秒(100個やった平均)
・pyqtgraph 0.03074252秒(同上)

0.22861378/0.03074252
≒7
7倍早い。

pyqtgraphは、100回中一回目の描画は、0.13073秒もかかっている。
10回くらい描けば、後はそんなに変わらず0.03秒前後で終わる。

あれぇ…こんなはずじゃなかったんだけど。
もっと劇的な違いが出るはずだった。
前やったときは、ホワイトノイズがかなり多いデータを描かせていたせいかも。

y = np.sin(np.linspace(0,4*np.pi,1000001))となっているけど、こんなゆったりとしたデータじゃなくて、もっと激しく上下させてみたら、変わるのではないか。
そう思って
y = np.sin(np.linspace(0,10000*np.pi,1000001))にして、再度挑戦。

20160504 pyqtgraph time 02.png
一万周期を1000ピクセル程度のグラフ幅で収めようとすると、もう何も見えない。


1万周期分のsinカーブ100万1点
matplotlibpyqtgraph
2.19913201124秒(90回やった平均。事情があって途中で止めた。)0.02954327(100回やった平均)

2.19913201124/0.02954327
≒70
やりましたよ。
pyqtgraphはmatplotlibの70倍早いこともある。
もう少し複雑なら、さらに差が出るかもしれない。
(20160521修正)
タイトルとかが間違っていたので直した。
書いている最中に、やりたい内容が変わってきてしまう事が原因。
書く前に、何を書くのかはっきり決めていないのがそのそもの原因。
改めるつもりはない。
posted by yuchan at 14:00 | Comment(1) | python