Dynamically updated plot in matplotlib

I am making a Python application that collects data from a serial port and displays a graph of the collected data by arrival time. The arrival time for the data is not defined. I want the plot updated when data is received. I searched how to do this and found two methods:

  • Clear the graph and draw the graph again with all the points.
  • Animate the chart by changing it after a certain interval.

I do not prefer the first, because the program starts and collects data for a long time (for example, a day), and redrawing the graph will be rather slow. The second option is also not preferred, since the arrival time of the data is indefinite, and I want the schedule to be updated only when the data is received.

Is there a way I can update a chart by simply adding more points to it only when the data is received?

+72
python matplotlib tkinter
Jun 08 2018-12-12T00:
source share
3 answers

Is there a way in which I can update the chart by simply adding more points [s] to it ...

There are several ways to animate data in matplotlib, depending on the version you have. Have you seen examples of matplotlib cookbook ? Also, check out more modern animation examples in the matplotlib documentation. Finally, the animation API defines a FuncAnimation function that animates the function over time. This function may just be the function you use to retrieve your data.

Each method basically sets the data property of the object that is being drawn, so it does not require cleaning the screen or drawing. The data property can simply be expanded so that you can save the previous points and simply add to your line (either the image or what you draw).

Given that you say that the arrival time of your data is unknown, your best bet is probably just to do something like:

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

Then, when you receive data from the serial port, just call update_line .

+91
Jun 08 2018-12-12T00:
source share

To do this without FuncAnimation (for example, if you want to execute other parts of the code while creating a chart or want to update several charts at the same time), calling draw only does not create a plot (at least using a qt backend).

The following works for me:

 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() 
+18
Jun 17 '14 at 19:44
source share

I know that I am late to answer this question, but for your problem you can look in the joystick package. I designed it to build a data stream from a serial port, but it works for any stream. It also allows for interactive logging or imaging (in addition to plotting). No need to make your own loops in a separate thread, the package will take care of this, just give the refresh rate you want. In addition, the terminal remains available for command control when plotting. See http://www.github.com/ceyzeriat/joystick/ or https://pypi.python.org/pypi/joystick (use the pip install joystick command to install)

Just replace np.random.random () with your real data point read from the serial port in the code below:

 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() 
+1
Sep 28 '16 at 22:41
source share



All Articles