Exercises — 3D plots — surface, wireframe
Before we begin, one shared picture to keep in your head — the checkerboard floor + poles:

Every little square on the floor has a position . At each square we stand a pole of height . meshgrid gives us the positions of all the squares; the function gives us the pole heights; plot_surface drapes a sheet over the pole tops.
Level 1 — Recognition
These test whether you can read the notation and name the pieces.
Recall Solution 1.1
The rule from the parent note: meshgrid(x, y) returns two arrays of shape .
- WHAT:
len(y) = 2(that's10, 20) andlen(x) = 4(that's1,2,3,4). - WHY this order: rows vary in , columns vary in — like an image indexed
(row, col). - So and . Both are the same shape.
Concretely:
X = [[1,2,3,4], Y = [[10,10,10,10],
[1,2,3,4]] [20,20,20,20]]
repeats down each row; repeats across each column.
Recall Solution 1.2
ax.plot_surface→ filled colored patches (supports acmapthat maps height to color).ax.plot_wireframe→ a see-through mesh of lines (usually one solidcolor=). Think DRAPE: a bedsheet (surface) vs a fishing net (wireframe).
Recall Solution 1.3
The keyword is projection='3d'.
ax = fig.add_subplot(projection='3d')WHY: the 3D drawing methods (plot_surface, plot_wireframe) only exist on a 3D axes object. Ask a plain 2D axes for plot_surface and you get an AttributeError.
Level 2 — Application
Now you compute actual numbers and grids.
Recall Solution 2.1
- WHAT: first write the coordinate grids.
X = [[0,1,2], Y = [[ 0, 0, 0],
[0,1,2]] [10,10,10]]
- WHY elementwise:
Z = X + Yadds cell-by-cell (this is NumPy vectorization) — no loop needed. - Add matching cells:
Z.shapeis(2, 3)— same asXandY.
Recall Solution 2.2
- WHAT: the plot is literally the function drawn, so read the height by plugging the coordinates into .
- .
- WHY it looks like a bowl: is the squared distance from the origin, so it grows in every direction from the minimum at .
Recall Solution 2.3
- Let be the distance from the origin. Here .
- WHY : it's the Pythagorean distance — this surface only cares how far you are from the center, not the direction, so it makes concentric ripples.
- . That's the top of the first ripple crest.
Level 3 — Analysis
Reason about strides, shapes, and why a plot looks the way it does.
Recall Solution 3.1
- WHAT
rstridemeans: row stride = draw every -th grid row line;cstridedoes the same for columns. - (a)
len(y) = 61, so there are 61 row lines before striding. - (b) Drawing every 10th of 61: indices → lines.
- WHY thin the mesh: a full mesh is a dense hairball; striding trades detail for a readable see-through cage.
Recall Solution 3.2
X.shape = Y.shape = (len(y), len(x)) = (40, 100).Z = np.cos(X) * Yis elementwise, soZ.shape = (40, 100)too.- All three grids match →
plot_surface(X, Y, Z)succeeds. The requirement is simplyX.shape == Y.shape == Z.shape.
Recall Solution 3.3
xis 1D with shape(len(x),), sonp.sin(x)is also 1D of shape(len(x),).- But
XandYare 2D of shape(len(y), len(x)). - WHY it fails:
plot_surfaceneeds a full 2D table of heights, one per floor cell. A 1DZhas no height for most cells → a shape mismatch error. - Fix: always feed the grid arrays into :
Z = np.sin(X).
Level 4 — Synthesis
Assemble complete, correct scripts from scratch.
Recall Solution 4.1
Follow GRID → HEIGHT → DRAPE:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-2, 2, 80) # GRID: even, endpoint-inclusive sampling
y = np.linspace(-2, 2, 80)
X, Y = np.meshgrid(x, y) # 2D coordinate grids, shape (80, 80)
Z = X**2 - Y**2 # HEIGHT: vectorized, same shape as X
fig = plt.figure()
ax = fig.add_subplot(projection='3d') # 3D axes — required
ax.plot_surface(X, Y, Z, cmap='viridis') # DRAPE the colored sheet
ax.set_xlabel('x'); ax.set_ylabel('y'); ax.set_zlabel('z')
plt.show()WHY linspace not arange: we want exactly 80 evenly spaced points including both ends — smoother surface. See Colormaps in Matplotlib for cmap choices.
What it looks like: a Pringle chip — rising along the -axis, falling along the -axis.

