在 pyqtgraph 中绘制大数组

Posted

技术标签:

【中文标题】在 pyqtgraph 中绘制大数组【英文标题】:Plotting large arrays in pyqtgraph 【发布时间】:2013-06-10 19:41:08 【问题描述】:

对于电生理数据分析集,我需要绘制一个大型二维阵列(暗淡约 20.000 x 120)点。我曾经在我的 PyQt 应用程序中嵌入一个 Matplotlib 小部件,但由于绘图花费了很长时间,我开始寻找其他解决方案。尽管如此,使用 pyqtgraph 绘制数据也需要比预期更长的时间,可能是因为它每次使用 plot() 函数时都会重绘小部件。

绘制大型数组的最佳做法是什么?

pyqtgraph 示例虽然内容广泛,但对我的帮助并没有太大...

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

for n in data:
    w1.plot(n)

w1.plot(data)

最后一条规则产生ValueError:operands could not be broadcast together with shapes (10) (10,120)

提前谢谢....

【问题讨论】:

您认为“相当长”的时间有多少?我很容易在一秒钟内绘制 20.000 x 120 点。对于没有问题的快照。例如,我想展示 128 导联实时心电图,这还不够。 即使我将数据减少到 200x120 点,也需要 6 秒。你使用相同的代码吗? 鉴于您还没有确切地告诉我“数据”是什么样的,我不确定。我使用了一个 numpy.empty ([120, 20000], dtype=numpy.int16) 数组。今晚可以发布代码。 【参考方案1】:

查看此讨论: 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 条情节线放在一个项目中以减少绘制调用的次数会怎样?

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

它启动很快,并且平移/缩放响应合理。不过,我要强调的是,此解决方案是否适合您可能取决于您的程序的细节。

【讨论】:

感谢您的广泛回答,我会将最后一个算法实现到程序中并试一试。作为奖励,我还学到了一些关于分析应用程序的知识:) 感谢您的回答。我一直在尝试在最短的时间内顺序读取和绘制多个数据集。我使用“lines.setPath”添加了一个简单的循环,效果很好。但是,我无法正确实现“RemoteGraphicsView”类以进一步加快绘图速度。可以举个例子吗?

以上是关于在 pyqtgraph 中绘制大数组的主要内容,如果未能解决你的问题,请参考以下文章

如何在 pyqtgraph 远程视图上绘制切片的 numpy 数据数组

Python 3.3 pyqtgraph 无法绘制点

用PyQtGraph绘制可视化数据图表

pyqtgraph 用于绘制多个数据列表

PyQtGraph:问题循环通过绘制数据切片

使用PyQtGraph绘制图形