Hoe plot ik in realtime in een while-lus met matplotlib?

Ik probeer een aantal gegevens van een camera in realtime te plotten met OpenCV. Het realtime plotten (met behulp van matplotlib) lijkt echter niet te werken.

Ik heb het probleem geïsoleerd in dit eenvoudige voorbeeld:

fig = plt.figure()
plt.axis([0, 1000, 0, 1])
i = 0
x = list()
y = list()
while i < 1000:
    temp_y = np.random.random()
    x.append(i)
    y.append(temp_y)
    plt.scatter(i, temp_y)
    i += 1
    plt.show()

Ik zou verwachten dat dit voorbeeld 1000 punten afzonderlijk zou plotten. Wat er feitelijk gebeurt, is dat het venster verschijnt met het eerste punt dat wordt weergegeven (oké), en vervolgens wacht tot de lus is voltooid voordat deze de rest van de grafiek vult.

Enig idee waarom ik de punten niet één voor één zie ingevuld?


Antwoord 1, autoriteit 100%

Hier is de werkende versie van de code in kwestie (vereist ten minste versie Matplotlib 1.1.0 van 2011-11-14):

import numpy as np
import matplotlib.pyplot as plt
plt.axis([0, 10, 0, 1])
for i in range(10):
    y = np.random.random()
    plt.scatter(i, y)
    plt.pause(0.05)
plt.show()

Let op de aanroep van plt.pause(0.05), die zowel de nieuwe gegevens tekent als de gebeurtenislus van de GUI uitvoert (waardoor muisinteractie mogelijk is).


Antwoord 2, autoriteit 27%

Als je geïnteresseerd bent in realtime plotten, raad ik je aan te kijken naar matplotlib’s animatie-API. Vooral het gebruik van blitom te voorkomen dat de achtergrond op elk frame opnieuw moet worden getekend, kan je aanzienlijke snelheidswinst opleveren (~10x):

#!/usr/bin/env python
import numpy as np
import time
import matplotlib
matplotlib.use('GTKAgg')
from matplotlib import pyplot as plt
def randomwalk(dims=(256, 256), n=20, sigma=5, alpha=0.95, seed=1):
    """ A simple random walk with memory """
    r, c = dims
    gen = np.random.RandomState(seed)
    pos = gen.rand(2, n) * ((r,), (c,))
    old_delta = gen.randn(2, n) * sigma
    while True:
        delta = (1. - alpha) * gen.randn(2, n) * sigma + alpha * old_delta
        pos += delta
        for ii in xrange(n):
            if not (0. <= pos[0, ii] < r):
                pos[0, ii] = abs(pos[0, ii] % r)
            if not (0. <= pos[1, ii] < c):
                pos[1, ii] = abs(pos[1, ii] % c)
        old_delta = delta
        yield pos
def run(niter=1000, doblit=True):
    """
    Display the simulation using matplotlib, optionally using blit for speed
    """
    fig, ax = plt.subplots(1, 1)
    ax.set_aspect('equal')
    ax.set_xlim(0, 255)
    ax.set_ylim(0, 255)
    ax.hold(True)
    rw = randomwalk()
    x, y = rw.next()
    plt.show(False)
    plt.draw()
    if doblit:
        # cache the background
        background = fig.canvas.copy_from_bbox(ax.bbox)
    points = ax.plot(x, y, 'o')[0]
    tic = time.time()
    for ii in xrange(niter):
        # update the xy data
        x, y = rw.next()
        points.set_data(x, y)
        if doblit:
            # restore background
            fig.canvas.restore_region(background)
            # redraw just the points
            ax.draw_artist(points)
            # fill in the axes rectangle
            fig.canvas.blit(ax.bbox)
        else:
            # redraw everything
            fig.canvas.draw()
    plt.close(fig)
    print "Blit = %s, average FPS: %.2f" % (
        str(doblit), niter / (time.time() - tic))
if __name__ == '__main__':
    run(doblit=False)
    run(doblit=True)

Uitgang:

Blit = False, average FPS: 54.37
Blit = True, average FPS: 438.27

Antwoord 3, Autoriteit 14%

