Reputation: 2785
I'm trying to animate a histogram over time, and so far the code I have is the following one:
import matplotlib.pyplot as plt
import numpy as np
import time
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
alphab = ['A', 'B', 'C', 'D', 'E', 'F']
frequencies = [1, 44, 12, 11, 2, 10]
pos = np.arange(len(alphab))
width = 1.0 # gives histogram aspect to the bar diagram
ax.set_xticks(pos + (width / 2))
ax.set_xticklabels(alphab)
for bin_idx in np.linspace(0,1000000,100000000):
t = time.time()
#Here we just change the first bin, so it increases through the animation.
frequencies[0] = bin_idx
line1 =plt.bar(pos, frequencies, width, color='r')
plt.draw()
elapsed = time.time() - t
print elapsed
The code works, but the outputs shows how after some iterations it just becomes way slower than at the beginning. Is there a way to speed things up, we want to update this in real time, and the process in which it runs is pretty fast.
Also, it is important to notice, that I do not want a post processing animation, we want real time updates, so the histogram animation example was not working for this particular process.
Thanks
Upvotes: 3
Views: 4478
Reputation: 880259
If you have a newer version of Matplotlib there is an animations.FuncAnimation
class which can help reduce some of the boiler-plate code. (See this page for an example.) It is pretty fast (~ 52 frames per second):
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import timeit
clock = timeit.default_timer
fig, ax = plt.subplots()
alphab = ['A', 'B', 'C', 'D', 'E', 'F']
frequencies = [1, 44, 12, 11, 2, 10]
pos = np.arange(len(alphab))
width = 1.0 # gives histogram aspect to the bar diagram
ax.set_xticks(pos + (width / 2))
ax.set_xticklabels(alphab)
rects = plt.bar(pos, frequencies, width, color='r')
start = clock()
def animate(arg, rects):
frameno, frequencies = arg
for rect, f in zip(rects, frequencies):
rect.set_height(f)
print("FPS: {:.2f}".format(frameno / (clock() - start)))
def step():
for frame, bin_idx in enumerate(np.linspace(0,1000000,100000000), 1):
#Here we just change the first bin, so it increases through the animation.
frequencies[0] = bin_idx
yield frame, frequencies
ani = animation.FuncAnimation(fig, animate, step, interval=10,
repeat=False, blit=False, fargs=(rects,))
plt.show()
If you don't have a newer version of Matplotlib, here is the older way to do it. It is slightly slower (~ 45 frames per second):
Don't call plt.bar
with each iteration of the loop. Instead, call it just once, save the rects
return value, and then call set_height
to modify the height of those rects
on subsequent iterations of the loop. This trick (and others) is explained in the Matplotlib Animations Cookbook.
import sys
import matplotlib as mpl
mpl.use('TkAgg') # do this before importing pyplot
import matplotlib.pyplot as plt
import numpy as np
import timeit
clock = timeit.default_timer
fig, ax = plt.subplots()
alphab = ['A', 'B', 'C', 'D', 'E', 'F']
frequencies = [1, 44, 12, 11, 2, 10]
pos = np.arange(len(alphab))
width = 1.0 # gives histogram aspect to the bar diagram
ax.set_xticks(pos + (width / 2))
ax.set_xticklabels(alphab)
def animate():
start = clock()
rects = plt.bar(pos, frequencies, width, color='r')
for frameno, bin_idx in enumerate(np.linspace(0,1000000,100000000), 2):
#Here we just change the first bin, so it increases through the animation.
frequencies[0] = bin_idx
# rects = plt.bar(pos, frequencies, width, color='r')
for rect, f in zip(rects, frequencies):
rect.set_height(f)
fig.canvas.draw()
print("FPS: {:.2f}".format(frameno / (clock() - start)))
win = fig.canvas.manager.window
win.after(1, animate)
plt.show()
For comparison, adding plt.clf
to your original code, on my machine reaches about 12 frames per second.
Some comments about timing:
You won't get accurate measurements by calculating the very small time differences with each pass through the loop. The time resolution of time.time()
-- at least on my computer -- is not great enough. You'll get more accurate measurements by measuring one starting time and calculating the large time difference between the start time and the current time, and then dividing by the number of frames.
I also changed time.time
to timeit.default_timer
. The two are the same on Unix computers, but timeit.default_timer
is set to time.clock
on Windows machines. Thus timeit.default_timer
chooses the more accurate timer for each platform.
Upvotes: 3
Reputation: 4928
I think that your code gets slower because you are not clearing the figure, so you are replotting histograms on top of each other for every iteration.
Adding a plt.clf()
call before your line1 = ...
clears the existing graph.
Upvotes: 1