|
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