Matplotlib/PyPlot 中的快速实时绘图 [英] Fast Live Plotting in Matplotlib / PyPlot

查看:129
本文介绍了Matplotlib/PyPlot 中的快速实时绘图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

多年来,我一直在努力在 matplotlib 中实现高效的实时绘图,直到今天我仍然不满意.

For years, I've been struggling to get efficient live plotting in matplotlib, and to this day I remain unsatisfied.

我想要一个 redraw_figure 函数来更新图形实时"(在代码运行时),并且如果我停在断点处将显示最新的图.

I want a redraw_figure function that updates the figure "live" (as the code runs), and will display the latest plots if I stop at a breakpoint.

这是一些演示代码:

import time
from matplotlib import pyplot as plt
import numpy as np

def live_update_demo():

    plt.subplot(2, 1, 1)
    h1 = plt.imshow(np.random.randn(30, 30))
    redraw_figure()
    plt.subplot(2, 1, 2)
    h2, = plt.plot(np.random.randn(50))
    redraw_figure()

    t_start = time.time()
    for i in xrange(1000):
        h1.set_data(np.random.randn(30, 30))
        redraw_figure()
        h2.set_ydata(np.random.randn(50))
        redraw_figure()
        print 'Mean Frame Rate: %.3gFPS' % ((i+1) / (time.time() - t_start))

def redraw_figure():
    plt.draw()
    plt.pause(0.00001)

live_update_demo()

绘图应该在代码运行时实时更新,并且在 redraw_figure() 之后的任何断点处停止时,我们应该看到最新的数据.问题是如何最好地实现 redraw_figure()

Plots should update live when the code is run, and we should see the latest data when stopping at any breakpoint after redraw_figure(). The question is how to best implement redraw_figure()

在上面的实现中 (plt.draw(); plt.pause(0.00001)),它可以工作,但是很慢 (~3.7FPS)

In the implementation above (plt.draw(); plt.pause(0.00001)), it works, but is very slow (~3.7FPS)

我可以将其实现为:

def redraw_figure():
    plt.gcf().canvas.flush_events()
    plt.show(block=False)

它运行得更快(~11FPS),但是当你在断点处停止时,绘图不是最新的(例如,如果我在 t_start = ... 行上放置一个断点,第二个情节没有出现).

And it runs faster (~11FPS), but plots are not up-to date when you stop at breakpoints (eg if I put a breakpoint on the t_start = ... line, the second plot does not appear).

奇怪的是,真正起作用的是两次调用该节目:

Strangely enough, what does actually work is calling the show twice:

def redraw_figure():
    plt.gcf().canvas.flush_events()
    plt.show(block=False)
    plt.show(block=False)

它提供了大约 11FPS,并且如果您在任何一条线上中断,都可以使绘图保持最新状态.

Which gives ~11FPS and does keep plots up-to-data if your break on any line.

现在我听说block"关键字已被弃用.并且两次调用同一个函数似乎是一个奇怪的,可能是不可移植的黑客攻击.

Now I've heard it said that the "block" keyword is deprecated. And calling the same function twice seems like a weird, probably-non-portable hack anyway.

那么我可以在这个函数中放入什么,以合理的帧速率绘图,不是一个巨大的杂物,并且最好跨后端和系统工作?

So what can I put in this function that will plot at a reasonable frame rate, isn't a giant kludge, and preferably will work across backends and systems?

一些注意事项:

  • 我在 OSX 上,并使用 TkAgg 后端,但欢迎任何后端/系统上的解决方案
  • 开启"交互模式将不起作用,因为它不会实时更新.当解释器等待用户输入时,它只会在 Python 控制台中更新.
  • 博客 建议实施:

  • I'm on OSX, and using TkAgg backend, but solutions on any backend/system are welcome
  • Interactive mode "On" will not work, because it does not update live. It just updates when in the Python console when the interpreter waits for user input.
  • A blog suggested the implementation:

def redraw_figure():无花果 = plt.gcf()fig.canvas.draw()fig.canvas.flush_events()

def redraw_figure(): fig = plt.gcf() fig.canvas.draw() fig.canvas.flush_events()

但至少在我的系统上,它根本不会重绘绘图.

But at least on my system, that does not redraw the plots at all.

所以,如果有人有答案,你会直接让我和其他成千上万的人高兴.他们的幸福可能会传递给他们的朋友和亲戚,以及他们的朋友和亲戚,等等,这样您就有可能改善数十亿人的生活.

ImportanceOfBeingErnest 展示了如何使用 blit 来加快绘图速度,但这并不像在 redraw_figure 函数中添加一些不同的东西那么简单(您需要跟踪要重绘的内容).

ImportanceOfBeingErnest shows how you can use blit for faster plotting, but it's not as simple as putting something different in the redraw_figure function (you need to keep track of what things to redraw).

推荐答案

首先,问题中发布的代码在我的机器上以 7 fps 的速度运行,以 QT4Agg 作为后端.

First of all, the code that is posted in the question runs with 7 fps on my machine, with QT4Agg as backend.

现在,正如许多帖子中所建议的,例如此处此处,使用 blit 可能是一种选择.尽管这篇文章 提到 blit 会导致强烈的内存泄漏,但我可以没有观察到.

Now, as has been suggested in many posts, like here or here, using blit might be an option. Although this article mentions that blit causes strong memory leakage, I could not observe that.

