Coordinate grids
meshgrid, mgrid, ogrid — how you compute a function of position without a loop.
np.meshgridnp.mgridnp.ogridnp.indicesnp.hypotsparse gridsWatch it happen
Play it through, or step back and forth yourself.
xx (column number)yy (row number)You want to evaluate something at every position of a grid — a distance from the centre, a colour ramp, a function of two variables. Broadcasting can do it, but first you need arrays holding the x and y of every cell.
The idea
Some values depend on where they are rather than on other data: a distance from a centre, a colour ramp, a height field, a decision boundary drawn across a plane. For those you need arrays holding the coordinates themselves.
xx, yy = np.meshgrid(np.arange(4), np.arange(3))
xx # [[0 1 2 3], [0 1 2 3], [0 1 2 3]] — column number
yy # [[0 0 0 0], [1 1 1 1], [2 2 2 2]] — row numberTwo arrays of the same shape: one holds every cell's x, the other every cell's y. After that, any function of position is ordinary vectorised arithmetic — no loops:
xx + yy
np.sqrt(xx**2 + yy**2)
np.sin(xx / 3) * np.cos(yy / 3)Three spellings, and an ordering trap
xx, yy = np.meshgrid(x, y) # columns first
yy, xx = np.mgrid[0:3, 0:4] # ROWS first — note the swap
yy, xx = np.ogrid[0:3, 0:4] # sparse: shapes (3,1) and (1,4)meshgrid takes arrays and returns x-then-y. mgrid takes slice syntax and returns them in axis order, which for a 2-D grid means rows first. Getting that backwards gives you a transposed result that looks almost right, so it's worth reading the line carefully.
ogrid is the efficient one. Rather than two full grids it returns shapes (3, 1) and (1, 4) and lets broadcasting do the expansion — same answer, a fraction of the memory. For a 4000×4000 grid that's the difference between 256MB of coordinates and 64KB. meshgrid has a sparse=True that does the same thing.
Distance fields
The pattern you'll use most:
yy, xx = np.mgrid[0:H, 0:W]
d = np.hypot(xx - cx, yy - cy) # distance from (cx, cy) at every pixel
mask = d < 40 # a circular mask
falloff = np.clip(1 - d / 40, 0, 1) # a soft radial gradientnp.hypot(a, b) is sqrt(a**2 + b**2) without the intermediate overflow. Circular crops, vignettes, spotlight effects and radial blurs all start here.
You have already used this
The sunset in the first capstone was built exactly this way — a coordinate grid, a vertical ramp for the sky, a radial falloff for the sun, a sine wave for the hills, and np.where to paint the ground in. Go back and read that setup code now; it should look completely ordinary.
np.indices((3, 4)) is a related shortcut that stacks the same coordinate arrays into one array of shape (2, 3, 4).
Practice
Write it yourself. The answer is there when you want it.
Putting the kettle on…
Starting up…
Write it yourself
not gradedBuild xx, yy with np.meshgrid over arange(4) and arange(3), and print both grids and their sum. Do the same with np.ogrid, printing the shapes so you can see how much less it stores for the same answer. Finish with a 9×9 np.mgrid and a circle: distance from the centre with np.hypot, under a radius of 3, as ints.
Your turn
3 exercises. Write the code yourself, then press Check — a nudge and the answer are there if you want them.
Build coordinate grids for a 3 x 4 area with np.meshgrid and return xx + yy.
Use np.mgrid[0:3, 0:4] and return just the row-number grid.
On a 9 x 9 grid, return an integer array that is 1 where the distance from (4, 4) is less than 3, and 0 elsewhere.
