Exercises: Specialized matplotlib Charts

Specialized chart types are hands-on. Each exercise produces a specific chart type that extends the Chapter 11 vocabulary.


Part A: Conceptual (6 problems)

A.1 ★☆☆ | Recall

List the specialized chart types covered in this chapter and state the data shape each is designed for.

Guidance Heatmaps (ax.imshow, ax.pcolormesh) for 2D tabular data. Contour plots (ax.contour, ax.contourf) for continuous 2D surfaces. Polar plots (projection="polar") for cyclical data. Error bars (ax.errorbar) for individual estimates with uncertainty. Fill-between (ax.fill_between) for confidence bands on continuous functions. Quiver and streamplot (ax.quiver, ax.streamplot) for vector fields. Plus violin plots, stem plots, and stack plots for less common but specific needs.

A.2 ★★☆ | Understand

Explain the difference between ax.imshow and ax.pcolormesh for heatmaps. When would you use each?

Guidance `ax.imshow` assumes a regular grid where all cells are the same size. It takes a 2D array and displays it as colored cells. Faster to render and simpler to call. `ax.pcolormesh` takes explicit edge coordinates (x_edges, y_edges) and supports irregular grids with unequal row heights or column widths. Use imshow for most heatmaps (most data is on regular grids); use pcolormesh when you have irregular cells or want explicit edge coordinates.

A.3 ★★☆ | Understand

When is a diverging colormap the right choice, and when is it wrong? Give one example of each.

Guidance Diverging colormaps are right when the data has a meaningful midpoint, with values extending in both directions. Example: temperature anomalies (positive and negative from baseline) or correlation matrices (between -1 and +1). They are wrong for data that only goes in one direction. Example: counts of events (all non-negative) — using a diverging colormap would waste the "negative" side of the palette on a range that does not exist. For counts, use a sequential colormap instead.

A.4 ★★☆ | Analyze

The chapter argues that polar plots should only be used for cyclical data. What happens if you use a polar plot for linear (non-cyclical) data? Give a specific example of a mistake.

Guidance Polar plots wrap data around a circle. If your data is linear (e.g., year-over-year revenue for 10 years), wrapping it in a polar plot puts "year 1" and "year 10" right next to each other, creating the false impression of continuity or similarity when there is none. Example: a polar bar chart of 10 years of revenue would show year 1 at the top, years 2-9 going clockwise, and year 10 just before year 1 again — visually implying that year 10 is "close to" year 1, which is meaningless. Use a standard bar chart instead.

A.5 ★★☆ | Understand

Explain why the chapter recommends setting symmetric vmin/vmax for diverging colormaps on anomaly data. What goes wrong if you let matplotlib autoscale?

Guidance Diverging colormaps (RdBu_r, coolwarm) have a neutral midpoint that represents "zero" or "no deviation." For the midpoint to align with zero in the data, you need symmetric vmin/vmax. If your data ranges from -1 to +3 and you autoscale, the neutral color ends up at +1 (the midpoint of the data), not at zero. The reader reads +1 as "neutral" and misinterprets the values. Fix: set `vmin=-abs_max, vmax=abs_max` where abs_max is the maximum absolute value in the data.

A.6 ★★★ | Evaluate

The chapter says "specialized chart types do not replace the Chapter 11 essentials; they extend the vocabulary." Defend this position. What would go wrong if every chart maker replaced their line charts with polar plots and their bar charts with radar charts?

Guidance The essentials (line, bar, scatter, histogram, box plot) are the most effective chart types for their respective question types and data shapes. Specialized types are optimizations for specific data shapes that the essentials cannot handle well. Replacing essentials with specialized types for general use would sacrifice effectiveness for novelty: polar plots for non-cyclical data add confusion; radar charts for simple comparisons distort area; heatmaps for small datasets are less precise than annotated bar charts. The rule: use specialized types when the data shape demands them, not as stylistic replacements for the essentials.

Part B: Applied — Specialized Chart Production (10 problems)

B.1 ★★☆ | Apply

Create a heatmap from a 10×12 random 2D array using ax.imshow. Add a colorbar, row labels for years 2015-2024, column labels for months Jan-Dec, and a title.

Guidance
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(42)
data = np.random.randn(10, 12)

fig, ax = plt.subplots(figsize=(12, 7))
im = ax.imshow(data, cmap="RdBu_r", aspect="auto", vmin=-abs(data).max(), vmax=abs(data).max())
fig.colorbar(im, ax=ax, label="Value")
ax.set_xticks(range(12))
ax.set_xticklabels(["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"])
ax.set_yticks(range(10))
ax.set_yticklabels(range(2015, 2025))
ax.set_title("Monthly Values by Year")
fig.savefig("heatmap.png", dpi=300, bbox_inches="tight")

B.2 ★★☆ | Apply

Add numerical annotations to each cell of the heatmap from B.1. Use white text on dark cells and black text on light cells.

