|
3 | 3 | Animated histogram |
4 | 4 | ================== |
5 | 5 |
|
6 | | -Use a path patch to draw a bunch of rectangles for an animated histogram. |
| 6 | +Use histogram's `.BarContainer` to draw a bunch of rectangles for an animated |
| 7 | +histogram. |
7 | 8 | """ |
8 | 9 |
|
9 | 10 | import numpy as np |
10 | 11 |
|
11 | 12 | import matplotlib.pyplot as plt |
12 | | -import matplotlib.patches as patches |
13 | | -import matplotlib.path as path |
14 | 13 | import matplotlib.animation as animation |
15 | 14 |
|
16 | 15 | # Fixing random state for reproducibility |
17 | 16 | np.random.seed(19680801) |
| 17 | +# Fixing bin edges |
| 18 | +HIST_BINS = np.linspace(-4, 4, 100) |
18 | 19 |
|
19 | 20 | # histogram our data with numpy |
20 | 21 | data = np.random.randn(1000) |
21 | | -n, bins = np.histogram(data, 100) |
22 | | - |
23 | | -# get the corners of the rectangles for the histogram |
24 | | -left = bins[:-1] |
25 | | -right = bins[1:] |
26 | | -bottom = np.zeros(len(left)) |
27 | | -top = bottom + n |
28 | | -nrects = len(left) |
29 | | - |
30 | | -############################################################################### |
31 | | -# Here comes the tricky part -- we have to set up the vertex and path codes |
32 | | -# arrays using `.Path.MOVETO`, `.Path.LINETO` and `.Path.CLOSEPOLY` for each |
33 | | -# rect. |
34 | | -# |
35 | | -# * We need 1 ``MOVETO`` per rectangle, which sets the initial point. |
36 | | -# * We need 3 ``LINETO``'s, which tell Matplotlib to draw lines from |
37 | | -# vertex 1 to vertex 2, v2 to v3, and v3 to v4. |
38 | | -# * We then need one ``CLOSEPOLY`` which tells Matplotlib to draw a line from |
39 | | -# the v4 to our initial vertex (the ``MOVETO`` vertex), in order to close the |
40 | | -# polygon. |
41 | | -# |
42 | | -# .. note:: |
43 | | -# |
44 | | -# The vertex for ``CLOSEPOLY`` is ignored, but we still need a placeholder |
45 | | -# in the ``verts`` array to keep the codes aligned with the vertices. |
46 | | -nverts = nrects * (1 + 3 + 1) |
47 | | -verts = np.zeros((nverts, 2)) |
48 | | -codes = np.full(nverts, path.Path.LINETO) |
49 | | -codes[0::5] = path.Path.MOVETO |
50 | | -codes[4::5] = path.Path.CLOSEPOLY |
51 | | -verts[0::5, 0] = left |
52 | | -verts[0::5, 1] = bottom |
53 | | -verts[1::5, 0] = left |
54 | | -verts[1::5, 1] = top |
55 | | -verts[2::5, 0] = right |
56 | | -verts[2::5, 1] = top |
57 | | -verts[3::5, 0] = right |
58 | | -verts[3::5, 1] = bottom |
| 22 | +n, _ = np.histogram(data, HIST_BINS) |
59 | 23 |
|
60 | 24 | ############################################################################### |
61 | 25 | # To animate the histogram, we need an ``animate`` function, which generates |
62 | | -# a random set of numbers and updates the locations of the vertices for the |
63 | | -# histogram (in this case, only the heights of each rectangle). ``patch`` will |
64 | | -# eventually be a `.Patch` object. |
65 | | -patch = None |
| 26 | +# a random set of numbers and updates the heights of rectanges. We utilize a |
| 27 | +# python closure to track an instance of `.BarContainer` whose `.Rectangle` |
| 28 | +# patches we shall update. |
66 | 29 |
|
67 | 30 |
|
68 | | -def animate(i): |
69 | | - # simulate new data coming in |
70 | | - data = np.random.randn(1000) |
71 | | - n, bins = np.histogram(data, 100) |
72 | | - top = bottom + n |
73 | | - verts[1::5, 1] = top |
74 | | - verts[2::5, 1] = top |
75 | | - return [patch, ] |
| 31 | +def prepare_animation(bar_container): |
| 32 | + |
| 33 | + def animate(frame_number): |
| 34 | + # simulate new data coming in |
| 35 | + data = np.random.randn(1000) |
| 36 | + n, _ = np.histogram(data, HIST_BINS) |
| 37 | + for count, rect in zip(n, bar_container.patches): |
| 38 | + rect.set_height(count) |
| 39 | + return bar_container.patches |
| 40 | + return animate |
76 | 41 |
|
77 | 42 | ############################################################################### |
78 | | -# And now we build the `.Path` and `.Patch` instances for the histogram using |
79 | | -# our vertices and codes. We add the patch to the `~.axes.Axes` instance, and |
80 | | -# setup the `.FuncAnimation` with our ``animate`` function. |
81 | | -fig, ax = plt.subplots() |
82 | | -barpath = path.Path(verts, codes) |
83 | | -patch = patches.PathPatch( |
84 | | - barpath, facecolor='green', edgecolor='yellow', alpha=0.5) |
85 | | -ax.add_patch(patch) |
| 43 | +# Using :func:`~matplotlib.pyplot.hist` allows us to get an instance of |
| 44 | +# `.BarContainer`, which is a collection of `.Rectangle` instances. Calling |
| 45 | +# ``prepare_animation`` will define ``animate`` function working with supplied |
| 46 | +# `.BarContainer`, all this is used to setup `.FuncAnimation`. |
86 | 47 |
|
87 | | -ax.set_xlim(left[0], right[-1]) |
88 | | -ax.set_ylim(bottom.min(), top.max()) |
| 48 | +fig, ax = plt.subplots() |
| 49 | +_, _, bar_container = ax.hist(data, HIST_BINS, lw=1, |
| 50 | + ec="yellow", fc="green", alpha=0.5) |
| 51 | +ax.set_ylim(top=55) # set safe limit to ensure that all data is visible. |
89 | 52 |
|
90 | | -ani = animation.FuncAnimation(fig, animate, 50, repeat=False, blit=True) |
| 53 | +ani = animation.FuncAnimation(fig, prepare_animation(bar_container), 50, |
| 54 | + repeat=False, blit=True) |
91 | 55 | plt.show() |
0 commit comments