¿Cómo trazo en tiempo real en un ciclo while usando matplotlib?

233

Estoy tratando de trazar algunos datos de una cámara en tiempo real usando OpenCV. Sin embargo, el trazado en tiempo real (usando matplotlib) no parece estar funcionando.

He aislado el problema en este simple ejemplo:

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

Esperaría que este ejemplo trazara 1000 puntos individualmente. Lo que realmente sucede es que la ventana aparece con el primer punto que se muestra (está bien con eso), luego espera a que finalice el bucle antes de llenar el resto del gráfico.

¿Alguna idea de por qué no veo puntos poblados uno a la vez?

Chris
fuente

Respuestas:

313

Aquí está la versión funcional del código en cuestión (requiere al menos la versión Matplotlib 1.1.0 del 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()

Tenga en cuenta algunos de los cambios:

  1. Llama plt.pause(0.05)a ambos para dibujar los nuevos datos y ejecuta el bucle de eventos de la GUI (permitiendo la interacción del mouse).
Velimir Mlaker
fuente
3
Esto funcionó para mí en Python2. En Python3 no lo hizo. Pausaría el bucle después de representar la ventana de trazado. Pero después de mover el método plt.show () después del ciclo ... lo resolvió para Python3, para mí.
Continuarqa
1
Extraño, funcionó bien para mí 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
en lugar de plt.show () y plt.draw () simplemente reemplace plt.draw () con plt.pause (0.1)
denfromufa
44
No funcionó en Win64 / Anaconda matplotlib .__ versión__ 1.5.0. Se abrió una ventana de figura inicial, pero no mostró nada, permaneció en un estado bloqueado hasta que la cerré
isti_spl
55
Esta respuesta requiere un conocimiento a priori de los datos x / y ... lo cual no es necesario: prefiero 1. no llame, plt.axis()sino que cree dos listas x e y y llame plt.plot(x,y)2. en su bucle, agregue nuevos valores de datos a las dos listas 3. llamarplt.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 está interesado en el trazado en tiempo real, le recomiendo que busque en la API de animación de matplotlib . En particular, usar blitpara evitar volver a dibujar el fondo en cada cuadro puede brindarle ganancias de velocidad sustanciales (~ 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)

Salida:

Blit = False, average FPS: 54.37
Blit = True, average FPS: 438.27
ali_m
fuente
1
@bejota La versión original fue diseñada para funcionar dentro de una sesión interactiva matplotlib. Para que funcione como una secuencia de comandos independiente, es necesario 1) seleccionar explícitamente un backend para matplotlib, y 2) para forzar que la figura se muestre y dibuje antes de ingresar al bucle de animación usando plt.show()y plt.draw(). He agregado estos cambios al código anterior.
ali_m
2
¿La intención / motivación de la blit()parece ser "mejorar la trama en tiempo real"? Si tiene un desarrollador / blog matplotlib discutiendo el por qué / propósito / intención / motivación, sería genial. (parece que esta nueva operación blit convertiría Matplotlib de usar solo para datos fuera de línea o que cambian muy lentamente, ahora puede usar Matplotlib con datos de actualización muy rápidos ... casi como un osciloscopio).
Trevor Boyd Smith
1
He descubierto que este enfoque hace que la ventana de la trama no responda: no puedo interactuar con él y, al hacerlo, puede bloquearse.
Ninjakannon
1
Para aquellos que obtienen el problema "gtk not found", funciona bien con un back-end diferente (usé 'TKAgg'). Para encontrar un respaldo compatible, utilicé esta solución: stackoverflow.com/questions/3285193/…
James Nelson el
1
El enlace en esta respuesta ya no parece funcionar. Este podría ser un enlace actualizado: scipy-cookbook.readthedocs.io/items/…
awelkie
35

Sé que llego un poco tarde para responder esta pregunta. Sin embargo, hice un código hace un tiempo para trazar gráficos en vivo, que me gustaría compartir:

Código para 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_())

''''''

 
Recientemente reescribí el código para PyQt5.
Código para 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_())

Solo pruébalo. Copie y pegue este código en un nuevo archivo python y ejecútelo. Debería obtener un gráfico hermoso y de movimiento suave:

