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

查看:117
本文介绍了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会导致严重的内存泄漏,但我可以不遵守.

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和不使用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
  • 带blit的175 fps
  • 28 fps when run without blit
  • 175 fps with 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:\n {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一样完整/成熟,但运行速度要快得多."


Update:
For faster plotting, one may consider using pyqtgraph.
As the pyqtgraph documentation puts it: "For plotting, pyqtgraph is not nearly as complete/mature as matplotlib, but runs much faster."

我将上面的示例移植到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(不带斑点):28 fps
  • matplotlib(带有斑点):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天全站免登陆