python - Plotting large arrays in pyqtgraph -


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