Earth & Environmental Computing · Week 05 · Course session · 28 slides
Download HTML
Earth & Environmental Computing | Computational Methods for Earth & Environmental Science | 16-Week Course

NumPy and Array Thinking

A slice is a subregion, a mask is a map of a condition, a gradient is terrain slope.

Arrays · Shape · Masks · Grids · Gradients
Week 05·Course session·Open Course Materials | Adapt for your classroom·Week 5 | Course session
Learning Objectives

By the end of this lecture, you will be able to...

  • Explain what an array adds over a list: shape and elementwise math
  • Reshape a flat column into a 2-D grid and say which axis is which
  • Build and use boolean masks
  • Anticipate the reshape-order and image-origin gotchas
Contents

Today's Lecture

01From column to grid
03Display and gradients
02Masks: a map of a condition
04Putting it together
Recap

Last Week in Sixty Seconds

  • Functions return; printing functions return None
  • Tiny tests on hand-checkable data catch logic errors
  • Magnitude anchors catch silent unit bugs
  • Today: the same discipline, applied to data with SHAPE
Motivation

Earth Data Are Arrays

Everywhere you look

  • Elevation rasters and satellite images
  • Climate model output on grids
  • Seismic sections, radar scans

The shape IS the science

  • 2,555 values in a line: a time series
  • 2,500 values in 50 x 50: a map
  • Same numbers, different questions
Data

Today's Datasets

FileSourceCoverageUsed for
synthetic_temperature_grid.csvCourse-generated, seed 410050 × 50 cells over 10 × 8 kmReshaping, masks, colour maps
synthetic_elevation_grid.csvCourse-generated, seed 410050 × 50 cells over 10 × 8 kmGradients, slope, the lapse-rate fit
01

From Column to Grid

Earth data arrive as a flat column and have to be given back their shape.

01 · From column to grid

From Flat Column to Grid

Python

elevation = elev["elevation_m"].to_numpy().reshape(50, 50)

elevation.shape        # (50, 50)
elevation[0, 0]        # one corner cell
elevation[25, :]       # row 25: a west-east profile
elevation[:, 10]       # column 10: a south-north profile
  • reshape fills row by row: value 51 starts row two
  • Convention all semester: rows are y, columns are x
01 · From column to grid

Indexing a Grid

Python

# elevation[row, col]   and NOT (x, y)

elevation[0, 0]      # south-west corner cell
elevation[-1, -1]    # north-east corner cell
elevation[:25, :]    # southern half  (25, 50)
elevation[25:, :]    # northern half  (25, 50)
elevation[:, :10]    # western strip  (50, 10)
elevation[10:15, 20:25].shape   # (5, 5) window
  • row = position along y, south to north
  • col = position along x, west to east
  • elevation[25:, :] is the northern half
  • elevation[25, :] is one terrain profile

The plotting habit of (x, y) fights this all week; say row-column out loud.

01 · From column to grid

Vectorization: No Loops for Arithmetic

Python

temp_f = temp_c * 9 / 5 + 32        # whole 50x50 grid, one line

elevation.mean()                     # one number for the map
elevation.mean(axis=0)               # 50 column means: a profile
elevation.max() - elevation.min()    # total relief
  • The code reads like the equation
  • Aggregations collapse the grid: whole map, or one axis at a time
Pause and think

?

A flat column of 2,500 values becomes reshape(50, 50). Where does value number 51 land?

Row 0 holds values 1 to 50; row 1 starts at value 51
Row 1 (the second row), column 0: filling is row-major
Getting this wrong scrambles the whole map, with no error
02

Masks: a Map of a Condition

A boolean array is a map of where something is true.

02 · Masks

A Mask Is a Map of a Condition

Python

high = elevation > np.percentile(elevation, 75)

high.sum()               # 625 cells
high.mean()              # 0.25 of the area
temperature[high].mean() # 23.5 C, only where high
  • The mask has the same shape as the grid: it IS a map
  • Masks count, measure area, and select in one move
02 · Masks

Combining Conditions on Arrays

Python

t25 = np.percentile(temperature, 25)    # 22.71 C
e75 = np.percentile(elevation, 75)      # 247.7 m

cool_high = (temperature < t25) & (elevation > e75)
either    = (temperature < t25) | (elevation > e75)
not_cool  = ~(temperature < t25)

