Exercises: Animation and Interactivity

These exercises require a matplotlib environment with animation support. Some require pillow (for GIF) or ffmpeg (for MP4). Install these before starting.


Part A: Conceptual (6 problems)

A.1 ★☆☆ | Recall

Name the core matplotlib animation class and describe its main arguments.

Guidance `matplotlib.animation.FuncAnimation`. Arguments: the figure, the update function (called for each frame), `frames` (either an integer count or an iterable of frame values), `init_func` (optional, called once at the start), `interval` (ms between frames), and `blit` (True for efficient rendering). The update function takes a frame argument and modifies Artist properties, then returns the modified artists.

A.2 ★★☆ | Understand

Explain what blit=True does and what the trade-offs are.

Guidance `blit=True` enables blitting, a rendering optimization where only changed parts of the figure are redrawn each frame instead of the whole figure. Benefits: much faster rendering, especially for complex figures. Costs: only the artists returned from the update function are re-rendered, so static elements like titles and axis labels cannot change during the animation. Use `blit=True` when you only need to modify specific artists and do not need to change titles or axes. Use `blit=False` when you need to update static elements.

A.3 ★★☆ | Understand

When is animation the right visualization choice? When is it the wrong choice?

Guidance **Right** when the story involves a process unfolding over time (growing accumulations, converging simulations), flow (particles, traffic, waves), or step-by-step education. Also right when motion itself is part of the data (particle physics, fluid dynamics). **Wrong** when the data is fundamentally static (a snapshot survey, a histogram, a bar chart of totals), when the audience will not see the full animation (short attention contexts), when the medium cannot support animation (print, PDF, basic slide decks), or when motion distracts from the data rather than revealing it.

A.4 ★★☆ | Analyze

Explain change blindness and how it applies to animation design. What is the practical implication for writing update functions?

Guidance Change blindness is the perceptual phenomenon where the visual system fails to notice changes when too many elements change simultaneously or when transitions are abrupt. For animation, this means viewers cannot track many moving elements at once. Practical implication: change one thing per frame (add one data point, advance one step), keep the rest stable (static titles, axes, colors), and use smooth transitions. Update functions should modify the minimum number of artists per call.

A.5 ★★☆ | Analyze

Compare GIF export (PillowWriter) and MP4 export (FFMpegWriter). When would you use each?

Guidance **GIF**: portable (works in every browser, email client, and slide deck), requires only pillow (installed with matplotlib), but limited to 256 colors and produces large file sizes. Use for web sharing, social media, and contexts where playback reliability matters more than quality. **MP4**: high quality, small file size, rich features, but requires ffmpeg installed separately. Use for serious presentation, publication, and any context where ffmpeg is available. For most everyday matplotlib animation, GIF is the safer default; MP4 is for higher-quality output.

A.6 ★★★ | Evaluate

The chapter says matplotlib animation is "less polished" than dedicated tools like Plotly. Defend or critique the chapter's recommendation that matplotlib is still the right choice for some animation tasks.

Guidance The chapter's argument: matplotlib animation is adequate for simple cases where you are already using matplotlib, want to export to a file, and do not need rich web-based interactivity. This is true for scientific publication, research presentations, and static-output contexts. For web-based interactive dashboards, Plotly and Bokeh are better. The critique would be that matplotlib's animation API is awkward (FuncAnimation requires explicit update functions), the performance is limited, and the learning curve for simple cases is no shorter than Plotly's. The chapter's recommendation is pragmatic: use matplotlib for static-output animations when you are already in the matplotlib ecosystem; switch to dedicated tools for interactive needs.

Part B: Applied (8 problems)

B.1 ★★☆ | Apply

Create a line chart animation that draws y = sin(x) for x from 0 to 10, one point at a time. Save as a GIF.

Guidance
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

fig, ax = plt.subplots(figsize=(10, 5))
x = np.linspace(0, 10, 100)
ax.set_xlim(0, 10)
ax.set_ylim(-1.5, 1.5)
ax.set_title("Sine Wave")