Guidance
threshold = data.mean()
for i in range(data.shape[0]):
    for j in range(data.shape[1]):
        color = "white" if data[i, j] > threshold else "black"
        ax.text(j, i, f"{data[i, j]:.2f}", ha="center", va="center", fontsize=8, color=color)
Add this loop after the imshow call. The text color adapts based on cell value.

B.3 ★★☆ | Apply

Create a contour plot of a 2D Gaussian function. Use ax.contour for line contours and ax.contourf for filled contours. Compare the two visually.

Guidance
import numpy as np

x = np.linspace(-3, 3, 100)
y = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x, y)
Z = np.exp(-(X**2 + Y**2) / 2)

fig, axes = plt.subplots(1, 2, figsize=(14, 6), constrained_layout=True)

cs1 = axes[0].contour(X, Y, Z, levels=10, cmap="viridis")
axes[0].clabel(cs1, inline=True, fontsize=8)
axes[0].set_title("Line Contours")

cf = axes[1].contourf(X, Y, Z, levels=20, cmap="viridis")
fig.colorbar(cf, ax=axes[1])
axes[1].set_title("Filled Contours")

fig.savefig("contours.png", dpi=300, bbox_inches="tight")

B.4 ★★☆ | Apply

Create a polar bar chart showing hourly activity counts for 24 hours. Set the zero location to North and the direction to clockwise.

Guidance
np.random.seed(1)
hours = np.arange(24)
counts = np.random.rand(24) * 100

theta = hours * 2 * np.pi / 24

fig = plt.figure(figsize=(8, 8))
ax = fig.add_subplot(111, projection="polar")
ax.bar(theta, counts, width=2 * np.pi / 24, alpha=0.7, color="steelblue")
ax.set_theta_zero_location("N")
ax.set_theta_direction(-1)
ax.set_xticks(theta)
ax.set_xticklabels([f"{h}h" for h in hours], fontsize=8)
ax.set_title("Activity by Hour of Day")
fig.savefig("polar.png", dpi=300, bbox_inches="tight")

B.5 ★★☆ | Apply

Create a line chart with a shaded confidence band using ax.plot and ax.fill_between. Use a central line and ±1σ bounds.

Guidance
x = np.linspace(0, 10, 100)
y = np.sin(x) + 0.1 * np.random.randn(len(x))
std = 0.3

fig, ax = plt.subplots(figsize=(10, 5))
ax.plot(x, y, color="#1f77b4", linewidth=1.5)
ax.fill_between(x, y - std, y + std, alpha=0.2, color="#1f77b4")
ax.set_title("Sine with Confidence Band")
fig.savefig("confidence_band.png", dpi=300, bbox_inches="tight")

B.6 ★★☆ | Apply

Create an errorbar plot with asymmetric errors (lower and upper errors different). Use five data points with different asymmetric error values.

Guidance
x = np.arange(5)
y = np.array([10, 15, 12, 18, 20])
lower = np.array([1, 2, 1, 3, 2])
upper = np.array([3, 2, 4, 1, 3])

fig, ax = plt.subplots(figsize=(8, 5))
ax.errorbar(x, y, yerr=[lower, upper], fmt="o", capsize=4, markersize=8, ecolor="gray")
ax.set_title("Asymmetric Error Bars")
fig.savefig("errorbar.png", dpi=300, bbox_inches="tight")

B.7 ★★★ | Apply

Create a climate heatmap from synthetic monthly anomaly data (years × months). Use a diverging colormap with symmetric vmin/vmax around zero.

Guidance
np.random.seed(42)
years = np.arange(1950, 2025)
months = np.arange(12)
anomaly = np.random.randn(len(years), 12) * 0.3 + (years - 1950)[:, None] * 0.005

fig, ax = plt.subplots(figsize=(12, 10))
abs_max = abs(anomaly).max()
im = ax.imshow(anomaly, cmap="RdBu_r", aspect="auto", vmin=-abs_max, vmax=abs_max)
fig.colorbar(im, ax=ax, label="Temperature Anomaly (°C)")

ax.set_xticks(range(12))
ax.set_xticklabels(["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"])
ax.set_yticks(range(0, len(years), 10))
ax.set_yticklabels(years[::10])
ax.set_title("Monthly Temperature Anomalies, 1950-2024")

fig.savefig("climate_heatmap.png", dpi=300, bbox_inches="tight")

B.8 ★★★ | Apply

Create a figure with four specialized chart types in a 2×2 grid: a heatmap, a contour plot, a polar bar chart, and an errorbar chart. Use GridSpec and add panel labels (a), (b), (c), (d).

Guidance
import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure(figsize=(14, 10), constrained_layout=True)
gs = fig.add_gridspec(2, 2)

# (a) Heatmap
ax_a = fig.add_subplot(gs[0, 0])
data = np.random.randn(10, 12)
im = ax_a.imshow(data, cmap="RdBu_r", aspect="auto")
fig.colorbar(im, ax=ax_a)
ax_a.set_title("(a) Heatmap", loc="left")