Ik weet dat ik een beetje laat ben om deze vraag te beantwoorden. Niettemin heb ik een tijdje geleden een tijdje gemaakt om live grafieken te plotten, die ik wil delen:

Code voor Pyqt4:

###################################################################
#                                                                 #
#                    PLOT A LIVE GRAPH (PyQt4)                    #
#                  -----------------------------                  #
#            EMBED A MATPLOTLIB ANIMATION INSIDE YOUR             #
#            OWN GUI!                                             #
#                                                                 #
###################################################################
import sys
import os
from PyQt4 import QtGui
from PyQt4 import QtCore
import functools
import numpy as np
import random as rd
import matplotlib
matplotlib.use("Qt4Agg")
from matplotlib.figure import Figure
from matplotlib.animation import TimedAnimation
from matplotlib.lines import Line2D
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
import time
import threading
def setCustomSize(x, width, height):
    sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
    sizePolicy.setHorizontalStretch(0)
    sizePolicy.setVerticalStretch(0)
    sizePolicy.setHeightForWidth(x.sizePolicy().hasHeightForWidth())
    x.setSizePolicy(sizePolicy)
    x.setMinimumSize(QtCore.QSize(width, height))
    x.setMaximumSize(QtCore.QSize(width, height))
''''''
class CustomMainWindow(QtGui.QMainWindow):
    def __init__(self):
        super(CustomMainWindow, self).__init__()
        # Define the geometry of the main window
        self.setGeometry(300, 300, 800, 400)
        self.setWindowTitle("my first window")
        # Create FRAME_A
        self.FRAME_A = QtGui.QFrame(self)
        self.FRAME_A.setStyleSheet("QWidget { background-color: %s }" % QtGui.QColor(210,210,235,255).name())
        self.LAYOUT_A = QtGui.QGridLayout()
        self.FRAME_A.setLayout(self.LAYOUT_A)
        self.setCentralWidget(self.FRAME_A)
        # Place the zoom button
        self.zoomBtn = QtGui.QPushButton(text = 'zoom')
        setCustomSize(self.zoomBtn, 100, 50)
        self.zoomBtn.clicked.connect(self.zoomBtnAction)
        self.LAYOUT_A.addWidget(self.zoomBtn, *(0,0))
        # Place the matplotlib figure
        self.myFig = CustomFigCanvas()
        self.LAYOUT_A.addWidget(self.myFig, *(0,1))
        # Add the callbackfunc to ..
        myDataLoop = threading.Thread(name = 'myDataLoop', target = dataSendLoop, daemon = True, args = (self.addData_callbackFunc,))
        myDataLoop.start()
        self.show()
    ''''''
    def zoomBtnAction(self):
        print("zoom in")
        self.myFig.zoomIn(5)
    ''''''
    def addData_callbackFunc(self, value):
        # print("Add data: " + str(value))
        self.myFig.addData(value)