line, = ax.plot([], [], color="#d62728", linewidth=2)

def init():
    line.set_data([], [])
    return (line,)

def update(frame):
    line.set_data(x[:frame], np.sin(x[:frame]))
    return (line,)

ani = FuncAnimation(fig, update, frames=len(x) + 1, init_func=init, interval=50, blit=True)
ani.save("sine.gif", writer="pillow", fps=20)

B.2 ★★☆ | Apply

Modify B.1 to also display a moving dot at the current end of the line. The dot should follow the tip as the line grows.

Guidance Add a second artist for the dot:
line, = ax.plot([], [], color="#d62728", linewidth=2)
dot, = ax.plot([], [], "o", color="#d62728", markersize=8)

def update(frame):
    line.set_data(x[:frame], np.sin(x[:frame]))
    if frame > 0:
        dot.set_data([x[frame-1]], [np.sin(x[frame-1])])
    return (line, dot)
Both artists are returned from the update function so they update together.

B.3 ★★☆ | Apply

Create an animated climate chart using synthetic data. The title should show the current year and update as the animation progresses. Save as GIF.

Guidance Because the title changes, you must set `blit=False`:
years = np.arange(1880, 2025)
temperature = -0.3 + (years - 1880) * 0.01 + np.random.randn(len(years)) * 0.15

fig, ax = plt.subplots(figsize=(12, 5))
ax.set_xlim(1880, 2024)
ax.set_ylim(-0.8, 1.6)

line, = ax.plot([], [], color="#d62728", linewidth=2)

def update(frame):
    line.set_data(years[:frame], temperature[:frame])
    if frame > 0:
        ax.set_title(f"Global Temperature, 1880-{years[frame-1]}")
    return (line,)

ani = FuncAnimation(fig, update, frames=len(years) + 1, interval=40, blit=False)
ani.save("climate.gif", writer="pillow", fps=25)

B.4 ★★☆ | Apply

Create an animated scatter plot where 100 points move randomly each frame (use np.random.randn with a different seed for each frame).

Guidance
fig, ax = plt.subplots(figsize=(8, 8))
ax.set_xlim(-5, 5)
ax.set_ylim(-5, 5)

sc = ax.scatter([], [], alpha=0.7, s=50)

def update(frame):
    np.random.seed(frame)
    x = np.random.randn(100)
    y = np.random.randn(100)
    sc.set_offsets(np.c_[x, y])
    return (sc,)

ani = FuncAnimation(fig, update, frames=60, interval=100, blit=True)
ani.save("random_scatter.gif", writer="pillow", fps=10)

B.5 ★★☆ | Apply

Create an animated bar chart with 5 bars whose heights update randomly each frame.

Guidance
fig, ax = plt.subplots(figsize=(8, 5))
categories = ["A", "B", "C", "D", "E"]
bars = ax.bar(categories, [0]*5, color="steelblue")
ax.set_ylim(0, 100)

def update(frame):
    np.random.seed(frame)
    heights = np.random.rand(5) * 100
    for bar, h in zip(bars, heights):
        bar.set_height(h)
    return bars

ani = FuncAnimation(fig, update, frames=40, interval=150, blit=False)
ani.save("bars.gif", writer="pillow", fps=7)
Note: `blit=False` because updating multiple Rectangle heights can be finicky with blit.

B.6 ★★★ | Apply

Create a hover tooltip on a scatter plot. When the user hovers over a point, display an annotation with its index and coordinates. Requires %matplotlib widget in Jupyter or a GUI backend.

Guidance See Section 15.5 of the chapter for the complete pattern. The key steps: create an initially-invisible annotation, register a `motion_notify_event` handler, check whether the cursor is over a scatter point with `sc.contains(event)`, and update the annotation's position and text accordingly.

B.7 ★★★ | Apply

