Worked examples — 2D plots — line, scatter, bar, histogram, contour, imshow
You have read the parent note and met the six plot types. Now we grind through every messy case you will actually hit: the degenerate ones (empty data, one bin, flat surface), the sign/orientation traps (upside-down images, negative extents), the limiting cases (many bins vs few), a real-world word problem, and an exam twist. Each example first asks you to guess, then shows the reasoning step-by-step, then verifies by plugging numbers back.
We will use only ideas already defined: a plot is a mapping from numbers to pixels; a histogram counts samples into bins of width ; density=True divides each count by so total area is ; a contour at level is the set ; imshow shows an array as colored cells with Z[row, col] = Z[y, x].
The scenario matrix
Every plotting bug or exam question lives in one of these cells. The examples below are labelled with the cell they cover.
| # | Cell class | Concrete trap / question | Example |
|---|---|---|---|
| C1 | Line — resolution limit | few points look jagged, many look smooth | Ex 1 |
| C2 | Histogram — bin arithmetic | which bin does a value land in? edge rule | Ex 2 |
| C3 | Histogram — normalization | does density=True really give area ? |
Ex 3 |
| C4 | Histogram — degenerate | bin, or all samples identical | Ex 4 |
| C5 | Bar — geometry | left edge, width, gaps, negative height | Ex 5 |
| C6 | Contour — the level set | which are on level ? radius formula | Ex 6 |
| C7 | imshow — orientation signs | origin upper vs lower, row↔y flip |
Ex 7 |
| C8 | imshow — extent mapping | array index → real coordinate, incl. negatives | Ex 8 |
| C9 | Real-world word problem | temperature log: choose the right plot + numbers | Ex 9 |
| C10 | Exam twist | mix meshgrid shape + contour count | Ex 10 |
Ex 1 — Line resolution (cell C1)
Forecast: Guess the number of segments and whether 5 points can look smooth.
- Find the spacing. With points over , the gap between consecutive points is
Why this step?
np.linspace(a, b, n)puts points at the two ends and inside, so there are gaps — not . Dividing by is the whole trick. - List the points. , giving . Why this step? These are exactly the peaks/zeros of sine, so the "curve" becomes a zig-zag of straight lines between them.
- Count segments. A line plot draws for , i.e. segments. Why this step? The parent formula: points connectors. Look at the red zig-zag in the figure — 4 straight pieces, no curve at all.