''' End Class '''
class CustomFigCanvas(FigureCanvas, TimedAnimation):
    def __init__(self):
        self.addedData = []
        print(matplotlib.__version__)
        # The data
        self.xlim = 200
        self.n = np.linspace(0, self.xlim - 1, self.xlim)
        a = []
        b = []
        a.append(2.0)
        a.append(4.0)
        a.append(2.0)
        b.append(4.0)
        b.append(3.0)
        b.append(4.0)
        self.y = (self.n * 0.0) + 50
        # The window
        self.fig = Figure(figsize=(5,5), dpi=100)
        self.ax1 = self.fig.add_subplot(111)
        # self.ax1 settings
        self.ax1.set_xlabel('time')
        self.ax1.set_ylabel('raw data')
        self.line1 = Line2D([], [], color='blue')
        self.line1_tail = Line2D([], [], color='red', linewidth=2)
        self.line1_head = Line2D([], [], color='red', marker='o', markeredgecolor='r')
        self.ax1.add_line(self.line1)
        self.ax1.add_line(self.line1_tail)
        self.ax1.add_line(self.line1_head)
        self.ax1.set_xlim(0, self.xlim - 1)
        self.ax1.set_ylim(0, 100)
        FigureCanvas.__init__(self, self.fig)
        TimedAnimation.__init__(self, self.fig, interval = 50, blit = True)
    def new_frame_seq(self):
        return iter(range(self.n.size))
    def _init_draw(self):
        lines = [self.line1, self.line1_tail, self.line1_head]
        for l in lines:
            l.set_data([], [])
    def addData(self, value):
        self.addedData.append(value)
    def zoomIn(self, value):
        bottom = self.ax1.get_ylim()[0]
        top = self.ax1.get_ylim()[1]
        bottom += value
        top -= value
        self.ax1.set_ylim(bottom,top)
        self.draw()
    def _step(self, *args):
        # Extends the _step() method for the TimedAnimation class.
        try:
            TimedAnimation._step(self, *args)
        except Exception as e:
            self.abc += 1
            print(str(self.abc))
            TimedAnimation._stop(self)
            pass
    def _draw_frame(self, framedata):
        margin = 2
        while(len(self.addedData) > 0):
            self.y = np.roll(self.y, -1)
            self.y[-1] = self.addedData[0]
            del(self.addedData[0])
        self.line1.set_data(self.n[ 0 : self.n.size - margin ], self.y[ 0 : self.n.size - margin ])
        self.line1_tail.set_data(np.append(self.n[-10:-1 - margin], self.n[-1 - margin]), np.append(self.y[-10:-1 - margin], self.y[-1 - margin]))
        self.line1_head.set_data(self.n[-1 - margin], self.y[-1 - margin])
        self._drawn_artists = [self.line1, self.line1_tail, self.line1_head]
''' End Class '''
# You need to setup a signal slot mechanism, to 
# send data to your GUI in a thread-safe way.
# Believe me, if you don't do this right, things
# go very very wrong..
class Communicate(QtCore.QObject):
    data_signal = QtCore.pyqtSignal(float)
''' End Class '''
def dataSendLoop(addData_callbackFunc):
    # Setup the signal-slot mechanism.
    mySrc = Communicate()
    mySrc.data_signal.connect(addData_callbackFunc)
    # Simulate some data
    n = np.linspace(0, 499, 500)
    y = 50 + 25*(np.sin(n / 8.3)) + 10*(np.sin(n / 7.5)) - 5*(np.sin(n / 1.5))
    i = 0
    while(True):
        if(i > 499):
            i = 0
        time.sleep(0.1)
        mySrc.data_signal.emit(y[i]) # <- Here you emit a signal!
        i += 1
    ###
###
if __name__== '__main__':
    app = QtGui.QApplication(sys.argv)
    QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('Plastique'))
    myGUI = CustomMainWindow()
    sys.exit(app.exec_())
''''''

 
Ik heb onlangs de code voor PyQt5 herschreven.
Code voor PyQt5:

###################################################################
#                                                                 #
#                    PLOT A LIVE GRAPH (PyQt5)                    #
#                  -----------------------------                  #
#            EMBED A MATPLOTLIB ANIMATION INSIDE YOUR             #
#            OWN GUI!                                             #
#                                                                 #
###################################################################
import sys
import os
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import functools
import numpy as np
import random as rd
import matplotlib
matplotlib.use("Qt5Agg")
from matplotlib.figure import Figure
from matplotlib.animation import TimedAnimation
from matplotlib.lines import Line2D
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import time
import threading
class CustomMainWindow(QMainWindow):
    def __init__(self):
        super(CustomMainWindow, self).__init__()
        # Define the geometry of the main window
        self.setGeometry(300, 300, 800, 400)
        self.setWindowTitle("my first window")
        # Create FRAME_A
        self.FRAME_A = QFrame(self)
        self.FRAME_A.setStyleSheet("QWidget { background-color: %s }" % QColor(210,210,235,255).name())
        self.LAYOUT_A = QGridLayout()
        self.FRAME_A.setLayout(self.LAYOUT_A)
        self.setCentralWidget(self.FRAME_A)
        # Place the zoom button
        self.zoomBtn = QPushButton(text = 'zoom')
        self.zoomBtn.setFixedSize(100, 50)
        self.zoomBtn.clicked.connect(self.zoomBtnAction)
        self.LAYOUT_A.addWidget(self.zoomBtn, *(0,0))
        # Place the matplotlib figure
        self.myFig = CustomFigCanvas()
        self.LAYOUT_A.addWidget(self.myFig, *(0,1))
        # Add the callbackfunc to ..
        myDataLoop = threading.Thread(name = 'myDataLoop', target = dataSendLoop, daemon = True, args = (self.addData_callbackFunc,))
        myDataLoop.start()
        self.show()
        return
    def zoomBtnAction(self):
        print("zoom in")
        self.myFig.zoomIn(5)
        return
    def addData_callbackFunc(self, value):
        # print("Add data: " + str(value))
        self.myFig.addData(value)
        return
