5.4.17 · D5Scientific Computing (Python)

Question bank — 3D plots — surface, wireframe

1,437 words7 min readBack to topic

Before you start, re-anchor the whole picture (the diagram below): a surface plot is a height map over a checkerboard floor. The floor is a grid of points; the height at each point is ; the picture is that sheet of heights draped in 3D. Almost every trap here comes from forgetting one of those three layers — floor grid, height table, draping call.

Figure — 3D plots — surface, wireframe

Keep two more pictures in mind as you work: what strides actually skip (below), and the ring of crests the ripple surface makes.

Figure — 3D plots — surface, wireframe
Figure — 3D plots — surface, wireframe

True or false — justify

Each line: decide true/false and give the reason, then reveal.

T/F: np.meshgrid(x, y) returns arrays of shape (len(x), len(y)).
False. With the default indexing='xy' it returns shape (len(y), len(x)) — rows vary in , columns vary in , matching image/matrix (row, col) order.
T/F: You can pass 1D x, y, z directly to ax.plot_surface.
False. A surface needs a height over every floor cell, so X, Y, Z must all be 2D of matching shape. 1D inputs raise a shape error; build the grid with meshgrid first.
T/F: plot_wireframe supports a cmap that colors each face by height.
False. A wireframe is made of lines, not filled patches, so it carries one solid color= — there are no faces to color by height.
T/F: Z = X**2 + Y and a Python double for loop filling Z give the same result.
True (if the loop is correct), but the vectorized form is faster and auto-shapes to X; see NumPy vectorization. The loop is where shape/transpose bugs sneak in.
T/F: Increasing rstride shows more detail.
False. rstride/cstride are strides — bigger stride skips more grid lines, giving a coarser mesh. Smaller stride = denser = more detail.
T/F: A 3D axes can be created with plain fig.add_subplot().
False. You need fig.add_subplot(projection='3d'); without it, methods like plot_surface don't exist on the axes (Matplotlib figure and axes objects).
T/F: plot_surface(X, Y, Z) and plot_surface(Y, X, Z) produce the same picture.
False. The first two args are floor-x and floor-y; swapping them mirrors/transposes the layout so the surface no longer sits over the intended coordinates.
T/F: The argument order for plot_surface is (Z, X, Y).
False. It is (X, Y, Z)floor, floor, sky. The two coordinate grids come first, the height last.
T/F: Z must always be built from X and Y via f; you can never supply arbitrary height data.
False. Z just has to be a 2D array matching X.shape; it can come from measurements, files, or any source — it need not be an analytic f(X, Y).

Spot the error

Each line describes a buggy snippet; name the bug and the fix.