Verify: . Segment count . The eye only sees a smooth sine once segments are tiny (Ex needs ~200 points, so ~199 segments). Units: in radians, dimensionless. ✔
Ex 2 — Which bin? (cell C2)
Forecast: Guess whether lands in the same bin as .
- Bin width. . Why this step? Everything depends on ; get it wrong and every index shifts.
- Bin edges. — indices . Why this step? Each interval is half-open (closed left, open right), so a value exactly on an edge goes to the bin on its right, except the very last edge.
- Place the values. The formula for the index is .
- : → bin 1 .
- : → bin 2 .
- : naively , but there is no bin 5. Matplotlib puts the max value into the last bin, so → bin 4 . Why this step? This is the classic "edge counted twice or lost" trap. The half-open rule stops double-counting; the last bin is closed on the right so the maximum isn't dropped.
Verify: and land in different bins (1 vs 2) — the boundary at belongs to the right bin. And is rescued into bin 4, not an illegal bin 5. ✔
Ex 3 — Does density=True give area 1? (cell C3)
Forecast: Guess the total area before computing — it should be a clean number.
- Bin width. . Why this step? Area of a bar height ; we need to turn heights into areas.
- Density heights. Each becomes : Why this step? Dividing by is the parent's normalization: it converts counts into a probability density so we can overlay a theoretical PDF (Probability density functions and normalization).
- Sum the areas. Area . Why this step? Because . The counts must sum to , so the areas must sum to 1.
Verify: Counts sum: ✔. Total area ✔. Note heights are not probabilities themselves (bin 1 height its probability only because ; with they'd differ). ✔
Ex 4 — Degenerate histograms (cell C4)
Forecast: Guess whether makes the area still equal , and whether identical samples crash.
- (a) One bin. With the whole range is one bin: raw height . Why this step? Every sample lands in the only bin, so the count is all of them.
- Density for one bin. ; height , and area still. Why this step? Normalization is designed to give area 1 for any — even the useless that shows no shape.
- (b) All identical. Then , so — division by zero looms. NumPy's fix: it expands the range to (a unit-wide window around the value) so and the count goes into one bin without crashing. Why this step? A degenerate zero-width range would make every height infinite; the auto-padding keeps the plot finite and drawable.
Verify: (a) raw height ; density area regardless of ✔. (b) NumPy uses range , so and the single count is ✔ — no exception thrown.
Ex 5 — Bar geometry with a negative value (cell C5)
Forecast: Guess whether the negative bar points down and where its top/bottom sit.
- Position. Category ("B") sits at integer . Bar spans with : left , right . Why this step? Parent anatomy: a bar is a rectangle centered on the category's integer slot.
- Height sign. Height means the rectangle grows downward from the baseline : its top is at , its bottom at .
Why this step?
bardraws from the baseline (default 0) to the value; a negative value simply flips the direction below the axis. - Gap between bars. Neighbours are 1 unit apart (integers 0,1,2); each occupies width , so the empty gap is . Why this step? Width is exactly what makes categories read as separate blocks — the parent's "WHY width matters".
Verify: Left , right ✔. Bar B occupies ✔. Gap so bars don't touch ✔.
Ex 6 — The contour level set (cell C6)
Forecast: Guess the shape of the highest level's contour — a curve or a point?
- Write the level equation. The contour at level is , i.e. . Why this step? A contour is a set of points solving an equation — we solve it explicitly.
- Take logs to free the exponent. . Why the logarithm? The unknown is trapped in an exponent; is the tool that undoes , turning "-to-the-something " into "something ". No other operation isolates the exponent.
- Read off the radius. is a circle of radius centered at the origin. Why this step? is the circle definition — so every level of a Gaussian bump is a circle, as the figure's red ring shows.
- Degenerate top. The maximum of is at the origin where . The level solves — a single point, not a curve. Levels give the empty set (no contour drawn). Why this step? This is the limiting case: as the peak, the circle shrinks to a dot; beyond the peak nothing exists to slice.

Verify: ; check ✔. Max value ; level is the single point ; empty ✔.
Ex 7 — imshow orientation flip (cell C7)
Forecast: Guess whether row 0 sits at the top or bottom by default.
- Row↔y rule.
imshowmapsZ[row, col]with row = y, col = x (parent trap #2). Value is atZ[1,0]→ row , column . Why this step? We must translate array indices into screen position before we can say "top" or "bottom". - Default
origin="upper". Row 0 is drawn at the top, rows increase downward. So row 1 (the row) is at the bottom, column 0 on the left → appears bottom-left. Why this step? Image files store row 0 at the top; that's the default and why images look "right" but math matrices look flipped. origin="lower". Row 0 is drawn at the bottom, rows increase upward. Now row 1 is at the top, column 0 left → appears top-left. Why this step?origin="lower"makes increasing row = increasing (upward), which matches how we plot math where grows up.

Verify: With origin="upper", Z[1,0]=30 is bottom-left; flipping to origin="lower" moves it top-left — the two images are vertical mirror-flips of each other, exactly one row-reversal apart ✔.
Ex 8 — imshow extent mapping with negatives (cell C8)
Forecast: Guess whether cell [0,0] lands exactly at the corner or slightly inside.
- Cell width in real units. The x-range spans units across columns, so each cell is wide (same for y).
Why this step?
extentstretches the whole array to fill ; each of 100 cells gets an equal slice. - Center of the first cell. Column 0 occupies real x from to ; its center is . With
origin="lower", row 0 is at the bottom so its y-center is also . SoZ[0,0]center . Why this step? Pixel centers, not edges, are the sample locations; the edge is at but the center sits half a cell in. - Center of the last cell. Column 99 spans x from to ; center . Same for row 99 →
Z[99,99]center . Why this step? By symmetry the far corner mirrors the near corner; the grid is symmetric about the origin.
Verify: Cell width ; first center , last center ; the two centers are symmetric () and span ✔. This uses the same even-spacing idea as NumPy meshgrid and broadcasting.
Ex 9 — Real-world word problem (cell C9)
Forecast: Guess which of the six plots fits ordered time vs a pile of samples.
- Temperature vs time → line. Time is an ordered axis and consecutive readings are connected as a trend, so
ax.plot. Why this step? The parent decision table: "trend over an ordered axis → line". Connecting the dots is meaningful here (temperature between hours is continuous). - Reaction times → histogram. 500 independent samples where we care about the shape of the distribution, not order, so
ax.histwith bins. Why this step? Table: "distribution/shape of one variable → histogram"; is the parent's bin rule of thumb. - The mean. Mean °C. A line plot shows the trend (when it peaked); a histogram would collapse the 24 ordered readings into value-buckets and lose the daily rhythm — so for time structure, line wins; for typical value, the mean/histogram wins. Why this step? Choosing the plot IS the question — each answers a different thing (WHEN vs HOW-OFTEN).
Verify: → about 22 bins ✔. Mean °C ✔ (units: degrees Celsius). Line for the 24-point ordered series, histogram for the 500-sample pile ✔.
Ex 10 — Exam twist: meshgrid shape meets contour (cell C10)
Forecast: Guess whether X is or — the order trips almost everyone.
- Grid shape.
meshgrid(xs, ys)returns arrays of shape(len(ys), len(xs))=(9, 5)— rows follow ys, columns follow xs (the parent's , ). SoX.shape = Z.shape = (9, 5). Why this step? Contour needsZshaped(len(y), len(x)); getting it backwards raises a shape error. This is the parent mistake "contour takes my 1D x and y" made concrete. - Number of contour lines.
levels=[1, 4, 9]lists 3 explicit levels, so up to 3 contour curves are drawn (one per level that actually intersects the surface). Why this step? Passing a list (not an int) means "draw exactly these levels", unlikelevels=8which means "pick 8 nice levels automatically". - A point on level . Solve . Pick a grid value: ; the point is on the surface's grid (since is one of
ys) and satisfies . Another: , and gives . Why this step? The level set is literally the solution set of ; we exhibit members of it to prove the line exists.
Verify: meshgrid(xs, ys) → shape (9,5) ✔ (9 rows, 5 cols). levels=[1,4,9] → 3 lines ✔. On : ✔ and ✔.
Recall Rapid self-test
Bin width for range , ? ::: .
Does a value exactly on a bin's left edge go left or right? ::: Right (bins are half-open).
Density heights sum-of-areas must equal? ::: (since ).
Radius of contour? ::: .
meshgrid(xs, ys) gives X of what shape? ::: (len(ys), len(xs)).
origin="lower" vs "upper" — which matches math matrices? ::: "lower" (row 0 at bottom, up).
Bar at category , width — left edge? ::: .
See also: Matplotlib Figure vs Axes object model · Colormaps and perceptual uniformity (viridis) · Image arrays and pixel coordinates · 3D surface plots (plot_surface) · Seaborn — statistical plotting wrappers.