This tip is about how to update matplotlib plot, it is based on this great tutorial: Speeding up Matplotlib
I learned two ways of updating matplotlib plot, both require first manually change the content of objects that to be updated. For example, lineObj.set_ydata()
for line object, vlinesObj.set_paths()
for vlines object. After that, you can do either of the following two things to update the plot:
-
simpler but slower: use
canvas.draw()
to redraw every objects inside the canvas. -
faster but require more knowledge of how things work underhood: since
canvas.draw()
may do some repetitive work of redrawing some objects like axis, legends which may don't need to be updated, there should be many ways to lower the amount of work in this updation step if you are a master of matplotlib. The way i learned is first restore canvas from a prestored background usingcanvas.restore_region()
, and then only update the objects i want to update usingax.draw_artist(obj)
rather than a whole canvas, and at last usecanvas.update()
to update the canvas.
Test:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | #! /usr/bin/env python
import matplotlib
import matplotlib.pyplot as plt
matplotlib.style.use("ggplot")
import numpy as np
import time
def newfig(l):
fig, ax = plt.subplots(figsize=(10,8))
fig.show()
fig.tight_layout()
#obj, = ax.plot(np.random.rand(l)) # normal line plot
obj = ax.vlines(np.arange(l), [0], np.ones(l), alpha=0.5, linewidths=400./l) # vlines return line collection
obj.remove() # if using restore from bg method, bg should not contain lines; comment this line if use canvas.draw() method
fig.canvas.draw()
bg = fig.canvas.copy_from_bbox(ax.bbox)
return fig, ax, obj, bg
class LiveFig:
def __init__(self, l):
self.fig, self.ax, self.obj, self.bg = newfig(l)
def update(self, h):
## update data
#self.obj.set_ydata(h) # for normal line plot
segs = np.zeros((len(h), 2, 2)) # for vlines
segs[:, :, 0] = np.arange(len(h))[:, np.newaxis]
segs[:, 1, 1] = h
self.obj.set_paths(segs)
############################################################
## way1: update canvas by default, if use this then should not do obj.remove() above
# self.fig.canvas.draw()
############################################################
############################################################
## way2: update canvas partially, faster but fig cannot be resized, if use this
## then should do obj.remove() above before saving background
self.fig.canvas.restore_region(self.bg)
self.ax.draw_artist(self.obj)
self.fig.canvas.update()
############################################################
self.fig.canvas.flush_events() # entering Qt event loop, both methods require this
if __name__ == "__main__":
l1 = 300
fig1 = LiveFig(l1)
l2 = 400
fig2 = LiveFig(l2)
tstart = time.time()
numfrm = 0
tdelta = 10
while time.time() - tstart < tdelta:
h1 = np.random.rand(l1)
fig1.update(h1)
h2 = np.random.rand(l2)
fig2.update(h2)
numfrm += 1
print "fps: ", numfrm / tdelta
raw_input("Press any key to exit.")
|
Example: