Actualización dinámica del gráfico en matplotlib

Resuelto Shadman Anwer asked hace 12 años • 4 respuestas

Estoy creando una aplicación en Python que recopila datos de un puerto serie y traza un gráfico de los datos recopilados en función de la hora de llegada. La hora de llegada de los datos es incierta. Quiero que el gráfico se actualice cuando se reciban los datos. Busqué cómo hacer esto y encontré dos métodos:

  1. Borre el gráfico y vuelva a dibujarlo con todos los puntos.
  2. Anima la trama cambiándola después de un intervalo particular.

No prefiero el primero porque el programa se ejecuta y recopila datos durante mucho tiempo (un día, por ejemplo), y volver a dibujar la trama será bastante lento. El segundo tampoco es preferible ya que el tiempo de llegada de los datos es incierto y quiero que el gráfico se actualice solo cuando se reciban los datos.

¿Hay alguna manera de actualizar el gráfico simplemente agregándole más puntos solo cuando se reciben los datos?

Shadman Anwer avatar Jun 08 '12 14:06 Shadman Anwer
Aceptado

¿Hay alguna manera de actualizar la trama simplemente agregándole más puntos...?

Hay varias formas de animar datos en matplotlib, según la versión que tenga. ¿ Has visto los ejemplos de animación en la documentación de matplotlib? La API de animación define una función FuncAnimation que anima una función en el tiempo. Esta función podría ser simplemente la función que utiliza para adquirir sus datos.

Básicamente, cada método establece la datapropiedad del objeto que se está dibujando, por lo que no requiere borrar la pantalla o la figura. La datapropiedad puede simplemente ampliarse, por lo que puede conservar los puntos anteriores y seguir agregando a su línea (o imagen o lo que sea que esté dibujando).

Dado que usted dice que la hora de llegada de sus datos es incierta, su mejor opción probablemente sea hacer algo como:

import matplotlib.pyplot as plt
import numpy

hl, = plt.plot([], [])

def update_line(hl, new_data):
    hl.set_xdata(numpy.append(hl.get_xdata(), new_data))
    hl.set_ydata(numpy.append(hl.get_ydata(), new_data))
    plt.draw()

Luego, cuando reciba datos del puerto serie, simplemente llame update_line.

Chris avatar Jun 08 '2012 07:06 Chris

Para hacer esto sin FuncAnimation (por ejemplo, si desea ejecutar otras partes del código mientras se produce el gráfico o desea actualizar varios gráficos al mismo tiempo), llamar por sí drawsolo no produce el gráfico (al menos con el backend qt).

Lo siguiente me funciona:

import matplotlib.pyplot as plt
plt.ion()
class DynamicUpdate():
    #Suppose we know the x range
    min_x = 0
    max_x = 10

    def on_launch(self):
        #Set up plot
        self.figure, self.ax = plt.subplots()
        self.lines, = self.ax.plot([],[], 'o')
        #Autoscale on unknown axis and known lims on the other
        self.ax.set_autoscaley_on(True)
        self.ax.set_xlim(self.min_x, self.max_x)
        #Other stuff
        self.ax.grid()
        ...

    def on_running(self, xdata, ydata):
        #Update data (with the new _and_ the old points)
        self.lines.set_xdata(xdata)
        self.lines.set_ydata(ydata)
        #Need both of these in order to rescale
        self.ax.relim()
        self.ax.autoscale_view()
        #We need to draw *and* flush
        self.figure.canvas.draw()
        self.figure.canvas.flush_events()

    #Example
    def __call__(self):
        import numpy as np
        import time
        self.on_launch()
        xdata = []
        ydata = []
        for x in np.arange(0,10,0.5):
            xdata.append(x)
            ydata.append(np.exp(-x**2)+10*np.exp(-(x-7)**2))
            self.on_running(xdata, ydata)
            time.sleep(1)
        return xdata, ydata

d = DynamicUpdate()
d()
Zah avatar Jun 17 '2014 19:06 Zah

A continuación se muestra una forma que permite eliminar puntos después de un cierto número de puntos trazados:

import matplotlib.pyplot as plt
# generate axes object
ax = plt.axes()

# set limits
plt.xlim(0,10) 
plt.ylim(0,10)

for i in range(10):        
     # add something to axes    
     ax.scatter([i], [i]) 
     ax.plot([i], [i+1], 'rx')

     # draw the plot
     plt.draw() 
     plt.pause(0.01) #is necessary for the plot to update for some reason

     # start removing points if you don't want all shown
     if i>2:
         ax.lines[0].remove()
         ax.collections[0].remove()
NDM avatar Mar 14 '2019 08:03 NDM