2013-06-14 3 views
9

전기 생리학 데이터 분석 세트의 경우 큰 2D 배열 (약 20,000 x 120)을 플롯해야합니다. 필자는 PyQt 응용 프로그램에 Matplotlib 위젯을 포함 시켰지 만 플롯팅이 상당히 오래 걸렸기 때문에 다른 솔루션을 찾고있었습니다. 여전히 pyqtgraph로 데이터를 플로팅하는 것은 plot() 함수를 사용할 때마다 매번 위젯을 다시 그리기 때문에 예상보다 훨씬 오래 걸립니다.pyqtgraph의 큰 배열 플로팅

큰 배열을 플로트하는 가장 좋은 방법은 무엇입니까? 피연산자 함께 방송 할 수 없습니다

pyqtgraph 예

, 광범위한하지만, 마지막 규칙은 ValueError를 생성 ... 훨씬 더

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

for n in data: 
    w1.plot(n) 

또는

w1.plot(data) 

을 나에게 도움이되지 않았다 모양 (10) (10,120)

미리 감사드립니다 ....

+0

"꽤 오래"이라고 생각하십니까? 저는 쉽게 초당 20.000 x 120 포인트를 그립니다. 문제가되지 않는 스냅 샷의 경우. 예를 들어 128 리드 라이브 ECG를 보여주고 싶다면 충분하지 않습니다. – Micke

+0

데이터를 200x120 포인트로 축소해도 6 초가 소요됩니다. 같은 코드를 사용합니까? –

+0

정확히 '데이터'가 어떤 것인지 정확히 알려주지 않았다면 확실하지 않습니다. numpy.empty ([120, 20000], dtype = numpy.int16) 배열을 사용했습니다. 오늘 밤에 코드를 게시 할 수 있습니다. – Micke

답변

25

이 토론을 참조하십시오 : 모든 호출이 음모 후 https://groups.google.com/forum/?fromgroups#!searchin/pyqtgraph/arraytoqpath/pyqtgraph/CBLmhlKWnfo/jinNoI07OqkJ

Pyqtgraph이 (다시 그리기하지 않습니다) 다시 그리기 전에 컨트롤이 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) 
. . . 

이미 우리가 뷰 박스 것을 볼 수 있습니다. 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 개의 모든 플롯 선을 단일 항목에 넣으면 어떨까요?

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_() 

그것은 빨리 시작하고 패닝/스케일링 합리적으로 반응한다. 하지만이 솔루션이 당신에게 효과가 있는지 여부는 프로그램의 세부 사항에 달려 있다고 강조 할 것입니다.

+1

광범위한 답변 주셔서 감사합니다. 마지막 알고리즘을 프로그램에 구현하고 시도해 보겠습니다. 보너스로, 나는 또한 프로파일 링 애플 리케이션에 대해서 뭔가를 배웠다 :) –

+0

이 답변을 주셔서 감사합니다. 나는 가장 짧은 시간에 여러 개의 데이터 세트를 순차적으로 읽고 쓰려고 시도했다. 나는 잘 작동하는 'lines.setPath'를 사용하여 사소한 루프를 추가했다. 그러나 'RemoteGraphicsView'클래스를 올바르게 구현하여 플로팅 속도를 향상시킬 수 없습니다. 예제를 추가 할 수 있습니까? – bejota