cool_high.sum()                  # 80 cells
100 * cool_high.mean()           # 3.2 % of the map
elevation[cool_high].mean()      # 250.4 m
  • Use & and |, never the words and / or
  • Parentheses around every comparison
  • Check mask.sum() before trusting any masked statistic
03

Display and Gradients

Two classic grid mistakes, and the spacing that slope depends on.

03 · Display and gradients

The Two Classic Grid Gotchas

1. Reshape order

  • Row-major filling, always
  • reshape(ny, nx), not (nx, ny)
  • Verify: grid[0, 0] == values[0]
  • and grid[1, 0] == values[nx]

2. Image origin

  • imshow counts rows downward by default
  • ax.imshow(grid) puts row 0 on top
  • ax.imshow(grid, origin="lower") puts row 0 at the bottom
  • An upside-down landscape is obvious in a plot, invisible in print
03 · Display and gradients

Every Gridded Figure, Same Rules

Python

fig, ax = plt.subplots(figsize=(6, 5))
image = ax.imshow(elevation, origin="lower", cmap="terrain")
ax.set_xlabel("Grid column")
ax.set_ylabel("Grid row")
fig.colorbar(image, ax=ax, label="Elevation (m)")
  • A colorbar with units is the map's axis label
03 · Display and gradients

Slope Needs Real Spacing

Python

dx_m = 204.1   # from the x_km column
dy_m = 163.3   # from the y_km column, and it differs from dx

dz_dy, dz_dx = np.gradient(elevation, dy_m, dx_m)
slope = np.sqrt(dz_dx**2 + dz_dy**2)      # m per m

slope_pct = 100 * slope                    # percent
slope_deg = np.degrees(np.arctan(slope))   # degrees
  • dz_dx = elevation change per meter, west-east
  • dz_dy = elevation change per meter, south-north
  • The two spacings differ here, so the order of arguments matters

Without spacing arguments the units are meters per grid cell: physically meaningless.

Pause and think

?

Your slope map has values near 40. Percent, degrees, or meters per cell? How do you know?

Whatever the code made it: trace the spacing arguments and the times-100
Units are decided by you, not discovered by the computer
On this gentle synthetic terrain the honest answer is under 1 percent
04

Putting It Together

Does temperature follow elevation on this grid?

04 · Putting it together

Does Temperature Follow Elevation?

01

Given

50 x 50 synthetic elevation and temperature grids on the same coordinates.

02

Solve

Flatten both with .ravel(), fit a line with np.polyfit, plot scatter plus fit.

03

Interpret

Slope times 100 = an apparent lapse rate in deg C per 100 m, sign included.

This synthetic field warms with height, +0.91 C per 100 m, so the sign is the discussion.

04 · Putting it together

Three Sentences to Keep

Slice

A slice is a subregion of the map: elevation[25:, :] is the north half.

Mask

A mask is a map of a condition: where is it steep, high, or cold?

Gradient

A gradient with real spacing is terrain slope with real units.

04 · Putting it together

Three Messages You Will Meet This Week

  • ValueError: cannot reshape array of size 2500 into (60, 60): count your cells
  • The ambiguous truth-value error: you wrote and/or where & / | belongs
  • IndexError: index 50 out of bounds: rows run 0 to 49
  • Each names its cause; read the bottom line first
04 · This week's lab

Lab 5: NumPy Arrays and Gridded Earth Data

Lab session

  • Reshape and verify the elevation grid
  • North/south comparison, one profile plot
  • Slope with correct units

Studio session

  • Lapse-rate scatter and fit
  • Joint mask percentage, column profile
  • Steep versus gentle temperature contrast
Pitfalls

Where This Week Goes Wrong

  • reshape(nx, ny) instead of (ny, nx)
  • origin left at the default, map upside down
  • and / or on arrays instead of & / |
  • Gradient without spacing: wrong units, no warning
Bridge

Connection to Next Week

This weekNext weekBridge idea
Grids indexed by positionPandas time seriesFrom cells in space to days in time: the index becomes a date
Summary

Key Takeaways

  • Shape is the science: line = series, grid = map
  • Rows are y, columns are x; reshape fills row-major
  • Masks count, measure, and select at once
  • Gradients need real spacing for real units
Discussion · Next week: pandas and time series

?

Name one gridded dataset from your own field. What would a mask of it show, and what would its gradient mean?