Recall Solution 4.2
Replace the surface call:
ax.plot_wireframe(X, Y, Z, rstride=8, cstride=8, color='green')- WHY
rstride=8, cstride=8: an mesh is far too dense; every 8th line gives lines per direction — a clean cage. - WHY no
cmap: wireframes are lines, not patches — a colormap has no per-face patch to color, so we pass a singlecolor=.
Recall Solution 4.3
- WHAT meshgrid does: repeat
xdown the rows, repeatyacross the columns.
import numpy as np
x = np.array([1,2,3]); y = np.array([10,20])
X = np.tile(x, (len(y), 1)) # x as a row, stacked len(y) times
Y = np.tile(y.reshape(-1,1), (1, len(x))) # y as a column, stretched len(x) wideResult:
X = [[1,2,3], Y = [[10,10,10],
[1,2,3]] [20,20,20]]
- WHY it matches: this is exactly what meshgrid and broadcasting does internally —
Xfrom broadcasting a row,Yfrom broadcasting a column.
Level 5 — Mastery
Predict the picture, then confirm — "Forecast-then-Verify".
Recall Solution 5.1
- Let . The surface is — concentric ripples because it depends only on distance .
- First trough where : the smallest positive is .
- At : — back to floor level, one full ripple completed.
- WHY concentric: since only enters, every point on a circle of radius has the same height → rings, like a pond after a stone drops.

Recall Solution 5.2
- (a) The exponent is largest (least negative) at the origin where it equals , so . Peak height at .
- (b) .
- (c) As , , so . The bump flattens to the floor — a single smooth hill that never quite reaches .
- WHY and not : using keeps it smooth and rotationally symmetric with a rounded top; a bare would give a sharp cone tip at the origin.
Recall Solution 5.3
- Use a contour plot (
ax.contourf(X, Y, Z)), viewed on a plain 2D axes. - What stays the same: the entire
X, Y, Zgrid — a contour is literally the surface's height map seen from the top, colored by height, exactly as colormaps color the surface. - WHY it works: both a surface and a contour answer "what is at each floor point?"; one drapes a sheet, the other paints the floor.
Recall Solution 5.4
- A grid of rows by columns of points creates gaps down and gaps across.
- Each pair of gaps bounds one quadrilateral facet → total facets .
- For : facets.
- WHY this matters: facet count explodes with grid size (this is why
rstride/cstrideexist) — a grid is facets, too dense to see.
Recall
Recall
meshgrid(x,y) with len(x)=5, len(y)=3 gives X.shape = ? ::: (3, 5), i.e. (len(y), len(x)). Height of Z = X2 + Y2 at (3,4)? ::: 25. Height of Z = sin(sqrt(X²+Y²)) at (0, π/2)? ::: sin(π/2) = 1. First-trough radius of z = sin(r)? ::: r = 3π/2 ≈ 4.712. Peak of z = e^{-(x²+y²)} and where? ::: 1, at the origin. Facets drawn by plot_surface for m×n points? ::: (n−1)(m−1). Does bigger rstride give more or less detail? ::: Less — it skips more lines.
Connections
- Parent: 3D plots — surface, wireframe
- meshgrid and broadcasting
- NumPy vectorization
- Matplotlib figure and axes objects
- Contour plots
- Colormaps in Matplotlib
- 2D plots — line, scatter