Save the same animation in three different formats: GIF (with pillow), MP4 (with ffmpeg, if available), and HTML5 video. Compare the file sizes.

Guidance
ani.save("animation.gif", writer="pillow", fps=20)
# If ffmpeg is installed:
ani.save("animation.mp4", writer="ffmpeg", fps=30, dpi=150)

# In a Jupyter notebook:
from IPython.display import HTML
HTML(ani.to_html5_video())
Typical relative sizes: MP4 is smallest, HTML5 video (embedded in HTML) is medium, GIF is largest.

B.8 ★★★ | Create

Build an animation that combines a line chart and a moving annotation. As the line grows, the annotation should point to the current maximum value seen so far.

Guidance
fig, ax = plt.subplots(figsize=(12, 5))
x = np.linspace(0, 20, 200)
y = np.sin(x) + 0.1 * x

ax.set_xlim(0, 20)
ax.set_ylim(-1, 3)

line, = ax.plot([], [], color="#1f77b4", linewidth=1.5)
max_dot, = ax.plot([], [], "o", color="#d62728", markersize=10)
max_annot = ax.annotate("", xy=(0, 0), xytext=(10, 10), textcoords="offset points",
                        fontsize=10, color="#d62728")

def update(frame):
    line.set_data(x[:frame], y[:frame])
    if frame > 0:
        max_idx = np.argmax(y[:frame])
        max_x = x[max_idx]
        max_y = y[max_idx]
        max_dot.set_data([max_x], [max_y])
        max_annot.set_text(f"Max: {max_y:.2f}")
        max_annot.xy = (max_x, max_y)
    return (line, max_dot, max_annot)

ani = FuncAnimation(fig, update, frames=len(x)+1, interval=30, blit=True)

Part C: Synthesis (4 problems)

C.1 ★★★ | Analyze

Take an existing static chart from an earlier chapter's exercises and describe how it could be animated. Identify what the animation would reveal and what it would obscure.

Guidance Most static charts can be animated by revealing data progressively. A line chart becomes "drawing year by year." A bar chart becomes "bars growing from zero to their final heights." A scatter plot becomes "points appearing one at a time." For each, the animation adds a sense of process (how did we get here?) but obscures the final state until the animation completes. Decide whether the process or the final state is more important to communicate.

C.2 ★★★ | Evaluate

The chapter argues that matplotlib is appropriate for simple animations but that Plotly and Bokeh are better for complex interactivity. For a specific use case — an interactive exploration tool for a time-series dataset — which tool would you choose and why?

Guidance For an interactive exploration tool with hover, zoom, selection, and linked views, Plotly or Bokeh is usually the right choice. They are designed for web-based interactivity and handle the specific features (hover tooltips, cross-filtering, dropdown selectors) more gracefully than matplotlib. matplotlib would work but would require more code and deliver less interactivity. For a static export (a GIF or MP4 showing the same data), matplotlib is the right choice because Plotly's animation-to-file export is less mature.

C.3 ★★★ | Create

Design an animation (on paper, not in code) that tells a specific data story you care about. Describe what each frame would show, how long the animation would run, and what the viewer should take away.

Guidance The exercise is to practice deliberate animation design. Specific details: what does frame 0 show? What does frame 100 show? What is the interval? What is the total duration? What is the final frame (the static version that remains)? A good design can be implemented in matplotlib or any other tool; the point is that the design precedes the code.

C.4 ★★★ | Create

Build an interactive tool that displays a scatter plot of your own data and shows a hover tooltip with information about the nearest point. Use ipympl or a GUI backend.

Guidance Combine the hover tooltip pattern from Section 15.5 with your own data. The result should be a Jupyter notebook cell or a script that opens an interactive window where hovering over points shows their metadata. This is the foundational pattern for exploratory data analysis with matplotlib.

These exercises are hands-on. Animation and interactivity require runtime experimentation — the static output of matplotlib is only the starting point. Do at least four of the Part B exercises to develop real fluency with FuncAnimation.