Dynamically updating plot in matplotlib

前端 未结 4 564
难免孤独
难免孤独 2020-11-22 08:31

I am making an application in Python which collects data from a serial port and plots a graph of the collected data against arrival time. The time of arrival for the data is

相关标签:
4条回答
  • 2020-11-22 08:59

    Is there a way in which I can update the plot just by adding more point[s] to it...

    There are a number of ways of animating data in matplotlib, depending on the version you have. Have you seen the matplotlib cookbook examples? Also, check out the more modern animation examples in the matplotlib documentation. Finally, the animation API defines a function FuncAnimation which animates a function in time. This function could just be the function you use to acquire your data.

    Each method basically sets the data property of the object being drawn, so doesn't require clearing the screen or figure. The data property can simply be extended, so you can keep the previous points and just keep adding to your line (or image or whatever you are drawing).

    Given that you say that your data arrival time is uncertain 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.

    0 讨论(0)
  • 2020-11-22 09:13

    I know I'm late to answer this question, but for your issue you could look into the "joystick" package. I designed it for plotting a stream of data from the serial port, but it works for any stream. It also allows for interactive text logging or image plotting (in addition to graph plotting). No need to do your own loops in a separate thread, the package takes care of it, just give the update frequency you wish. Plus the terminal remains available for monitoring commands while plotting. See http://www.github.com/ceyzeriat/joystick/ or https://pypi.python.org/pypi/joystick (use pip install joystick to install)

    Just replace np.random.random() by 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()
    
    0 讨论(0)
  • 2020-11-22 09:15

    In order to do this without FuncAnimation (eg you want to execute other parts of the code while the plot is being produced or you want to be updating several plots at the same time), calling draw alone does not produce the plot (at least with the 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()
    
    0 讨论(0)
  • 2020-11-22 09:16

    Here is a way which allows to remove points after a certain number of points plotted:

    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()
    
    0 讨论(0)
提交回复
热议问题