Quiz: Subplots and GridSpec
20 questions. Aim for mastery (18+).
Multiple Choice (10 questions)
1. To create a simple 2×3 grid of equal-sized panels, which call is correct?
(a) fig, axes = plt.subplots(grid=(2, 3))
(b) fig, axes = plt.subplots(2, 3, figsize=(12, 8))
(c) fig = plt.figure(rows=2, cols=3)
(d) fig, ax = plt.subplot(2, 3)
Answer
**(b)** `fig, axes = plt.subplots(2, 3, figsize=(12, 8))`. `plt.subplots(nrows, ncols)` is the canonical call. It returns a Figure and a numpy array of Axes. With nrows=2 and ncols=3, the returned `axes` is a 2D array indexed as `axes[row, col]`. Note the plural: `plt.subplots` (with s) is different from `plt.subplot` (singular, legacy pyplot).2. In a 2D axes array from plt.subplots(2, 3), how do you access the bottom-right panel?
(a) axes[-1, -1] or axes[1, 2]
(b) axes[3]
(c) axes[2, 3]
(d) axes[bottom, right]
Answer
**(a)** `axes[-1, -1]` or `axes[1, 2]`. The array is indexed as `axes[row, col]` with zero-based integers. For a 2×3 grid: rows are 0 and 1, columns are 0, 1, 2. The bottom-right is row 1, column 2. Negative indexing works too: `-1` is the last element in each dimension.3. The sharex=True parameter in plt.subplots does what?
(a) Makes all panels use the same x-axis limits and hides redundant tick labels (b) Draws only one x-axis shared across all panels (c) Uses the same x-data for every panel (d) Removes the x-axis entirely
Answer
**(a)** Makes all panels use the same x-axis limits and hides redundant tick labels. Shared axes align the x-limits and tick positions across all panels. Tick labels appear only on the bottom row (for `sharex`) or leftmost column (for `sharey`) to reduce redundancy. This is essential for small-multiple layouts where comparison depends on consistent scales.4. GridSpec is used when:
(a) You need a regular grid with equal-sized panels (b) You need unequal panel sizes, spanning cells, or asymmetric layouts (c) You only have one panel (d) You want to share axes
Answer
**(b)** You need unequal panel sizes, spanning cells, or asymmetric layouts. `plt.subplots` handles regular grids. GridSpec is the next level of flexibility, supporting `width_ratios`, `height_ratios`, and slicing notation to create panels that span multiple cells. Use GridSpec when the design requires more than `plt.subplots` can produce.5. The height_ratios=[2, 1, 1] parameter in fig.add_gridspec(3, 1, height_ratios=[2, 1, 1]) means:
(a) The first row is twice as tall as the other two (b) The last row is twice as tall (c) All rows are equal (d) The middle row is twice as tall
Answer
**(a)** The first row is twice as tall as the other two. `height_ratios` specifies the relative heights of each row. With `[2, 1, 1]`, the first row's height is 2 units, the second and third are 1 unit each, so the first is twice the others. Similarly, `width_ratios` specifies column widths.6. The GridSpec slice gs[0, :] creates an Axes that:
(a) Spans the entire first row (all columns) (b) Is only the top-left cell (c) Spans the entire first column (d) Is the last row
Answer
**(a)** Spans the entire first row (all columns). The slice `[0, :]` uses numpy notation: row 0, all columns. This creates a panel that covers the full width of the figure at the top row. It is the standard pattern for "hero plus supporting" layouts where the hero spans the top.7. constrained_layout=True is preferred over fig.tight_layout() because:
(a) It is faster to execute (b) It handles complex layouts (colorbars, legends, suptitles) more robustly (c) It is newer (d) It requires less code
Answer
**(b)** It handles complex layouts (colorbars, legends, suptitles) more robustly. `constrained_layout` uses a proper constraint-solving algorithm that runs during drawing, while `tight_layout` is a simpler heuristic applied after drawing. `constrained_layout` works correctly with colorbars, figure-level legends, and suptitles, where `tight_layout` can produce unexpected results. Both produce correct output for simple cases.8. To create an inset axes inside an existing Axes, you use:
(a) ax.add_inset(...)
(b) ax.inset_axes([x, y, width, height])
(c) plt.inset(ax, ...)
(d) fig.add_inset(...)
Answer
**(b)** `ax.inset_axes([x, y, width, height])`. The `inset_axes` method takes a list of `[x, y, width, height]` in axes-fraction coordinates and returns a new Axes positioned inside the parent. `[0.1, 0.5, 0.35, 0.35]` creates an inset 10% from the left, 50% from the bottom, 35% wide, and 35% tall, relative to the parent Axes.9. For a three-panel climate figure with temperature, CO2, and sea level sharing a time axis but having different units, which axis-sharing strategy is correct?
(a) sharex=True, sharey=True
(b) sharex=True, sharey=False
(c) sharex=False, sharey=True
(d) sharex=False, sharey=False
Answer
**(b)** `sharex=True, sharey=False`. The three variables share a common time axis (year), so `sharex=True` lets the reader compare at matching time points. But the three variables have different units (°C, ppm, mm) and different meaningful ranges, so they should NOT share the y-axis. Forcing them onto a shared y-axis would either squish some variables or obscure their variation. This is the "free y-axis, shared x-axis" pattern from Chapter 8.10. The chapter's threshold concept for layout is:
(a) All layouts should use plt.subplots
(b) Layout is code — every design decision maps to specific matplotlib API calls
(c) Complex layouts should be avoided
(d) GridSpec is always better than subplots
Answer
**(b)** Layout is code — every design decision maps to specific matplotlib API calls. The skill is translating design sketches into the appropriate matplotlib primitives (nrows/ncols, width_ratios/height_ratios, slicing, sharex/sharey, inset_axes). Once you can do this mapping fluently, complex multi-panel figures become approachable.True / False (5 questions)
11. "When you use plt.subplots(1, 3), the returned axes is a 2D array indexed as axes[0, 0]."
Answer
**False.** With a single row (1×3) or single column (3×1), the returned `axes` is a 1D array indexed as `axes[0]`, `axes[1]`, `axes[2]`. It becomes 2D only when both nrows and ncols are ≥ 2. To force consistent 2D behavior, pass `squeeze=False`.12. "constrained_layout=True can be set as a global default in rcParams so you never have to specify it explicitly."
Answer
**True.** Setting `mpl.rcParams["figure.constrained_layout.use"] = True` (or including it in your `.mplstyle` file) applies `constrained_layout=True` to every new figure automatically. This is a reasonable default for most matplotlib work.13. "Inset axes are fully functional Axes — you can call any plot method on them."
Answer
**True.** An inset created with `ax.inset_axes(...)` returns a normal Axes object. You can call `plot`, `scatter`, `bar`, `hist`, `set_title`, `set_xlabel`, etc. on it just like on any other Axes. It just happens to be positioned inside a parent Axes.14. "Dual-axis charts (ax.twinx) are always wrong and should never be used."
Answer
**False.** Dual-axis charts are usually wrong when the two y-axes show different variables, because the visual alignment can manufacture apparent correlations. But they are legitimate when the two axes are unit conversions of the same variable (Celsius and Fahrenheit, for example). This is the narrow exception noted in Chapter 4 and Section 13.8. Outside that narrow case, avoid dual-axis charts in favor of small multiples.15. "The fig.subfigures() API is required for any multi-panel figure."
Answer
**False.** `subfigures` is a matplotlib 3.4+ feature for composing figures from logical groups, useful for complex layouts. Most multi-panel figures can be produced with `plt.subplots` or `GridSpec` without subfigures. Use subfigures when the structure is genuinely hierarchical and other approaches become awkward.Short Answer (3 questions)
16. Describe the five-step decomposition process from Section 13.8 for translating a design sketch into GridSpec code.
Answer
(1) **Count rows and columns** to determine the basic grid structure. (2) **Identify unequal sizes**: if some rows or columns need to be larger than others, use `width_ratios` or `height_ratios`. (3) **Identify spanning cells**: if any panel covers multiple cells, use slice notation like `gs[0, :]` or `gs[0:2, 0:2]`. (4) **Identify shared axes**: if panels should align on x or y, use `sharex` or `sharey`. (5) **Set a reasonable figsize** based on the total size needed for all panels. Once you can do these five steps automatically, the GridSpec code writes itself.17. Explain when shared axes (sharex=True or sharey=True) are appropriate and when they are not. Give an example of each.
Answer
Shared axes are appropriate when the panels need to enable direct comparison of values at matching positions. Example: a small multiple showing sales for 10 products over time — shared x-axis (year) and shared y-axis (revenue) let the reader compare products directly. Shared axes are inappropriate when the panels show different variables with different units or very different ranges. Example: a three-panel climate figure with temperature, CO2, and sea level — shared x-axis (year) is appropriate because they share a time range, but shared y-axis would force different units onto the same scale, which is either meaningless or hides variation in the smaller-range variables.18. Describe three common layout pitfalls from Section 13.7 and their fixes.
Answer
**(1) Titles overlapping the panels above** — fix by using `constrained_layout=True` or increasing `hspace` via `subplots_adjust`. **(2) Axis labels cut off at the edges** — fix by using `bbox_inches="tight"` in savefig or using `constrained_layout`. **(3) Tick labels colliding between adjacent panels** — fix by using `constrained_layout`, increasing `wspace`, or sharing the y-axis when magnitudes are comparable. Other pitfalls include legends appearing on every panel instead of once at the figure level, shared axes hiding tick labels you wanted, and `constrained_layout` warnings when the figure is too small.Applied Scenarios (2 questions)
19. You are designing a dashboard figure for a quarterly business review. It needs: a hero chart showing quarterly revenue (large, spanning the top), three supporting metric panels (equal-sized, in a row below the hero), and a wide bottom panel showing a detailed trend. Write the GridSpec code for this layout.
Answer
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(14, 10), constrained_layout=True)
gs = fig.add_gridspec(
nrows=3,
ncols=3,
height_ratios=[2, 1, 1.5],
)
ax_hero = fig.add_subplot(gs[0, :]) # spans top row, all columns
ax_m1 = fig.add_subplot(gs[1, 0]) # supporting panel 1
ax_m2 = fig.add_subplot(gs[1, 1]) # supporting panel 2
ax_m3 = fig.add_subplot(gs[1, 2]) # supporting panel 3
ax_bottom = fig.add_subplot(gs[2, :]) # spans bottom row, all columns
# Populate with your data
ax_hero.plot(quarters, revenue)
ax_hero.set_title("Quarterly Revenue (Hero)")
# ... and so on
The `height_ratios=[2, 1, 1.5]` makes the hero twice as tall as the supporting row and the detail panel 1.5x the supporting row. The spanning cells `gs[0, :]` and `gs[2, :]` create the wide panels.
20. A colleague shows you the following code for a three-panel climate figure. Identify what is wrong and propose a fix.
fig = plt.figure(figsize=(8, 4))
plt.subplot(3, 1, 1)
plt.plot(years, temperature)
plt.title("Temperature")
plt.subplot(3, 1, 2)
plt.plot(years, co2)
plt.title("CO2")
plt.subplot(3, 1, 3)
plt.plot(years, sea_level)
plt.title("Sea Level")
plt.tight_layout()
plt.savefig("climate.png")
Answer
**Problems identified:** 1. **Uses pyplot state-machine API throughout.** The code mixes `plt.subplot()` (singular, legacy), `plt.plot()`, and `plt.title()`. This is the old style. The recommended OO API uses `fig, axes = plt.subplots(3, 1, ...)` and then `axes[0].plot(...)`, `axes[0].set_title(...)`, etc. 2. **figsize (8, 4) is too small for 3 panels.** An 8-inch wide, 4-inch tall figure with 3 stacked panels gives each panel only ~1.3 inches of height, which is too squished. 3. **No shared x-axis.** All three panels show data indexed by year; they should share the x-axis with `sharex=True` to enable comparison and reduce redundant labels. 4. **Uses `tight_layout()` instead of `constrained_layout`.** The chapter recommends `constrained_layout=True` for multi-panel figures. 5. **No axis labels.** No y-axis labels means units are missing (violation of Chapter 7). 6. **No savefig bbox_inches="tight" or dpi.** The saved image will be low quality and potentially cropped. **Corrected code:**import matplotlib.pyplot as plt
fig, axes = plt.subplots(3, 1, figsize=(12, 9), sharex=True, constrained_layout=True)
axes[0].plot(years, temperature)
axes[0].set_title("Temperature")
axes[0].set_ylabel("Temperature Anomaly (°C)")
axes[1].plot(years, co2)
axes[1].set_title("CO2")
axes[1].set_ylabel("CO2 (ppm)")
axes[2].plot(years, sea_level)
axes[2].set_title("Sea Level")
axes[2].set_ylabel("Sea Level (mm)")
axes[2].set_xlabel("Year")
for ax in axes:
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
fig.suptitle("Three Measurements of a Warming Planet, 1880-2024", fontsize=14)
fig.savefig("climate.png", dpi=300, bbox_inches="tight")
Review results against mastery thresholds. Chapter 14 introduces specialized chart types (heatmaps, contours, polar plots, error bars) and assumes you can build and customize multi-panel layouts using the tools from this chapter.