ingrese la descripción de la imagen aquí

K.Mulier
fuente
Noté que el dataSendLoophilo seguía ejecutándose en segundo plano cuando cierras la ventana. Entonces agregué la daemon = Truepalabra clave para resolver ese problema.
K.Mulier
1
El entorno virtual para esto tomó un poco de trabajo. Finalmente, conda install pyqt=4hizo el truco.
Reb.Cabin
1
Muchas gracias por el código básico. Me ayudó a construir una interfaz de usuario simple modificando y agregando características basadas en su código. Me ahorró tiempo =]
Isaac Sim
Hola @IsaacSim, muchas gracias por tu amable mensaje. Estoy feliz de que este código haya sido útil :-)
K.Mulier
Así que tomé este script y agregué marcas de tiempo al eje x modificando el mecanismo de ranura de señal para usar un tipo np.ndarry y emitiendo una matriz np. de la marca de tiempo relativa y la señal. Estoy actualizando el xlim () en cada dibujo del cuadro, lo que funciona bien para mostrar la señal con el nuevo eje, pero no las etiquetas x / ticks solo se actualizan brevemente cuando cambio el tamaño de la ventana. @ K.Mulier Básicamente, estoy buscando un eje xtick deslizante como los datos y me preguntaba si tuvo algún éxito en algo como esto.
nimig18
33

showProbablemente no sea la mejor opción para esto. Lo que haría es usar pyplot.draw()en su lugar. También es posible que desee incluir un pequeño retraso de tiempo (p. Ej., time.sleep(0.05)) En el bucle para que pueda ver los gráficos que ocurren. Si realizo estos cambios en su ejemplo, funciona para mí y veo que cada punto aparece uno a la vez.

BrenBarn
fuente
10
Tengo una parte muy similar del código, y cuando pruebo su solución (dibujar en lugar de mostrar y demora) Python no abre una ventana de figura, simplemente pasa por el ciclo ...
George Aprilis
31

Ninguno de los métodos funcionó para mí. Pero he encontrado que este diagrama de matplotlib en tiempo real no funciona mientras todavía está en un bucle

Todo lo que necesitas es agregar

plt.pause(0.0001)

y luego podías ver las nuevas parcelas.

Entonces su código debería verse así, y funcionará

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
Oren
fuente
66
Esto abre una nueva ventana de figura / diagrama cada vez para mí, ¿hay alguna manera de actualizar la figura existente? tal vez es porque estoy usando imshow?
Francisco Vargas
@FranciscoVargas si está usando imshow, necesita usar set_data, mire aquí: stackoverflow.com/questions/17835302/…
Oren
22

Las respuestas principales (y muchas otras) se plt.pause()basaron, pero esa era una antigua forma de animar la trama en matplotlib. No solo es lento, sino que también hace que se enfoque en cada actualización (tuve dificultades para detener el proceso de trazado de Python).

TL; DR: es posible que desee utilizar matplotlib.animation( como se menciona en la documentación ).

Después de buscar varias respuestas y fragmentos de código, de hecho, resultó ser una forma fluida de extraer datos entrantes infinitamente para mí.

Aquí está mi código para un inicio rápido. Traza la hora actual con un número aleatorio en [0, 100) cada 200 ms infinitamente, al tiempo que maneja el cambio de escala automático de la vista:

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

También puede explorar blitpara obtener un rendimiento aún mejor, como en la documentación de FuncAnimation .

Un ejemplo de la blitdocumentación:

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()
Hai Zhang
fuente
Hola, qué pasaría si todo esto estuviera en un bucle. por ejemplo for i in range(1000): x,y = some func_func(). Aquí some_func()genera x,ypares de datos en línea , que me gustaría trazar una vez que estén disponibles. ¿Es posible hacer esto con FuncAnimation? Mi objetivo es construir la curva definida por los datos paso a paso con cada iteración.
Alexander Cska
@Alexander Cska pyploy.show()debería bloquear. Si desea agregar datos, recupérelos y actualícelos en la updatefunción.
Hai Zhang el
Me temo que realmente no entiendo tu respuesta. ¿Podría ampliar su sugerencia por favor?
Alexander Cska
Quiero decir, si llamas pyplot.showen un bucle, el bucle será bloqueado por esta llamada y no continuará. Si desea agregar datos a la curva paso a paso, ingrese su lógica update, que se llamará cada uno, intervalpor lo que también es paso a paso.
Hai Zhang el
El código de Zhang funciona desde la consola pero no en jupyter. Acabo de obtener un diagrama en blanco allí. De hecho, cuando relleno una matriz en jupyter en un bucle secuencial e imprimo la matriz a medida que crece con una declaración pet.plot, puedo obtener una impresión de las matrices individualmente, pero solo una gráfica. vea este código: gist.github.com/bwanaaa/12252cf36b35fced0eb3c2f64a76cb8a
aquagremlin
15

