08 · Stay lazy until the answer is requested¶
Context¶
Real cube archives quickly outgrow memory. Lazy arrays let us describe a method first and materialize only the final analysis.
Question¶
Can a composed pipe preserve Dask-backed execution while computing a compact spatial summary from real observations?
Analysis story¶
We open the reviewed fixture with chunks, compose anomaly and variance verbs, confirm that the result stays lazy, and compute only the final map.
Data used in this lesson¶
Every value comes from the PRISM Group at Oregon State University's AN91d daily 4 km climate product. This repository carries a small Boulder-region extract for 1–30 January 2024 so the lesson runs offline without replacing observations with generated values. The data validation page records source URLs, terms, checksums, bounds, units, and acceptance tests.
# Record the exact code imported by this notebook kernel.
import cubedynamics as cd
print(cd.version_info())
CubeDynamics 0.1.0rc3 Artifact: development checkout Code: git:a852137944cabf238e9321f2eecf09e483c9b9f5 Imported from: /home/runner/work/cubedynamics/cubedynamics/src/cubedynamics Distribution: /opt/hostedtoolcache/Python/3.11.16/x64/lib/python3.11/site-packages
Prepare · Open the official extract as a chunked cube¶
from pathlib import Path
import xarray as xr
# The checked-in extract makes the lesson reproducible without a live service.
data_path = next(
candidate / "tests" / "fixtures" / "real_data" / "prism_boulder_january_2024.nc"
for candidate in (Path.cwd(), *Path.cwd().parents)
if (candidate / "tests" / "fixtures" / "real_data" / "prism_boulder_january_2024.nc").exists()
)
cube = xr.open_dataset(
data_path,
engine="scipy",
chunks={"time": 10, "y": 12, "x": 12},
)["tmax"]
assert cube.attrs["is_synthetic"] == 0
assert hasattr(cube.data, "chunks")
cube
<xarray.DataArray 'tmax' (time: 30, y: 24, x: 24)> Size: 69kB
dask.array<open_dataset-tmax, shape=(30, 24, 24), dtype=float32, chunksize=(10, 12, 12), chunktype=numpy.ndarray>
Coordinates:
* time (time) datetime64[ns] 240B 2024-01-01 2024-01-02 ... 2024-01-30
* y (y) float64 192B 40.5 40.46 40.42 40.37 ... 39.67 39.62 39.58 39.54
* x (x) float64 192B -105.7 -105.7 -105.7 ... -104.9 -104.8 -104.8
Attributes:
long_name: daily maximum air temperature
standard_name: air_temperature
units: degC
source_variable: PRISM tmax
is_synthetic: 0Pipe · Describe the method without triggering computation¶
from cubedynamics import pipe, verbs as v
lazy_variability = (
pipe(cube)
| v.anomaly(dim="time")
| v.variance(dim="time")
).unwrap()
assert hasattr(lazy_variability.data, "chunks")
lazy_variability
<xarray.DataArray 'tmax' (time: 1, y: 24, x: 24)> Size: 2kB
dask.array<getitem, shape=(1, 24, 24), dtype=float32, chunksize=(1, 12, 12), chunktype=numpy.ndarray>
Coordinates:
* y (y) float64 192B 40.5 40.46 40.42 40.37 ... 39.67 39.62 39.58 39.54
* x (x) float64 192B -105.7 -105.7 -105.7 ... -104.9 -104.8 -104.8
Dimensions without coordinates: time
Attributes: (12/13)
long_name: daily maximum air temperature
standard_name: air_temperature
units: degC^2
source_variable: PRISM tmax
is_synthetic: 0
transform_operation: anomaly
... ...
semantic_name: variance of anomaly of tmax
semantic_kind: summary
semantic_category: summary
summary_operation: variance
summary_dimensions: time
semantic_units: degC^2Figure · Compute only the final spatial answer¶
import matplotlib.pyplot as plt
variability = lazy_variability.compute()
assert not hasattr(variability.data, "chunks")
fig, ax = plt.subplots(figsize=(6.5, 4.5), constrained_layout=True)
variability.plot(ax=ax, cmap="viridis", cbar_kwargs={"label": "Anomaly variance (°C²)"})
ax.set_title("January maximum-temperature variability")
plt.show()
Data used¶
| Field | Frozen analysis input |
|---|---|
| Provider | PRISM Group, Oregon State University |
| Product | AN91d daily 4 km time series |
| Dates | 2024-01-01 to 2024-01-30 |
| Fixture | tests/fixtures/real_data/prism_boulder_january_2024.nc |
| Provenance record | tests/fixtures/real_data/prism_boulder_january_2024.provenance.json |
The PRISM source reference describes current catalog support; the fixture record above identifies the observations used here. Data validation documents checksums and acceptance checks. The analytical baseline and thresholds belong to this story, not the provider.
Reproduce¶
Clone the repository, then run these commands from its root:
python -m pip install -e ".[vignettes]"
python scripts/run_vignettes.py docs/vignettes/lazy_composition.ipynb
No network is needed after installation. Open the downloaded notebook in
Jupyter and run all cells to see the same figures. The website executes these
cells during its strict build. Environment setup
and the vignette contract explain the workflow.
The first code cell prints cd.version_info() so a rendered result can be tied
to a package path and, for development checkouts, a Git commit.