x=np.linspace(-3,3,60); ax.plot_surface(x, x, x**2) — what breaks?
x is 1D, so x**2 is 1D too — plot_surface needs 2D grids. Fix (preserving the intended shape): X,Y=np.meshgrid(x,x); Z=X**2 then pass X,Y,Z.
X,Y=np.meshgrid(x,y); Z=[f(a,b) for a in x for b in y]; ax.plot_surface(X,Y,Z) — what's wrong?
Z is a flat Python list, not a 2D array of shape (len(y),len(x)). Fix: use vectorized Z=f(X,Y) (or reshape carefully) so Z.shape==X.shape.
ax=fig.add_subplot(); ax.plot_surface(X,Y,Z) — what's the error?
AttributeError: without projection='3d' the axes is 2D and has no plot_surface. Fix: ax=fig.add_subplot(projection='3d').
ax.plot_wireframe(X,Y,Z, cmap='viridis') — why does color not vary with height?
Wireframes ignore cmap (they're lines). Use color='...' for a single hue, or switch to plot_surface if you want height→color (Colormaps in Matplotlib).
X,Y=np.meshgrid(x,y,indexing='ij'); Z=f(X,Y); ax.plot_surface(X,Y,Z) — surface looks transposed. Why?
'ij' gives shape (len(x),len(y)) (matrix convention). For plotting keep the default 'xy' so rows map to and columns to .
Z = X**2 + Y**2 but Z was built from a different meshgrid than X,Y — symptom?
Shape mismatch or a garbled/transposed surface. Rule: Z.shape must equal the exact X.shape==Y.shape you pass in — print all three before plotting.
ax.plot_surface(X, Y, Z, rstride=0) — what happens?
A stride of 0 is invalid/degenerate (no lines can be stepped) and errors or draws nothing. Stride must be a positive integer ≥ 1.

Why questions

Why does a z = f(x,y) plot need a 2D grid instead of one loop over a 1D array?
Because f has two independent inputs, so you must evaluate every combination of an x-value and a y-value — that's an table, inherently 2D.
Why is meshgrid's default output shape (len(y), len(x)) and not (len(x), len(y))?
So rows index and columns index , matching how images/matrices are addressed as (row, col) — this makes contour and imshow overlays line up (Contour plots).
Why does plot_surface accept a cmap but plot_wireframe doesn't meaningfully?
A surface has filled facets whose color can encode height; a wireframe is only edges, and a line has no interior area to shade by a colormap.
Why prefer np.linspace(a,b,N) over np.arange for a smooth surface?
linspace includes both endpoints and lets you pin the exact point count, giving even, controllable sampling density instead of relying on a step size that may miss the endpoint.
Why does raising rstride/cstride improve readability on a large grid?
A 200×200 grid draws tens of thousands of tiny facets that blur into solid color; skipping lines thins the mesh so the shape is legible and the file is lighter.
Why can Z = X**2 + Y**2 be written in one line with no loop?
NumPy broadcasts the operation elementwise across the whole 2D array at once, so the arithmetic and the resulting shape follow automatically from X, Y.
Why does swapping X and Y in the plot call distort the picture even though both are grids?
The first two arguments define which coordinate is horizontal-x vs horizontal-y; swapping remaps the floor, so each height lands over the wrong location.

Edge cases

What does a surface look like when Z is a constant array (e.g. Z = np.zeros_like(X))?
A flat horizontal sheet at that height — valid, just featureless; useful as a reference plane under another surface.
For Z = np.sin(np.sqrt(X**2+Y**2)), what is the height exactly at the origin?
, so — the surface passes through the origin's floor point.
For that same , at what radius does the first crest () appear?
Where , i.e. radius — a ring of crests, not a single point, as the third figure shows.
What happens if the grid is only 2×2 points?
plot_surface still works but draws just one flat quadrilateral patch — technically valid, visually useless; you need a fine grid to reveal curvature.
If x has a duplicated value (e.g. [1,1,2]), what goes wrong visually?
Two grid columns sit at the same floor-x, so facets there collapse to zero width — a crease/degenerate strip appears in the surface.
What if Z contains np.nan at some cells?
Those facets/lines are simply not drawn, leaving holes in the surface — a legitimate way to mask regions where f is undefined.
Does a wireframe with rstride=1, cstride=len(x) still show anything?
Yes — you keep every row line, but cstride=len(x) steps past all interior columns and lands only on the very first column (), so you see near-horizontal "hoops" with essentially one vertical wire; strides act per-axis and independently.

Connections

  • 5.4.17 3D plots — surface, wireframe (Hinglish) — the parent topic these traps drill.
  • meshgrid and broadcasting — the floor-grid machinery behind every trap here.
  • NumPy vectorization — why Z=f(X,Y) beats loops (and avoids shape bugs).
  • 2D plots — line, scatter — the 1D-input habit these traps warn against.
  • Contour plots — same Z grid, viewed top-down; the (len(y),len(x)) shape choice pays off here.
  • Colormaps in Matplotlib — why surfaces take cmap and wireframes don't.
  • Matplotlib figure and axes objects — the projection='3d' axes trap.