# (b) Contour
ax_b = fig.add_subplot(gs[0, 1])
x, y = np.meshgrid(np.linspace(-3, 3, 50), np.linspace(-3, 3, 50))
z = np.exp(-(x**2 + y**2))
cf = ax_b.contourf(x, y, z, levels=15, cmap="viridis")
fig.colorbar(cf, ax=ax_b)
ax_b.set_title("(b) Contour", loc="left")

# (c) Polar bar
ax_c = fig.add_subplot(gs[1, 0], projection="polar")
theta = np.arange(24) * 2 * np.pi / 24
counts = np.random.rand(24) * 100
ax_c.bar(theta, counts, width=2 * np.pi / 24, alpha=0.7)
ax_c.set_theta_zero_location("N")
ax_c.set_theta_direction(-1)
ax_c.set_title("(c) Polar", loc="left")

# (d) Errorbar
ax_d = fig.add_subplot(gs[1, 1])
xp = np.arange(10)
yp = np.random.randn(10) + 5
yerr = np.random.rand(10) + 0.5
ax_d.errorbar(xp, yp, yerr=yerr, fmt="o", capsize=4, ecolor="gray")
ax_d.set_title("(d) Error bars", loc="left")

fig.savefig("specialized_grid.png", dpi=300, bbox_inches="tight")

B.9 ★★★ | Apply

Create a quiver plot of a simple rotational vector field (U = -Y, V = X) on a 10×10 grid. Set aspect="equal" so the arrows look correct.

Guidance
x = np.linspace(-2, 2, 10)
y = np.linspace(-2, 2, 10)
X, Y = np.meshgrid(x, y)
U = -Y
V = X

fig, ax = plt.subplots(figsize=(8, 8))
ax.quiver(X, Y, U, V, color="#1f77b4")
ax.set_aspect("equal")
ax.set_title("Rotational Vector Field")
fig.savefig("quiver.png", dpi=300, bbox_inches="tight")

B.10 ★★★ | Create

Create a line chart of noisy data with three layers: the raw data as faint points, a smoothed central line, and a confidence band around the central line. Use alpha on the points so they recede visually.

Guidance
from scipy.ndimage import uniform_filter1d

x = np.linspace(0, 10, 200)
raw = np.sin(x) + 0.3 * np.random.randn(len(x))
smooth = uniform_filter1d(raw, size=10)
std = np.full_like(smooth, 0.2)

fig, ax = plt.subplots(figsize=(12, 5))
ax.scatter(x, raw, color="gray", alpha=0.3, s=15, label="Raw")
ax.fill_between(x, smooth - std, smooth + std, alpha=0.2, color="#1f77b4", label="95% CI")
ax.plot(x, smooth, color="#1f77b4", linewidth=1.5, label="Smoothed")
ax.legend()
ax.set_title("Raw Data with Smoothed Central Line")
fig.savefig("layered.png", dpi=300, bbox_inches="tight")

Part C: Synthesis (4 problems)

C.1 ★★★ | Analyze

For the climate monthly anomaly heatmap (B.7), identify three specific Chapter 3 principles the colormap choice implements.

Guidance (1) Diverging palette for data with a meaningful midpoint (zero). (2) Symmetric vmin/vmax so the neutral midpoint aligns with zero. (3) Perceptually uniform (RdBu_r is colorblind-safer than jet and has a meaningful luminance profile). Also: (4) red-for-warm semantically matches the temperature variable.

C.2 ★★★ | Create

Write a reusable function plot_annotated_heatmap(data, row_labels, col_labels, ax=None, cmap="RdBu_r") that takes data and labels and produces an annotated heatmap. If ax is None, create a new figure.

Guidance See Section 14.8 of the chapter for a template. The function should encapsulate the annotation loop and the text color logic. It should return the Axes for further customization.

C.3 ★★★ | Evaluate

The chapter argues that radar charts have area-distortion issues. Find a published radar chart online and describe how the shape could be misinterpreted. Would a different chart type serve the same question better?

Guidance Common radar chart examples include sports player comparisons and product feature comparisons. The area of the radar shape is not a meaningful aggregate because it depends on the order of the categories. A player with similar values across categories has a more "circular" shape, but this circular shape has no quantitative meaning. Alternative: a grouped bar chart or a parallel coordinates plot would preserve the individual category values without implying area as a meaningful quantity.

C.4 ★★★ | Create

Pick one of the specialized chart types and find three real-world uses of it in published visualizations (news graphics, scientific papers, or dashboards). Annotate what the chart type achieves in each case that the standard Chapter 11 chart types could not.

Guidance This exercise builds judgment about when specialized types are the right choice. Heatmaps appear in correlation matrices (papers), stock market calendars (news), and vital-signs dashboards (medical). Contours appear in weather maps and density visualizations. Polar plots appear in wind roses and activity clocks. For each, ask: what question does it answer, and why would a line or bar chart have failed?

These exercises cover the specialized chart types. Each builds muscle memory for a specific chart type you may need rarely but importantly.