Assignment 4#
Due: Wednesday Sep 30th at 11:59 pm ET
An extended assignment
This assignment spans two weeks and is worth double the weight of a regular assignment. Plan accordingly, commit your work incrementally, and do not leave it to the last day.
The goal of this assignment is to carry out a reproducible climate data analysis with Xarray in JupyterLab, and to do it the way a professional would: with your analysis logic written as reusable functions in a Python module, your notebook reserved for calling those functions and visualizing results, and an AI coding agent used to accelerate the harder half with your review and ownership.
You will analyze sea surface temperature (SST) to characterize the El Niño–Southern Oscillation (ENSO) signal, building on the Advanced Xarray Operations lecture.
Background: what is ENSO, and why these functions?#
The El Niño–Southern Oscillation (ENSO) is the largest source of year-to-year climate variability on Earth. Every few years the equatorial Pacific swings between a warm phase (El Niño) and a cool phase (La Niña), and because the tropical Pacific is so vast, those temperature swings reorganize rainfall, droughts, and storm tracks around the globe. Climatologists summarize the whole phenomenon with a single number: the average sea surface temperature anomaly in the Niño-3.4 region (5°S–5°N, 170°W–120°W). When that index stays above +0.5 °C for several months, it’s an El Niño event; below −0.5 °C, a La Niña.
Every function you build in this assignment is a step toward computing and interpreting that index from raw SST. By the end, you will have rebuilt, from scratch, the core of how operational centers actually monitor ENSO.
What you are practicing
Xarray: the data model, selection,
groupbyclimatologies, anomalies, weighted means, rolling indices, composites,coarsenvs.interp, and trends.Reproducible environments: a pixi project and a notebook anyone can rerun.
Software structure: analysis functions in a module, notebooks for visualization only.
Agentic coding: using GitHub Copilot in Agent mode, guided by an
AGENTS.md.Judgment & QA: tests for your functions, and catching analysis the agent gets subtly wrong.
Notebook discipline
As covered in the class, your notebook should not contain your analysis functions. Notebooks are for visualization and investigating the results. Every function you write (or the agent writes) should live in src/utils.py, and your notebook imports them and uses them to explore and plot.
All deliverables from this assignment should live in a new directory named assignment-4/ in your geog313-assignments repository. Follow the same git discipline as previous assignments: stage explicitly by name (never git add .), commit in small focused steps with clear messages, and push as you go. Read the whole assignment before you start, so you can take notes as you implement the code.
Project layout#
Your assignment-4/ directory must follow this layout:
assignment-4/
pixi.toml environment, dependencies, and tasks
pixi.lock locked versions (managed by pixi)
.gitignore
src/
__init__.py makes src an importable package
utils.py ALL analysis functions
notebooks/
analysis.ipynb imports from src.utils; explores and plots ONLY
tests/
test_utils.py tests for the functions in src/utils.py as needed
results/ exported CSV / NetCDF / figures
AGENTS.md guidance for the agent
README.md what this is and how to reproduce it
reflection.md your written reflection (no AI)
Tip
Run JupyterLab from the assignment-4/ directory (pixi run jupyter lab). With src/__init__.py in place you can then write from src.utils import ... in your notebook.
Part 1: Reproducible environment (10 pts)#
From the root of your
geog313-assignmentsrepository, create the projectassignment-4and initialize with pixi.Set the platforms in
pixi.tomlto["osx-arm64", "osx-64", "linux-64"].Add the following packages:
xarray=2026.7.*,pooch,netcdf4,matplotlib,jupyterlabCreate the
src/,notebooks/,tests/, andresults/structure above, addsrc/__init__.py, and write aREADME.mdthat says what the project does and gives the command(s) for reproduction.Commit the scaffolding.
Part 2: The core functionality (60 pts)#
Do not use the agent for this part. You are strictly prohibited to use agentic coding for this part of the assignment. Writing the core by hand is how you build the Xarray fluency you will need to judge the agent’s work in Part 2.
In src/utils.py, write and document the following pure functions, then import and use them in notebooks/analysis.ipynb:
load_sst(): retrieve the NOAA ERSST v6 monthly SST file reproducibly withpooch(pinnedknown_hash), open it withxarraydroppingtime_bnds, and return theDataset. Use the source shown in Lecture 7:url = "https://raw.githubusercontent.com/HamedAlemo/advanced-geo-python/main/files/noaa.ersst.v6/sst.mnmean.nc"
subset_region(ds, lat_bounds, lon_bounds): subsetDatasetdsto a range of given lat/lon.monthly_climatology(da, baseline=("1991", "2020")): calculate the mean seasonal cycle forDataArraydaover a baseline period, usinggroupby.anomalies(da, clim): calculate monthly anomalies forDataArraydawith respect to mean seasonal cycleclimarea_weighted_mean(da): calculateda’s spatial mean weighted by the cosine of the latitude of the center of each pixel.
In the notebook, use these to:
Print a concise summary of the NOAA ERSST v6 monthly SST
Datasetand thesstvariable, and in 3–5 sentences explain howdims,coords, andattrsreflect the Xarray data model, and confirm the SST units / relevant CF attributes.Produce two separate plots for the Niño-3.4 region (5°S–5°N, 170°W–120°W): the area-weighted monthly mean SST, and the area-weighted monthly SST anomaly time series.
Commit src/utils.py and the current version of the notebook.
Part 3: The agentic extension (80 pts)#
Now use GitHub Copilot in Agent mode, guided by an AGENTS.md you write, to add the more advanced analysis. The agent adds functions to src/utils.py; your notebook imports them and visualizes the results. Review every change before accepting it; you own this code and this science.
3.1 Write your AGENTS.md#
Before prompting the agent, write AGENTS.md. It must, at minimum, tell the agent: this is a pixi-only project; all analysis functions go in src/utils.py; the notebook is for visualization only; do not create new files or edit reflection.md; the domain rules (ERSST v6 via pooch, cosine-latitude weighting, 1991–2020 climatology baseline, units °C); run tests with pixi run test; and a definition of done. Commit it.
3.2 Functions for the agent to add#
rolling_index(anom_series, window=3): calculate centered rolling mean of the area-averaged anomalies (an ENSO-style index).detect_events(index, threshold=0.5, min_months=3): detect contiguous warm events (index≥ +0.5 °C for ≥ 3 consecutive months) and cold events (index≤ −0.5 °C for ≥ 3 consecutive months); return their start/end dates and peak magnitudes.composite(anom_field, event_months): build spatial composite maps by taking the mean of an anomaly field (warm or cold events) over a set of event months.coarsen_to_4deg(field)andinterp_to_4deg(field): create coarser (4°) anomaly maps from 2° anomalies using (coarsen(...).mean()and.interp()).linear_trend(anom_field): calculate a per-pixel linear trend (°C per decade) viapolyfit(dim='time', deg=1).
3.3 In the notebook, visualize and export#
Plot the ENSO index time series with warm/cold events marked, and export the area-averaged monthly anomaly and 3-month index to
results/enso_index.csv(columnstime, anomaly, rolling_anomaly). Report the top three El Niño and three La Niña events by peak magnitude with start/end dates.Plot the warm and cold composite anomaly maps on the same color scale, and save them to
results/composite_warm.ncandresults/composite_cold.nc. Interpret the patterns in 3–4 sentences.Plot the 4° coarsen vs. interp fields side-by-side for one representative month and discuss the differences.
Plot the trend map (°C per decade) as your headline figure, and comment on the pattern and its caveats.
Part 4: Tests and quality assurance (30 pts)#
Because your logic lives in src/utils.py, it can be tested.
Add
pytestto the project and define atesttask so the suite runs withpixi run test.Write tests in
tests/test_utils.pythat check the functions on small synthetic inputs where you know the answer, for example:area_weighted_meanon a constant field returns that constant; anomalies over the baseline have (near) zero mean;coarsen_to_4degreturns the expected shape;detect_eventsfinds a known event in a hand made series.The agent may help write tests, but verify each test is meaningful. It must actually fail if the function were wrong. Iterate until
pixi run testpasses.
Part 5: Reflection (20 pts)#
Write reflection.md yourself, without AI assistance (400–600 words):
Manual vs. agentic for data analysis: How did writing the Part 1 functions by hand compare to having the agent write the Part 2 functions? What did the agent do well, and what did you have to fix?
Catching bad science: Describe the incorrect or misleading analysis the agent produced and how your own understanding let you catch it. Would you have caught it if you had not written Part 1 by hand?
Context engineering: How did your
AGENTS.mdshape the agent’s behavior, and what would you tighten next time?
Deliverables#
Committed to your private geog313-assignments repository under assignment-4/, following the project layout above:
pixi.toml,pixi.lock,.gitignore.src/utils.pywith all analysis functions;src/__init__.py.notebooks/analysis.ipynb: executed, with all figures rendered on GitHub, importing fromsrc.utilsand containing visualization/analysis only.tests/test_utils.py;pixi run testpasses.results/enso_index.csv,results/composite_warm.nc, andresults/composite_cold.nc.AGENTS.md,README.md, andreflection.md(written without AI).A commit history showing incremental, reviewed development.