简体   繁体   中英

plotting dynamic data using matplotlib

I'm writing an application do display data that changes dynamically (the data being read from a socket).

As a dummy case, I try to draw a sine with an amplitude multiplied by 1.1 each second:

import numpy as np
import matplotlib.pyplot as plt
import time

x = np.arange(0, 10, 0.1);
y = np.sin(x)


for i in xrange(100):
    plt.plot(x, y)
    time.sleep(1)
    y=y*1.1

This obviously not the way do it, but it shows my intentions.

How can it be done correctly?

EDIT: The following is the traceback output of the code suggested in @mskimm answer:

plt.show() #Exception in thread Thread-2:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 552, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 505, in run
    self.__target(*self.__args, **self.__kwargs)
  File "<ipython-input-5-ed773f8e3e84>", line 7, in update
    plt.draw()
  File "/usr/lib/pymodules/python2.7/matplotlib/pyplot.py", line 466, in draw
    get_current_fig_manager().canvas.draw()
  File "/usr/lib/pymodules/python2.7/matplotlib/backends/backend_tkagg.py", line 240, in draw
    tkagg.blit(self._tkphoto, self.renderer._renderer, colormode=2)
  File "/usr/lib/pymodules/python2.7/matplotlib/backends/tkagg.py", line 12, in blit
    tk.call("PyAggImagePhoto", photoimage, id(aggimage), colormode, id(bbox_array))
RuntimeError: main thread is not in main loop

EDIT 2:

it turns out that same code works when run in qtconsole... (any idea why?) How ever, each print rescaling to plot, so the "animation effect" is missing. I try to use plt.autoscale_view(False,False,False) but that just caused no plot at all.

There are better ways to do this using thematplotlib animation API , but here's a quick and dirty approach:

import numpy as np
import matplotlib.pyplot as plt

x = np.arange(0, 10, 0.1)
y = np.sin(x)

plt.ion()
ax = plt.gca()
ax.set_autoscale_on(True)
line, = ax.plot(x, y)

for i in xrange(100):
    line.set_ydata(y)
    ax.relim()
    ax.autoscale_view(True,True,True)
    plt.draw()
    y=y*1.1
    plt.pause(0.1)

The key steps are:

  1. Turn on interactive mode with plt.ion() .
  2. Keep track of the line(s) you want to update, and overwrite their data instead of calling plot again.
  3. Give Python some time to update the plot window by calling plt.pause .

I included the code to autoscale the viewport, but that's not strictly necessary.

Is it hard to apply animated ? How about using thread that updates the figure. Even though plt.show() is blocking, the thread will update the figure.

import numpy as np
import matplotlib.pyplot as plt
import time
import threading

def update(x, y):
    for i in xrange(100):
        # clear
        plt.clf()
        plt.plot(x, y)
        # draw figure
        plt.draw()
        time.sleep(1)
        y=y*1.1

x = np.arange(0, 10, 0.1);
y = np.sin(x)
plt.plot(x, y)

# use thread
t = threading.Thread(target=update, args=(x, y))
t.start()

plt.show() # blocking but thread will update figure.

Another solution is to redraw the plot from scratch. Surely it's slower than updating it point by point, but if you have not many points, the overhead is negligible.

from IPython import display

def dynamic_plot(X,Y, figsize=[10,5], max_x=None, min_y=None, max_y=None):
    '''plots dependency between X and Y dynamically: after each call current graph is redrawn'''
    gcf().set_size_inches(figsize)
    cla()
    plot(X,Y)
    if max_x: 
        plt.gca().set_xlim(right=max_x)
    if min_y: 
        plt.gca().set_ylim(bottom=min_y)
    if max_y: 
        plt.gca().set_ylim(top=max_y)

    display.display(gcf())
    display.clear_output(wait=True)  

And the demo use for jupyter notebook:

import time

X=[]
Y=[]
for i in range(10):
    X.append(i)
    Y.append(i**.5)
    dynamic_plot(X,Y,[14,10], max_x=10, max_y=4)  
    time.sleep(0.3)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM