1.4.8 · D5Python & Scientific Computing
Question bank — Matplotlib and Seaborn visualization
This is a rapid-fire misconception hunt for Matplotlib and Seaborn visualization. Each line is a question ::: answer reveal. Read the question, commit to an answer in your head, then reveal. If your instinct differs from the reasoning, you just found a trap worth remembering.
Before we start, two words used everywhere below, so nothing is unearned:
True or false — justify
TF: plt.plot(x, y) and ax.plot(x, y) do exactly the same thing.
False in control, similar in result.
plt.plot draws on whatever Axes is "current" (hidden global state); ax.plot draws on the specific Axes you named. With one plot they look identical, but with subplots plt.plot can silently target the wrong panel.TF: Seaborn replaces Matplotlib — you don't need to know Matplotlib to use Seaborn.
False. Every Seaborn function returns a Matplotlib Axes, and you customize titles, limits, and labels with Matplotlib methods (
ax.set_title, etc.). Seaborn is a convenience layer on top of Matplotlib, not a substitute for it.TF: plt.tight_layout() changes your data.
False. It only re-adjusts spacing/margins so labels and titles stop overlapping or clipping. The plotted numbers are untouched; it is purely cosmetic geometry.
TF: A histogram with density=True shows the probability of each bar.
False. It shows probability density, meaning bar height × bar width = probability. Heights can exceed 1; only the total area sums to 1. That is exactly why you can overlay a smooth density curve on it.
TF: ax.set_yscale('log') is the right choice whenever numbers get large.
False (misleading). Log scale is right when values span many orders of magnitude or decay/grow exponentially (like training loss). For merely large-but-linear values it distorts perception of equal differences.
TF: Starting a bar chart's y-axis at a non-zero value is always dishonest.
Partly false. For comparing tiny differences (accuracies of 0.82 vs 0.85) a zoomed range makes the difference visible and is standard. It becomes dishonest only when you hide the zoom or imply the bars start at zero.
TF: transform=ax.transAxes lets you place text at a fixed corner regardless of the data range.
True.
transAxes uses normalized (0,0)-to-(1,1) coordinates of the Axes box, so (0.2, 0.8) is always "20% right, 80% up" even if your data runs from 0 to a million.TF: KDE (kde=True) shows the true underlying distribution of your data.
False. KDE shows an estimate: it places a smooth bump at each data point and sums them. Its smoothness depends on a bandwidth choice, so a wrong bandwidth can invent or erase peaks. It is a model of the distribution, not the truth.
TF: Calling sns.histplot(...) twice in a row makes two separate figures.
False by default. Without passing distinct
ax= targets, both draw onto the same current Axes, overlaying each other. You must create separate Axes (via plt.subplots) to get separate panels.Spot the error
Error: fig, ax = plt.subplots(2, 2); ax.plot(x, y) — what breaks?
With a 2×2 grid,
ax is now a 2D array of four Axes, not one Axes, so it has no .plot of its own. You must index it: ax[0,0].plot(x, y).Error: sns.violinplot(df); ax.set_xlabel('Optimizer') where ax was never created.
ax is undefined. Either capture it — ax = sns.violinplot(...) — or create one first with fig, ax = plt.subplots() and pass ax=ax. Seaborn returns/uses an Axes; you can't label a variable that doesn't exist.Error: A loss curve uses ax.plot(train_loss) with no x values, then someone claims "epoch 30 is highest".
With one argument, Matplotlib uses the index
0,1,2,... as x. If epochs start at 1, every reported x is off by one — index 30 is actually epoch 31. Pass explicit epochs to name the axis correctly.Error: ax.text(0.2, 0.8, 'label') is used to pin a caption to a corner, but it drifts when data changes.
The default is data coordinates, so
(0.2, 0.8) are data values and can even land off-screen. To pin it to the corner you must add transform=ax.transAxes.Error: ax.hist(data); ax.hist(other) to compare two distributions, but the second bars fully hide the first.
Opaque overlapping bars occlude each other. Fix with
alpha=0.5 for transparency, or histtype='step', or plot them side-by-side. Otherwise one dataset is invisible.Error: A colorbar is added with plt.colorbar() after several subplots and lands on the wrong panel.
Bare
plt.colorbar() guesses the current Axes/mappable. Be explicit: fig.colorbar(scatter, ax=ax2) ties the bar to the exact scatter and Axes you mean.Why questions
Why prefer the object-oriented (fig, ax) interface over pyplot for real projects?
Because it is explicit and reproducible — you always know which Axes you're modifying, functions can accept
ax as an argument, and there is no hidden global state to surprise you inside subplots or loops.Why does Seaborn want a DataFrame and column names instead of raw arrays?
Because it can then read categories, labels, and groupings straight from the columns to auto-facet, auto-label axes, and compute per-group statistics (like confidence intervals) — work you'd otherwise hand-code in Matplotlib. See 1.4.02-Pandas-DataFrames-and-data-manipulation.
Why is a scatter plot with a color dimension (c=..., colorbar) useful in ML?
It encodes a third variable (confidence, density, class) onto a 2D plot, letting you see relationships among three quantities at once without a 3D plot that's hard to read.
Why plot train and validation loss on the same axes side-by-side with accuracy?
To catch overfitting instantly: when validation loss rises while training loss keeps falling, the gap is visible at a glance — the visual signature of 3.2.02-Overfitting-vs-underfitting.
Why does Seaborn shade a confidence band around regplot's line automatically?
Because the fitted line is an estimate from finite noisy data; the band communicates uncertainty in that estimate, so you don't over-trust a single line. Matplotlib would make you compute and draw that band by hand.
Why is visualization called "debugging with your visual cortex" in ML?
A raw probability matrix hides patterns, but humans spot outliers, trends, and imbalance far faster in a picture — useful throughout 4.1.01-Exploratory-data-analysis and when reading a 2.3.04-Confusion-matrix-and-classification-metrics.
Edge cases
What does a histogram show if all your data values are identical?
Every point falls in one bin, giving a single tall bar and empty everywhere else. With
density=True that lone bar's height is 1/binwidth — a degenerate "spike" distribution, not a bell.What happens to ax.set_yscale('log') if any plotted value is zero or negative?
The logarithm is undefined there, so those points are dropped or the axis errors/clips. Log axes assume strictly positive data — a real trap for loss values that hit exactly 0.
What does a KDE do at the edge of a bounded variable (e.g. a probability that can't exceed 1)?
The Gaussian bumps leak past the boundary, so the KDE puts nonzero density in impossible regions (above 1 or below 0). It doesn't know the bound exists, so trust the histogram near hard limits.
What happens when you draw a KDE from just two or three data points?
You get a smooth-looking curve with a couple of bumps that looks like a real distribution but is almost pure artifact of bandwidth. Density estimates need many points to be meaningful; with a handful, the smoothness is a lie.
A violin plot of a group with near-zero variance looks like what?
A razor-thin sliver — almost a horizontal line — because the violin's width is the density and there's no spread to fill it. Not a bug; it's honestly reporting that the group barely varies.
What does plt.subplots(1, 1) return for ax, and why does that matter for reusable code?
A single Axes object (not an array), unlike
subplots(2,2) which returns an array. Code that assumes an array (ax[0]) breaks for the 1×1 case — a classic off-by-shape trap when generalizing plotting functions.Recall Quick self-test
One-word interface for reproducible ML plotting? ::: Object-oriented (fig, ax).
Bar-height × bar-width equals what when density=True? ::: Probability for that bin.
KDE near a hard boundary tends to do what? ::: Leak density into impossible regions.