Sé que esta pregunta es antigua, pero ahora hay un paquete disponible llamado drawnow en GitHub como "python-drawnow". Esto proporciona una interfaz similar al dibujo de MATLAB: puede actualizar fácilmente una figura.

Un ejemplo para su caso de uso:

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 es una envoltura delgada plt.drawpero proporciona la capacidad de confirmar (o depurar) después de la visualización de la figura.

Scott
fuente
Esto hace que tk cuelgue en alguna parte
chwi
Si es así, presente un problema con más contexto github.com/scottsievert/python-drawnow/issues
Scott
+1 Esto funcionó para mí para trazar datos en vivo por fotograma de captura de video de opencv, mientras que matplotlib se congeló.
jj080808
Intenté esto y parecía más lento que otros métodos.
Dave C
no use, mi servidor se reinicia, matplotlib frozen
big-vl
6

El problema parece ser que espera plt.show()mostrar la ventana y luego regresar. No hace eso. El programa se detendrá en ese punto y solo se reanudará una vez que cierre la ventana. Debería poder probar eso: si cierra la ventana y luego aparece otra ventana emergente.

Para resolver ese problema, solo llame plt.show()una vez después de su ciclo. Entonces obtienes la trama completa. (Pero no una "trama en tiempo real")

Puede intentar establecer el argumento de palabra clave de blockesta manera: plt.show(block=False)una vez al principio y luego usar .draw()para actualizar.

Michael Mauderer
fuente
1
Trazar en tiempo real es realmente lo que estoy buscando. Voy a realizar una prueba de 5 horas en algo y quiero ver cómo progresan las cosas.
Chris
@ Chris, ¿pudiste realizar la prueba de 5 horas? También estoy buscando algo similar. Estoy usando plyplot.pause (time_duration) para actualizar la trama. ¿Hay alguna otra forma de hacerlo?
Prakhar Mohan Srivastava
4

Aquí hay una versión que pude trabajar en mi sistema.

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 línea dibujada (makeFig) se puede reemplazar por un makeFig (); secuencia plt.draw () y aún funciona bien.

slehar
fuente
1
¿Cómo sabes cuánto tiempo hacer una pausa? Parece depender de la trama misma.
CMCDragonkai
1

Si desea dibujar y no congelar su hilo a medida que se dibujan más puntos, debe usar plt.pause () no time.sleep ()

Estoy usando el siguiente código para trazar una serie de coordenadas 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)
usuario2672474
fuente
1

Otra opción es ir con bokeh . En mi opinión, es una buena alternativa al menos para tramas en tiempo real. Aquí hay una versión bokeh del código en la pregunta:

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)

y para ejecutarlo:

pip3 install bokeh
bokeh serve --show test.py

bokeh muestra el resultado en un navegador web a través de comunicaciones websocket. Es especialmente útil cuando los datos son generados por procesos remotos del servidor sin cabeza.

parcela de muestra de bokeh

Hamid Fadishei
fuente
0

Un ejemplo de caso de uso para trazar el uso de la CPU en tiempo real.

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
Nilani Algiriyage
fuente
Realmente comienza a disminuir después de unos 2 minutos. ¿Cuál podría ser la razón? Quizás los puntos anteriores, que quedan fuera de la vista actual, deberían descartarse.
pfabri
Esto se ve muy bien, pero hay un par de problemas: 1. es imposible dejar de fumar 2. después de unos minutos, el programa consume casi 100 Mb de RAM y comienza a disminuir drásticamente.
pfabri