Comment puis-je tracer en temps réel dans une boucle while en utilisant matplotlib?


233

J'essaie de tracer certaines données d'une caméra en temps réel en utilisant OpenCV. Cependant, le traçage en temps réel (en utilisant matplotlib) ne semble pas fonctionner.

J'ai isolé le problème dans cet exemple simple:

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

Je m'attendrais à ce que cet exemple trace 1000 points individuellement. Ce qui se passe réellement, c'est que la fenêtre apparaît avec le premier point affiché (ok avec ça), puis attend que la boucle se termine avant de remplir le reste du graphique.

Avez-vous des raisons pour lesquelles je ne vois pas de points remplis un à la fois?

Réponses:


313

Voici la version de travail du code en question (nécessite au moins la version Matplotlib 1.1.0 du 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()

Notez certaines des modifications:

  1. Appelez plt.pause(0.05)à la fois pour dessiner les nouvelles données et il exécute la boucle d'événements de l'interface graphique (permettant une interaction avec la souris).

3
Cela a fonctionné pour moi en Python2. En Python3, ce n'était pas le cas. Cela interromprait la boucle après le rendu de la fenêtre de tracé. Mais après avoir déplacé la méthode plt.show () après la boucle ... cela l'a résolu pour Python3, pour moi.
continuousqa

1
Bizarre, a bien fonctionné pour moi en Python 3 (ver 3.4.0) Matplotlib (ver 1.3.1) Numpy (ver 1.8.1) Ubuntu Linux 3.13.0 64 bits
Velimir Mlaker

37
au lieu de plt.show () et plt.draw () remplacez simplement plt.draw () par plt.pause (0.1)
denfromufa

4
N'a pas fonctionné sur Win64 / Anaconda matplotlib .__ version__ 1.5.0. Une première fenêtre de chiffres s'est ouverte, mais n'affichait rien, elle est restée bloquée jusqu'à ce que je la ferme
isti_spl

5
Cette réponse nécessite une connaissance a priori des données x / y ... ce qui n'est pas nécessaire: je préfère 1. n'appelez pas plt.axis()mais créez plutôt deux listes x et y et appelez plt.plot(x,y)2. dans votre boucle, ajoutez de nouvelles valeurs de données à les deux listes 3. appelerplt.gca().lines[0].set_xdata(x); plt.gca().lines[0].set_ydata(y); plt.gca().relim(); plt.gca().autoscale_view(); plt.pause(0.05);
Trevor Boyd Smith

76

Si vous êtes intéressé par le traçage en temps réel, je vous recommande de consulter l'API d'animation de matplotlib . En particulier, l'utilisation blitpour éviter de redessiner l'arrière-plan sur chaque image peut vous donner des gains de vitesse importants (~ 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)

Production:

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

1
@bejota La version originale a été conçue pour fonctionner dans une session interactive matplotlib. Pour le faire fonctionner comme un script autonome, il est nécessaire 1) de sélectionner explicitement un backend pour matplotlib, et 2) de forcer la figure à être affichée et dessinée avant d'entrer dans la boucle d'animation en utilisant plt.show()et plt.draw(). J'ai ajouté ces modifications au code ci-dessus.
ali_m

2
L'intention / la motivation de blit()semble - t-elle vraiment "améliorer le traçage en temps réel"? Si vous avez un développeur / blog matplotlib discutant du pourquoi / du but / de l'intention / de la motivation, ce serait formidable. (il semble que cette nouvelle opération de conversion convertirait Matplotlib uniquement en utilisation hors ligne ou en modifiant très lentement les données pour maintenant vous pouvez utiliser Matplotlib avec des données de mise à jour très rapides ... presque comme un oscilloscope).
Trevor Boyd Smith

1
J'ai trouvé que cette approche rend la fenêtre de traçage insensible: je ne peux pas interagir avec elle, et cela pourrait la bloquer.
Ninjakannon

1
Pour ceux qui rencontrent un problème "gtk not found", cela fonctionne très bien avec un back-end différent (j'ai utilisé 'TKAgg'). Pour trouver un support pris en charge, j'ai utilisé cette solution: stackoverflow.com/questions/3285193/…
James Nelson

1
Le lien dans cette réponse ne semble plus fonctionner. Il peut s'agir d'un lien à jour: scipy-cookbook.readthedocs.io/items/…
awelkie

35

Je sais que je suis un peu en retard pour répondre à cette question. Néanmoins, j'ai créé du code il y a un certain temps pour tracer des graphiques en direct, que je voudrais partager:

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

''''''

 
J'ai récemment réécrit le code de PyQt5.
Code pour 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_())