''' End Class '''
class CustomFigCanvas(FigureCanvas, TimedAnimation):
    def __init__(self):
        self.addedData = []
        print(matplotlib.__version__)
        # The data
        self.xlim = 200
        self.n = np.linspace(0, self.xlim - 1, self.xlim)
        a = []
        b = []
        a.append(2.0)
        a.append(4.0)
        a.append(2.0)
        b.append(4.0)
        b.append(3.0)
        b.append(4.0)
        self.y = (self.n * 0.0) + 50
        # The window
        self.fig = Figure(figsize=(5,5), dpi=100)
        self.ax1 = self.fig.add_subplot(111)
        # self.ax1 settings
        self.ax1.set_xlabel('time')
        self.ax1.set_ylabel('raw data')
        self.line1 = Line2D([], [], color='blue')
        self.line1_tail = Line2D([], [], color='red', linewidth=2)
        self.line1_head = Line2D([], [], color='red', marker='o', markeredgecolor='r')
        self.ax1.add_line(self.line1)
        self.ax1.add_line(self.line1_tail)
        self.ax1.add_line(self.line1_head)
        self.ax1.set_xlim(0, self.xlim - 1)
        self.ax1.set_ylim(0, 100)
        FigureCanvas.__init__(self, self.fig)
        TimedAnimation.__init__(self, self.fig, interval = 50, blit = True)
        return
    def new_frame_seq(self):
        return iter(range(self.n.size))
    def _init_draw(self):
        lines = [self.line1, self.line1_tail, self.line1_head]
        for l in lines:
            l.set_data([], [])
        return
    def addData(self, value):
        self.addedData.append(value)
        return
    def zoomIn(self, value):
        bottom = self.ax1.get_ylim()[0]
        top = self.ax1.get_ylim()[1]
        bottom += value
        top -= value
        self.ax1.set_ylim(bottom,top)
        self.draw()
        return
    def _step(self, *args):
        # Extends the _step() method for the TimedAnimation class.
        try:
            TimedAnimation._step(self, *args)
        except Exception as e:
            self.abc += 1
            print(str(self.abc))
            TimedAnimation._stop(self)
            pass
        return
    def _draw_frame(self, framedata):
        margin = 2
        while(len(self.addedData) > 0):
            self.y = np.roll(self.y, -1)
            self.y[-1] = self.addedData[0]
            del(self.addedData[0])
        self.line1.set_data(self.n[ 0 : self.n.size - margin ], self.y[ 0 : self.n.size - margin ])
        self.line1_tail.set_data(np.append(self.n[-10:-1 - margin], self.n[-1 - margin]), np.append(self.y[-10:-1 - margin], self.y[-1 - margin]))
        self.line1_head.set_data(self.n[-1 - margin], self.y[-1 - margin])
        self._drawn_artists = [self.line1, self.line1_tail, self.line1_head]
        return
''' End Class '''
# You need to setup a signal slot mechanism, to
# send data to your GUI in a thread-safe way.
# Believe me, if you don't do this right, things
# go very very wrong..
class Communicate(QObject):
    data_signal = pyqtSignal(float)
''' End Class '''
def dataSendLoop(addData_callbackFunc):
    # Setup the signal-slot mechanism.
    mySrc = Communicate()
    mySrc.data_signal.connect(addData_callbackFunc)
    # Simulate some data
    n = np.linspace(0, 499, 500)
    y = 50 + 25*(np.sin(n / 8.3)) + 10*(np.sin(n / 7.5)) - 5*(np.sin(n / 1.5))
    i = 0
    while(True):
        if(i > 499):
            i = 0
        time.sleep(0.1)
        mySrc.data_signal.emit(y[i]) # <- Here you emit a signal!
        i += 1
    ###
