15

電気生理学データ分析セットの場合、ポイントの大規模な 2D 配列 (約 20.000 x 120) をプロットする必要があります。以前は PyQt アプリケーションに Matplotlib ウィジェットを埋め込んでいましたが、プロットにかなりの時間がかかったため、他のソリューションを探していました。それでも、おそらく plot() 関数を使用するたびにウィジェットを再描画するため、pyqtgraph を使用してデータをプロットすると、予想よりもはるかに時間がかかります。

大きな配列をプロットするベスト プラクティスは何ですか?

pyqtgraph の例は、広範ではありますが、それ以上は役に立ちませんでした...

import pyqtgraph as pg
view = pg.GraphicsLayoutWidget()
w1 = view.addPlot()

for n in data:
    w1.plot(n)

また

w1.plot(data)

最後のルールはValueError を生成します: オペランドを形状と一緒にブロードキャストできませんでした (10) (10,120)

前もって感謝します....

4

1 に答える 1

41

このディスカッションを参照してください: https://groups.google.com/forum/?fromgroups#!searchin/pyqtgraph/arraytoqpath/pyqtgraph/CBLmhlKWnfo/jinNoI07OqkJ

Pyqtgraph は、plot() を呼び出すたびに再描画しません。再描画する前に、制御が Qt イベントループに戻るまで待機します。ただし、QApplication.processEvents() を呼び出すことにより、コードが強制的にイベント ループをより頻繁に訪問する可能性があります (これは、進行状況ダイアログがある場合など、間接的に発生する可能性があります)。

一般に、パフォーマンスを向上させるための最も重要なルールは、コードをプロファイリングすることです。代わりにそれを直接測定できる場合は、速度が低下している可能性があるものについて推測しないでください。

私はあなたのコードにアクセスできないので、それを改善する方法を推測し、プロファイリングがどのように役立つかを示すことしかできません. ここでは、「遅い」例から始めて、いくつかの改善を行います。

1. 遅い実装

import pyqtgraph as pg
import numpy as np
app = pg.mkQApp()
data = np.random.normal(size=(120,20000), scale=0.2) + \
       np.arange(120)[:,np.newaxis]
view = pg.GraphicsLayoutWidget()
view.show()
w1 = view.addPlot()
now = pg.ptime.time()
for n in data:
    w1.plot(n)
print "Plot time: %0.2f sec" % (pg.ptime.time()-now)
app.exec_()

これの出力は次のとおりです。

Plot time: 6.10 sec

それでは、プロファイリングしましょう:

$ python -m cProfile -s cumulative speed_test.py
. . .
     ncalls  tottime  percall  cumtime  percall filename:lineno(function)
          1    0.001    0.001   11.705   11.705 speed_test.py:1(<module>)
        120    0.002    0.000    8.973    0.075 PlotItem.py:614(plot)
        120    0.011    0.000    8.521    0.071 PlotItem.py:500(addItem) 
    363/362    0.030    0.000    7.982    0.022 ViewBox.py:559(updateAutoRange)
. . .

ViewBox.updateAutoRange がかなりの時間を取っていることがすでにわかっているので、オートレンジを無効にしましょう。

2.少し速い

import pyqtgraph as pg
import numpy as np
app = pg.mkQApp()
data = np.random.normal(size=(120,20000), scale=0.2) + \
       np.arange(120)[:,np.newaxis]
view = pg.GraphicsLayoutWidget()
view.show()
w1 = view.addPlot()
w1.disableAutoRange()
now = pg.ptime.time()
for n in data:
    w1.plot(n)
w1.autoRange() # only after plots are added
print "Plot time: %0.2f sec" % (pg.ptime.time()-now)
app.exec_()

..出力は次のとおりです。

Plot time: 0.68 sec

そのため、少し速くなりますが、プロットのパン/スケーリングは依然として非常に遅くなります。プロットをしばらくドラッグした後でプロファイルを見ると、次のようになります。

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.034    0.034   16.627   16.627 speed_test.py:1(<module>)
        1    1.575    1.575   11.627   11.627 {built-in method exec_}
       20    0.000    0.000    7.426    0.371 GraphicsView.py:147(paintEvent)
       20    0.124    0.006    7.425    0.371 {paintEvent}
     2145    0.076    0.000    6.996    0.003 PlotCurveItem.py:369(paint)

そのため、PlotCurveItem.paint() への呼び出しが多数見られます。ペイントの呼び出し回数を減らすために、120 のプロット ラインすべてを 1 つのアイテムにまとめたらどうなるでしょうか。

3. 迅速な実装

数ラウンドのプロファイリングの後、私はこれを思いつきました。上記のスレッドで提案されているように、pg.arrayToQPath の使用に基づいています。

import pyqtgraph as pg
import numpy as np
app = pg.mkQApp()

y = np.random.normal(size=(120,20000), scale=0.2) + np.arange(120)[:,np.newaxis]
x = np.empty((120,20000))
x[:] = np.arange(20000)[np.newaxis,:]
view = pg.GraphicsLayoutWidget()
view.show()
w1 = view.addPlot()

class MultiLine(pg.QtGui.QGraphicsPathItem):
    def __init__(self, x, y):
        """x and y are 2D arrays of shape (Nplots, Nsamples)"""
        connect = np.ones(x.shape, dtype=bool)
        connect[:,-1] = 0 # don't draw the segment between each trace
        self.path = pg.arrayToQPath(x.flatten(), y.flatten(), connect.flatten())
        pg.QtGui.QGraphicsPathItem.__init__(self, self.path)
        self.setPen(pg.mkPen('w'))
    def shape(self): # override because QGraphicsPathItem.shape is too expensive.
        return pg.QtGui.QGraphicsItem.shape(self)
    def boundingRect(self):
        return self.path.boundingRect()

now = pg.ptime.time()
lines = MultiLine(x, y)
w1.addItem(lines)
print "Plot time: %0.2f sec" % (pg.ptime.time()-now)

app.exec_()

それはすぐに始まり、パン/スケーリングは適度に反応します。ただし、このソリューションが機能するかどうかは、プログラムの詳細に依存する可能性が高いことを強調しておきます。

于 2013-06-14T12:25:22.650 に答える