for electrophysiology data analysis set need plot large 2d array (dim approx 20.000 x 120) of points. used embed matplotlib widget in pyqt application, went looking other solutions because plotting took quite long. still, plotting data pyqtgraph takes longer expected, because redraws widget everytime when using plot() function.
what best practice plot large arrays?
the pyqtgraph examples, although extensive, didn't me further...
import pyqtgraph pg view = pg.graphicslayoutwidget() w1 = view.addplot() n in data: w1.plot(n)
or
w1.plot(data)
the last rule generates valueerror: operands not broadcast shapes (10) (10,120)
thanks in advance....
see discussion: https://groups.google.com/forum/?fromgroups#!searchin/pyqtgraph/arraytoqpath/pyqtgraph/cblmhlkwnfo/jinnoi07oqkj
pyqtgraph not redraw after every call plot(); wait until control returns qt event loop before redrawing. however, possible code forces event loop visited more calling qapplication.processevents() (this can happen indirectly e.g. if have progress dialog).
generally, important rule improving performance is: profile code. not make assumptions might slowing down if can instead measure directly.
since don't have access code, can guess how improve , show how profiling can help. i'm going start 'slow' example here , work through few improvements.
1. slow implementation
import pyqtgraph pg import numpy 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() = pg.ptime.time() n in data: w1.plot(n) print "plot time: %0.2f sec" % (pg.ptime.time()-now) app.exec_()
the output of is:
plot time: 6.10 sec
now let's profile it:
$ 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) . . .
already can see viewbox.updateautorange taking lot of time, let's disable auto-ranging:
2. bit faster
import pyqtgraph pg import numpy 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() = pg.ptime.time() n in data: w1.plot(n) w1.autorange() # after plots added print "plot time: %0.2f sec" % (pg.ptime.time()-now) app.exec_()
..and output is:
plot time: 0.68 sec
so that's bit faster, panning/scaling plot still quite slow. if @ profile after dragging plot while, looks this:
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)
so see lot of calls plotcurveitem.paint(). if put 120 plot lines single item reduce number of paint calls?
3. fast implementation
after couple rounds of profiling, came this. it's based on using pg.arraytoqpath, suggested in thread above:
import pyqtgraph pg import numpy 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 , y 2d arrays of shape (nplots, nsamples)""" connect = np.ones(x.shape, dtype=bool) connect[:,-1] = 0 # don't draw 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 expensive. return pg.qtgui.qgraphicsitem.shape(self) def boundingrect(self): return self.path.boundingrect() = pg.ptime.time() lines = multiline(x, y) w1.additem(lines) print "plot time: %0.2f sec" % (pg.ptime.time()-now) app.exec_()
it starts , panning/scaling reasonably responsive. i'll stress, though, whether solution works depend on details of program.
Comments
Post a Comment