###
if __name__== '__main__':
    app = QApplication(sys.argv)
    QApplication.setStyle(QStyleFactory.create('Plastique'))
    myGUI = CustomMainWindow()
    sys.exit(app.exec_())

Probeer het gewoon uit. Kopieer deze code in een nieuw Python-bestand en voer het uit. Je moet een mooie, soepel bewegende grafiek krijgen:


Antwoord 4, Autoriteit 10%

Geen van de methoden voor mij.
Maar ik heb dit gevonden
Realtime Matplotlib-plot werkt niet terwijl nog steeds in een lus

alles wat je nodig hebt is om

toe te voegen

plt.pause(0.0001)

En dan zag je de nieuwe plots.

zodat uw code er zo uit moet zien, en het zal werken

import matplotlib.pyplot as plt
import numpy as np
plt.ion() ## Note this correction
fig=plt.figure()
plt.axis([0,1000,0,1])
i=0
x=list()
y=list()
while i <1000:
    temp_y=np.random.random();
    x.append(i);
    y.append(temp_y);
    plt.scatter(i,temp_y);
    i+=1;
    plt.show()
    plt.pause(0.0001) #Note this correction

Antwoord 5, Autoriteit 9%

showis waarschijnlijk niet de beste keuze hiervoor. Wat ik zou doen is gebruiken pyplot.draw()in plaats daarvan. U kunt ook een kleine tijdsvertraging opnemen (bijvoorbeeld time.sleep(0.05)) in de lus, zodat u de plots gebeuren kunt zien. Als ik deze wijzigingen in uw voorbeeld maak, werkt het voor mij en zie ik elk punt één tegelijk.


Antwoord 6, Autoriteit 8%

De bovenste (en vele andere) antwoorden zijn gebouwd op plt.pause(), maar dat was een oude manier om het perceel in matplotlib te animeren. Het is niet alleen traag, maar zorgt er ook voor dat de focus op elke update wordt gepakt (ik had het moeilijk om het plotpython-proces te stoppen).

TL; DR: Mogelijk wilt u matplotlib.animation(zoals vermeld in documentatie ).

Na het graven van verschillende antwoorden en stukken code, bleek dit in feite een soepele manier om inkomende gegevens oneindig voor mij te tekenen.

Hier is mijn code voor een snelle start. Het plotsert de huidige tijd met een willekeurig getal in [0, 100) elke 200ms oneindig, terwijl ook automatisch recaling van de weergave:

from datetime import datetime
from matplotlib import pyplot
from matplotlib.animation import FuncAnimation
from random import randrange
x_data, y_data = [], []
figure = pyplot.figure()
line, = pyplot.plot_date(x_data, y_data, '-')
def update(frame):
    x_data.append(datetime.now())
    y_data.append(randrange(0, 100))
    line.set_data(x_data, y_data)
    figure.gca().relim()
    figure.gca().autoscale_view()
    return line,
animation = FuncAnimation(figure, update, interval=200)
pyplot.show()

U kunt ook verkennen blitvoor nog betere prestaties zoals in funcanimatiedocumentatie .

Een voorbeeld van de blitdocumentatie:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = plt.plot([], [], 'ro')
def init():
    ax.set_xlim(0, 2*np.pi)
    ax.set_ylim(-1, 1)
    return ln,
def update(frame):
    xdata.append(frame)
    ydata.append(np.sin(frame))
    ln.set_data(xdata, ydata)
    return ln,
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
                    init_func=init, blit=True)
plt.show()

Antwoord 7, autoriteit 4%

Ik weet dat deze vraag oud is, maar er is nu een pakket beschikbaar met de naam drawnowop GitHub als “python-getekende”. Dit biedt een interface die lijkt op MATLAB’s drawow — u kunt gemakkelijkeen figuur bijwerken.

Een voorbeeld voor uw gebruik:

import matplotlib.pyplot as plt
from drawnow import drawnow
def make_fig():
    plt.scatter(x, y)  # I think you meant this
