One day, one bird, eight satellite lenses. A seasonal habitat model for the house sparrow in Albuquerque — where fixing one quiet assumption about absence points reversed the result, and the honest answer turned out to be two behaviours instead of one.
Python · scikit-learn · Google Earth Engine · Sentinel-2 · Landsat 8 · NLCD · GBIF and eBird APIs · Folium · Git / GitHub Pages
Bars are feature importance — how much the model leans on each driver. The line above is direction: how far sparrow locations sit from surveyed background. Warmth barely moves all year. Greenness reverses.
GBIF and eBird hold thousands of dated house sparrow records for the Albuquerque basin. Train on the pile and you get a map that looks authoritative and means very little.
Every point is a bird being somewhere and a human being there with a phone. That isn't a footnote — it's the central methodological problem of the field, and version one walked straight into it.
Two behaviours wearing one habitat map. Average them together and both disappear.
Measured against places birders actually surveyed, the thermal association barely moves all year. Vegetation does the opposite.
| Effect size | Winter | Spring | Summer | Fall |
|---|---|---|---|---|
| Warmth | +0.36 | +0.42 | +0.28 | +0.35 |
| Greenness | +0.41 | −0.19 | +0.08 | +0.06 |
| Development | +0.56 | +0.62 | +0.18 | +0.50 |
| Pavement | +0.27 | +0.61 | 0.00 | +0.26 |
Positive = sparrows sit above the surveyed background. Greenness is the only row that reverses.
In a high-desert winter the only green is irrigated or evergreen, so it's scarce and worth competing for. By spring the whole basin greens up, green stops distinguishing anywhere from anywhere, and the signal dissolves.
Summer flattens everything else — pavement lands at exactly zero. For three months the sparrows are nearly indistinguishable from every other bird in town, except that they're still in the warmer places.
The first answer was a map of birdwatchers.
The first environmental layers were placeholder math. The output rendered as a perfect radial ripple — clean rings spreading from the city center. Sparrows do not live in concentric rings. The model had faithfully learned my placeholder and reported it back as ecology.
The second nearly got published. Absence points were scattered at random, which sounds neutral and isn't: random points land on unsurveyed desert, so the model could win by learning where birders go. Drawing background from other species' actual records instead:
| Effect size | Random | Target-group |
|---|---|---|
| Land surface temp | −0.32 | +0.35 |
| NDVI greenness | +0.39 | +0.09 |
| Built density | +0.61 | +0.47 |
| Impervious | +0.41 | +0.29 |
Sparrow values never moved. The background did — unsurveyed desert runs 2°C hotter than places people go.
Temperature reversed sign. A confident vegetation finding collapsed to nothing. Nothing in the training loop flagged either mistake — domain knowledge isn't a complement to modeling, it's the error-detection layer.
Summer AUC is 0.630 and nearly every driver flattens to zero. Either the birds genuinely disperse after breeding, or three months of peak birding saturate the map. Effort covariates from complete eBird checklists would separate those.
House sparrow is a generalist commensal. The same pipeline on a piñon-juniper specialist, compared side by side, turns one map into a finding about which spectral lens each species loads onto.
If a city bird tracks the irrigated, shaded fraction of the built environment, a suitability surface is also a map of where the city buffers its own heat — and its inverse marks the blocks where people suffer in July. That's a biomimicry brief hiding inside a species model.
Two interactive maps: one animates January through December, one lets you toggle seasons side by side. Code and the full write-up are public.
| Skill | What I did with it |
|---|---|
| AI Modeling | |
| Sampling bias correction | Replaced randomly placed absence points with target-group background sampling, drawn from other species' records, so the model measured habitat rather than where birdwatchers go. |
| Feature interpretation | Separated feature importance, which variable a model relies on, from effect direction, whether the animal prefers more or less of it. They answer different questions and are routinely confused. |
| Evaluation judgment | Read a drop in accuracy from 0.73 to 0.63 correctly — as evidence the corrected model was solving the harder, right problem instead of the easier, wrong one. |
| AI Engineering | |
| Reproducible experiments | Built the bias correction as a configuration flag, so both versions run from identical code and can be compared directly rather than edited between runs. |
| Pipeline staging | Separated slow satellite processing from fast rendering, so changing a layout takes seconds instead of re-running twelve months of imagery queries. |
| Automated diagnostics | Wrote reports that print effect direction and seasonal breakdowns on every run, so the model's claims get checked without anyone remembering to check them. |