Essayez-le. Copiez-collez ce code dans un nouveau fichier python et exécutez-le. Vous devriez obtenir un beau graphique en mouvement fluide:

entrez la description de l'image ici


J'ai remarqué que le dataSendLoopfil continuait de fonctionner en arrière-plan lorsque vous fermez la fenêtre. J'ai donc ajouté le daemon = Truemot clé pour résoudre ce problème.
K.Mulier

1
L'environnement virtuel pour cela a pris un peu de travail. Enfin, a conda install pyqt=4fait l'affaire.
Reb.Cabin

1
Merci beaucoup pour le code de base. Cela m'a aidé à créer une interface utilisateur simple en modifiant et en ajoutant des fonctionnalités en fonction de votre code. Cela m'a fait gagner du temps =]
Isaac Sim

Bonjour @IsaacSim, merci beaucoup pour votre aimable message. Je suis heureux que ce code ait été utile :-)
K.Mulier

J'ai donc pris ce script et ajouté des horodatages à l'axe des x en modifiant le mécanisme de fente du signal pour utiliser un type np.ndarry et en émettant un np.array de l'horodatage et du signal relatifs. Je mets à jour le xlim () sur chaque dessin de trame, ce qui convient pour afficher le signal avec le nouvel axe, mais pas les x-labels / ticks ne se mettent à jour que brièvement lorsque je change la taille de la fenêtre. @ K.Mulier Je suis fondamentalement après un axe xtick coulissant comme les données et je me demandais si vous aviez du succès sur quelque chose comme ça?
nimig18

33

shown'est probablement pas le meilleur choix pour cela. Ce que je ferais, c'est utiliser à la pyplot.draw()place. Vous pouvez également vouloir inclure un petit délai (par exemple, time.sleep(0.05)) dans la boucle afin que vous puissiez voir les tracés se produire. Si j'apporte ces modifications à votre exemple, cela fonctionne pour moi et je vois chaque point apparaître un à la fois.


10
J'ai une partie très similaire du code, et quand j'essaie votre solution (dessiner au lieu de montrer et retarder) python n'ouvre pas du tout une fenêtre de figure, passe simplement par la boucle ...
George Aprilis

31

Aucune des méthodes n'a fonctionné pour moi. Mais j'ai trouvé que ce tracé matplotlib en temps réel ne fonctionne pas alors qu'il est encore en boucle

Il vous suffit d'ajouter

plt.pause(0.0001)

et ensuite vous pouviez voir les nouvelles parcelles.

Donc, votre code devrait ressembler à ceci, et cela fonctionnera

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

6
Cela ouvre à chaque fois une nouvelle figure / fenêtre de tracé. Y a-t-il un moyen de simplement mettre à jour la figure existante? peut-être que c'est parce que j'utilise imshow?
Francisco Vargas

@FranciscoVargas si vous utilisez imshow, vous devez utiliser set_data, regardez ici: stackoverflow.com/questions/17835302/…
Oren

22