plt.ion()  # enable interactivity
fig = plt.figure()  # make a figure
x = list()
y = list()
for i in range(1000):
    temp_y = np.random.random()
    x.append(i)
    y.append(temp_y)  # or any arbitrary update to your figure's data
    i += 1
    drawnow(make_fig)

python-draw is een dunne wikkel rond plt.drawmaar biedt de mogelijkheid om te bevestigen (of te debuggen) na weergave van figuren.


Antwoord 8, autoriteit 2%

Het probleem lijkt te zijn dat je verwacht dat plt.show()het venster toont en dan terugkeert. Dat doet het niet. Het programma stopt op dat punt en wordt pas hervat als u het venster sluit. Je zou dat moeten kunnen testen: Als je het venster sluit en er zou een ander venster moeten verschijnen.

Om dat probleem op te lossen, hoeft u slechts eenmaal na uw lus plt.show()aan te roepen. Dan krijg je de volledige plot. (Maar geen ‘realtime plotten’)

U kunt proberen het trefwoordargument blockals volgt in te stellen: plt.show(block=False)eenmaal aan het begin en vervolgens .draw()om bij te werken.


Antwoord 9, autoriteit 2%

Een andere optie is om te kiezen voor bokeh. IMO, het is in ieder geval een goed alternatief voor realtime plots. Hier is een bokeh-versie van de code in de vraag:

from bokeh.plotting import curdoc, figure
import random
import time
def update():
    global i
    temp_y = random.random()
    r.data_source.stream({'x': [i], 'y': [temp_y]})
    i += 1
i = 0
p = figure()
r = p.circle([], [])
curdoc().add_root(p)
curdoc().add_periodic_callback(update, 100)

en om het uit te voeren:

pip3 install bokeh
bokeh serve --show test.py

bokeh toont het resultaat in een webbrowser via websocket-communicatie. Het is vooral handig wanneer gegevens worden gegenereerd door externe headless serverprocessen.


Antwoord 10

Hier is een versie die ik op mijn systeem heb laten werken.

import matplotlib.pyplot as plt
from drawnow import drawnow
import numpy as np
def makeFig():
    plt.scatter(xList,yList) # I think you meant this
plt.ion() # enable interactivity
fig=plt.figure() # make a figure
xList=list()
yList=list()
for i in np.arange(50):
    y=np.random.random()
    xList.append(i)
    yList.append(y)
    drawnow(makeFig)
    #makeFig()      The drawnow(makeFig) command can be replaced
    #plt.draw()     with makeFig(); plt.draw()
    plt.pause(0.001)

De GetoWow (Makefig) -lijn kan worden vervangen door een makfig (); PLT.DRAW () SEQUENCE en het werkt nog steeds goed.


Antwoord 11

Een voorbeeldgebruik-case om CPU-gebruik in realtime te plotten.

import time
import psutil
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
i = 0
x, y = [], []
while True:
    x.append(i)
    y.append(psutil.cpu_percent())
    ax.plot(x, y, color='b')
    fig.canvas.draw()
    ax.set_xlim(left=max(0, i - 50), right=i + 50)
    fig.show()
    plt.pause(0.05)
    i += 1

Antwoord 12

Als u wilt tekenen en uw draad niet bevriezen, omdat er meer punt is getekend, moet u PLT.PAUSE () NOT TIME.SLEEP ()

gebruiken

Im gebruik van de volgende code om een ​​reeks XY-coördinaten te plotten.

import matplotlib.pyplot as plt 
import math
pi = 3.14159
fig, ax = plt.subplots()
x = []
y = []
def PointsInCircum(r,n=20):
    circle = [(math.cos(2*pi/n*x)*r,math.sin(2*pi/n*x)*r) for x in xrange(0,n+1)]
    return circle
circle_list = PointsInCircum(3, 50)
for t in range(len(circle_list)):
    if t == 0:
        points, = ax.plot(x, y, marker='o', linestyle='--')
        ax.set_xlim(-4, 4) 
        ax.set_ylim(-4, 4) 
    else:
        x_coord, y_coord = circle_list.pop()
        x.append(x_coord)
        y.append(y_coord)
        points.set_data(x, y)
    plt.pause(0.01)

Other episodes