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.