Actualización dinámica del gráfico en matplotlib
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:
- Borre el gráfico y vuelva a dibujarlo con todos los puntos.
- 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?
¿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 data
propiedad del objeto que se está dibujando, por lo que no requiere borrar la pantalla o la figura. La data
propiedad 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
.
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í draw
solo 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()
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()