2012-06-08 20 views
72

Estoy haciendo una aplicación en Python que recopila datos de un puerto serie y traza un gráfico de los datos recopilados con respecto al tiempo de llegada. La hora de llegada de los datos es incierta. Quiero que la trama se actualice cuando se reciban los datos. Busqué cómo hacer esto y encontré dos métodos:Parcela de actualización dinámica en matplotlib

  1. Borre la gráfica y vuelva a dibujar la gráfica con todos los puntos nuevamente.
  2. Anima la trama cambiándola después de un intervalo determinado.

No prefiero la primera ya que 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 diagrama se actualice solo cuando se reciban los datos.

¿Hay alguna manera de actualizar la gráfica simplemente añadiéndole más puntos solo cuando se reciben los datos?

+1

duplicado posible de [trazado en tiempo real en ciclo while con matplotlib] (http://stackoverflow.com/questions/11874767/real-time-plotting-in-while-loop-with-matplotlib) –

Respuesta

92

¿Hay una manera en la que puedo actualizar la trama simplemente añadiendo más puntos [s] a ella ...

Hay una serie de formas de animación de datos en matplotlib, dependiendo de la versión que tengas ¿Has visto los ejemplos matplotlib cookbook? Además, consulte la más moderna animation examples en la documentación de matplotlib. Finalmente, el animation API define una función FuncAnimation que anima una función en el tiempo. Esta función podría ser simplemente la función que usa para adquirir sus datos.

Cada método básicamente establece la propiedad data del objeto que se está dibujando, por lo que no es necesario borrar la pantalla o la figura. La propiedad data puede simplemente extenderse, por lo que puede mantener los puntos anteriores y seguir agregando a su línea (o imagen o lo que sea que esté dibujando).

Dado que usted dice que su hora de llegada de datos es incierta su mejor apuesta es, probablemente, sólo para 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 se reciben datos desde el puerto serie acaba de llamar update_line.

+0

¡Finalmente! He estado buscando una respuesta a este +1 :) ¿Cómo hacemos la reescala del diagrama automáticamente? ax.set_autoscale_on (True) no parece funcionar. –

+11

Encontré la respuesta: llame ax.relim() luego ax.autoscale_view() después de actualizar los datos pero antes de llamar a plt.draw() –

+0

Esto no funciona como esperaba –

19

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

Los siguientes obras para mí:

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

¡Sí! ¡Finalmente una solución que funciona con Spyder! Lo que me faltaba era gcf(). Canvas.flush_events() después del comando draw() -. – np8

1

sé que estoy tarde para responder a esta pregunta, pero para su problema se puede mirar en el paquete de "palanca de mando". Lo diseñé para trazar una secuencia de datos desde el puerto serie, pero funciona para cualquier transmisión. También permite el registro de texto interactivo o el trazado de imágenes (además del trazado gráfico). No es necesario hacer sus propios bucles en un hilo separado, el paquete se encarga de ello, simplemente proporcione la frecuencia de actualización que desee. Además, el terminal permanece disponible para monitorear comandos durante el trazado.Ver http://www.github.com/ceyzeriat/joystick/ o https://pypi.python.org/pypi/joystick (PIP utilización instalar la palanca de mando para instalar)

basta con sustituir np.random.random() por su punto de leer los datos reales desde el puerto serie en el siguiente código:

import joystick as jk 
import numpy as np 
import time 

class test(jk.Joystick): 
    # initialize the infinite loop decorator 
    _infinite_loop = jk.deco_infinite_loop() 

    def _init(self, *args, **kwargs): 
     """ 
     Function called at initialization, see the doc 
     """ 
     self._t0 = time.time() # initialize time 
     self.xdata = np.array([self._t0]) # time x-axis 
     self.ydata = np.array([0.0]) # fake data y-axis 
     # create a graph frame 
     self.mygraph = self.add_frame(jk.Graph(name="test", size=(500, 500), pos=(50, 50), fmt="go-", xnpts=10000, xnptsmax=10000, xylim=(None, None, 0, 1))) 

    @_infinite_loop(wait_time=0.2) 
    def _generate_data(self): # function looped every 0.2 second to read or produce data 
     """ 
     Loop starting with the simulation start, getting data and 
    pushing it to the graph every 0.2 seconds 
     """ 
     # concatenate data on the time x-axis 
     self.xdata = jk.core.add_datapoint(self.xdata, time.time(), xnptsmax=self.mygraph.xnptsmax) 
     # concatenate data on the fake data y-axis 
     self.ydata = jk.core.add_datapoint(self.ydata, np.random.random(), xnptsmax=self.mygraph.xnptsmax) 
     self.mygraph.set_xydata(t, self.ydata) 

t = test() 
t.start() 
t.stop() 
Cuestiones relacionadas