Les meilleures réponses (et bien d'autres) ont été construites plt.pause(), mais c'était une ancienne façon d'animer l'intrigue dans matplotlib. Il est non seulement lent, mais provoque également une concentration sur chaque mise à jour (j'ai eu du mal à arrêter le processus de traçage de python).

TL; DR: vous pouvez utiliser matplotlib.animation( comme mentionné dans la documentation ).

Après avoir creusé autour de diverses réponses et morceaux de code, cela s'est en fait avéré être un moyen fluide de dessiner les données entrantes à l'infini pour moi.

Voici mon code pour un démarrage rapide. Il trace l'heure actuelle avec un nombre aléatoire en [0, 100) toutes les 200 ms à l'infini, tout en gérant la mise à l'échelle automatique de la vue:

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

Vous pouvez également explorer blitpour des performances encore meilleures comme dans la documentation FuncAnimation .

Un exemple tiré de la blitdocumentation:

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

Salut, que se passera-t-il si tout cela est en boucle. dire for i in range(1000): x,y = some func_func(). Ici some_func()génère x,ydes paires de données en ligne , que je voudrais tracer une fois qu'elles sont disponibles. Est-il possible de le faire avec FuncAnimation. Mon objectif est de construire la courbe définie par les données étape par étape à chaque itération.
Alexander Cska

@Alexander Cska pyploy.show()devrait bloquer. Si vous souhaitez ajouter des données, récupérez-les et mettez à jour la updatefonction.
Hai Zhang

Je crains de ne pas vraiment comprendre votre réponse. Pourriez-vous amplifier votre suggestion s'il vous plaît.
Alexander Cska

Je veux dire, si vous appelez pyplot.showdans une boucle, la boucle sera bloquée par cet appel et ne continuera pas. Si vous souhaitez ajouter des données à la courbe étape par étape, insérez votre logique update, qui sera appelée chaque intervaldonc c'est également étape par étape.
Hai Zhang

Le code de Zhang fonctionne depuis la console mais pas en jupyter. J'obtiens juste un complot vide là-bas. En fait, lorsque je remplis un tableau en jupyter dans une boucle séquentielle et que j'imprime le tableau au fur et à mesure qu'il grandit avec une instruction pet.plot, je peux obtenir une impression des tableaux individuellement mais un seul tracé. voir ce code: gist.github.com/bwanaaa/12252cf36b35fced0eb3c2f64a76cb8a
aquagremlin

15

Je sais que cette question est ancienne, mais il y a maintenant un paquet disponible appelé drawow sur GitHub comme "python-drawnow". Cela fournit une interface similaire au dessin de MATLAB - vous pouvez facilement mettre à jour une figure.

Un exemple pour votre cas d'utilisation:

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-drawnow est un wrapper mince, plt.drawmais offre la possibilité de confirmer (ou déboguer) après l'affichage de la figure.


Cela fait tk pendre quelque part
chwi

Si c'est le cas, signalez
Scott

+1 Cela a fonctionné pour moi pour tracer des données en direct par image de capture vidéo à partir d'opencv, tandis que matplotlib s'est figé.
jj080808

J'ai essayé cela et cela semblait plus lent que les autres méthodes.
Dave C

ne pas utiliser, le redémarrage de mon serveur, matplotlib figé
big-vl

6

Le problème semble être que vous vous attendez plt.show()à afficher la fenêtre, puis à revenir. Ça ne fait pas ça. Le programme s'arrêtera à ce point et ne reprendra qu'une fois que vous aurez fermé la fenêtre. Vous devriez pouvoir tester cela: si vous fermez la fenêtre, puis une autre fenêtre devrait apparaître.

Pour résoudre ce problème, appelez simplement plt.show()une fois après votre boucle. Ensuite, vous obtenez l'intrigue complète. (Mais pas un «tracé en temps réel»)

Vous pouvez essayer de définir l'argument mot-clé blockcomme ceci: plt.show(block=False)une fois au début, puis utilisez .draw()pour mettre à jour.


1
le traçage en temps réel est vraiment ce que je veux. Je vais exécuter un test de 5 heures sur quelque chose et je veux voir comment les choses progressent.
Chris

@Chris avez-vous pu effectuer le test de 5 heures? Je recherche également quelque chose de similaire. J'utilise plyplot.pause (time_duration) pour mettre à jour le tracé. Y a-t-il une autre manière de procéder?
Prakhar Mohan Srivastava

4

Voici une version que j'ai pu travailler sur mon système.

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)

La ligne drawow (makeFig) peut être remplacée par un makeFig (); plt.draw () séquence et cela fonctionne toujours OK.


1
Comment savez-vous combien de temps faire une pause? Cela semble dépendre de l'intrigue elle-même.
CMCDragonkai

1

Si vous voulez dessiner et ne pas geler votre thread car plus de points sont dessinés, vous devez utiliser plt.pause () et non time.sleep ()

im en utilisant le code suivant pour tracer une série de coordonnées xy.

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)

1

Une autre option consiste à opter pour le bokeh . OMI, c'est une bonne alternative au moins pour les tracés en temps réel. Voici une version bokeh du code dans la question:

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)

et pour l'exécuter:

pip3 install bokeh
bokeh serve --show test.py

bokeh affiche le résultat dans un navigateur Web via les communications Websocket. Il est particulièrement utile lorsque les données sont générées par des processus de serveur sans tête distants.

échantillon de bokeh


0

Un exemple d'utilisation pour tracer l'utilisation du processeur en temps réel.

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

Il commence vraiment à ralentir après environ 2 minutes. Quelle pourrait être la raison? Peut-être les points antérieurs, qui ne relèvent pas de la vue actuelle, devraient être supprimés.
pfabri

Cela a l'air vraiment bien, mais il y a quelques problèmes: 1. il est impossible de quitter 2. après quelques minutes seulement, le programme consomme près de 100 Mo de RAM et commence à ralentir considérablement.
pfabri
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.