我稍微修改了您的代码,并比较了使用和不使用 blit 的帧速率.下面的代码给出了

I have modified your code a bit and compared the frame rate with and without the use of blit. The code below gives

  • 在没有 blit 的情况下运行时为 28 fps
  • 175 fps 和 blit

代码:

import time
from matplotlib import pyplot as plt
import numpy as np


def live_update_demo(blit = False):
    x = np.linspace(0,50., num=100)
    X,Y = np.meshgrid(x,x)
    fig = plt.figure()
    ax1 = fig.add_subplot(2, 1, 1)
    ax2 = fig.add_subplot(2, 1, 2)

    img = ax1.imshow(X, vmin=-1, vmax=1, interpolation="None", cmap="RdBu")


    line, = ax2.plot([], lw=3)
    text = ax2.text(0.8,0.5, "")

    ax2.set_xlim(x.min(), x.max())
    ax2.set_ylim([-1.1, 1.1])

    fig.canvas.draw()   # note that the first draw comes before setting data 


    if blit:
        # cache the background
        axbackground = fig.canvas.copy_from_bbox(ax1.bbox)
        ax2background = fig.canvas.copy_from_bbox(ax2.bbox)

    plt.show(block=False)


    t_start = time.time()
    k=0.

    for i in np.arange(1000):
        img.set_data(np.sin(X/3.+k)*np.cos(Y/3.+k))
        line.set_data(x, np.sin(x/3.+k))
        tx = 'Mean Frame Rate:
 {fps:.3f}FPS'.format(fps= ((i+1) / (time.time() - t_start)) ) 
        text.set_text(tx)
        #print tx
        k+=0.11
        if blit:
            # restore background
            fig.canvas.restore_region(axbackground)
            fig.canvas.restore_region(ax2background)

            # redraw just the points
            ax1.draw_artist(img)
            ax2.draw_artist(line)
            ax2.draw_artist(text)

            # fill in the axes rectangle
            fig.canvas.blit(ax1.bbox)
            fig.canvas.blit(ax2.bbox)

            # in this post http://bastibe.de/2013-05-30-speeding-up-matplotlib.html
            # it is mentionned that blit causes strong memory leakage. 
            # however, I did not observe that.

        else:
            # redraw everything
            fig.canvas.draw()

        fig.canvas.flush_events()
        #alternatively you could use
        #plt.pause(0.000000000001) 
        # however plt.pause calls canvas.draw(), as can be read here:
        #http://bastibe.de/2013-05-30-speeding-up-matplotlib.html


live_update_demo(True)   # 175 fps
#live_update_demo(False) # 28 fps

<小时>

更新:
为了更快地绘图,可以考虑使用 pyqtgraph.
正如 pyqtgraph 文档 所说:对于绘图,pyqtgraph 并不像与 matplotlib 一样完整/成熟,但运行速度要快得多."

我将上面的例子移植到 pyqtgraph.虽然它看起来有点难看,但它在我的机器上以 250 fps 的速度运行.

I ported the above example to pyqtgraph. And although it looks kind of ugly, it runs with 250 fps on my machine.

总结一下,

  • matplotlib(无 blitting):28 fps
  • matplotlib(带 blitting):175 fps
  • pyqtgraph:250 fps

pyqtgraph 代码:

pyqtgraph code:

import sys
import time
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
import pyqtgraph as pg


class App(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(App, self).__init__(parent)

        #### Create Gui Elements ###########
        self.mainbox = QtGui.QWidget()
        self.setCentralWidget(self.mainbox)
        self.mainbox.setLayout(QtGui.QVBoxLayout())

        self.canvas = pg.GraphicsLayoutWidget()
        self.mainbox.layout().addWidget(self.canvas)

        self.label = QtGui.QLabel()
        self.mainbox.layout().addWidget(self.label)

        self.view = self.canvas.addViewBox()
        self.view.setAspectLocked(True)
        self.view.setRange(QtCore.QRectF(0,0, 100, 100))

        #  image plot
        self.img = pg.ImageItem(border='w')
        self.view.addItem(self.img)

        self.canvas.nextRow()
        #  line plot
        self.otherplot = self.canvas.addPlot()
        self.h2 = self.otherplot.plot(pen='y')


        #### Set Data  #####################

        self.x = np.linspace(0,50., num=100)
        self.X,self.Y = np.meshgrid(self.x,self.x)

        self.counter = 0
        self.fps = 0.
        self.lastupdate = time.time()

        #### Start  #####################
        self._update()

    def _update(self):

        self.data = np.sin(self.X/3.+self.counter/9.)*np.cos(self.Y/3.+self.counter/9.)
        self.ydata = np.sin(self.x/3.+ self.counter/9.)

        self.img.setImage(self.data)
        self.h2.setData(self.ydata)

        now = time.time()
        dt = (now-self.lastupdate)
        if dt <= 0:
            dt = 0.000000000001
        fps2 = 1.0 / dt
        self.lastupdate = now
        self.fps = self.fps * 0.9 + fps2 * 0.1
        tx = 'Mean Frame Rate:  {fps:.3f} FPS'.format(fps=self.fps )
        self.label.setText(tx)
        QtCore.QTimer.singleShot(1, self._update)
        self.counter += 1


if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)
    thisapp = App()
    thisapp.show()
    sys.exit(app.exec_())

这篇关于Matplotlib/PyPlot 中的快速实时绘图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