Skip to content

Commit 9e5164d

Browse files
committed
Add concepts, recipes, and API docs structure
1 parent cf8df80 commit 9e5164d

File tree

10 files changed

+470
-12
lines changed

10 files changed

+470
-12
lines changed

docs/api.md

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
# API reference
1+
# API reference (legacy)
22

3-
The functions below are rendered directly from the source code using
4-
`mkdocstrings`, so the documentation always matches the currently released
5-
version of the package.
3+
This project originally exposed a small demo API for ruled time hull
4+
visualizations. Those helpers now live alongside the broader
5+
`climate_cube_math` package inside `code/`. For the comprehensive module-level
6+
reference, see the [`climate_cube_math` API page](api/climate_cube_math.md).
67

7-
## Demo helpers
8+
The older demo utilities remain available for anyone still exploring the ruled
9+
hull prototype:
810

9-
::: climate_cube_math.demo.make_demo_event
10-
11-
## Visualization
12-
13-
::: climate_cube_math.hulls.plot_ruled_time_hull
11+
- `climate_cube_math.demo.make_demo_event`
12+
- `climate_cube_math.hulls.plot_ruled_time_hull`

docs/api/climate_cube_math.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# `climate_cube_math` API Reference
2+
3+
This page documents the main public modules and functions of the
4+
`climate_cube_math` package. The API is auto-generated from the docstrings using
5+
`mkdocstrings`.
6+
7+
## Data loading
8+
9+
::: climate_cube_math.data.sentinel2
10+
11+
## Vegetation indices
12+
13+
::: climate_cube_math.indices.vegetation
14+
15+
## Statistics: anomalies and rolling windows
16+
17+
::: climate_cube_math.stats.anomalies
18+
::: climate_cube_math.stats.rolling
19+
::: climate_cube_math.stats.correlation
20+
::: climate_cube_math.stats.tails
21+
22+
## Visualization helpers
23+
24+
::: climate_cube_math.viz.lexcube_viz
25+
::: climate_cube_math.viz.qa_plots
26+
27+
## Utilities
28+
29+
::: climate_cube_math.utils.chunking
30+
::: climate_cube_math.utils.reference

docs/concepts/climate_cubes.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# What is a climate cube?
2+
3+
A **climate cube** is an `xarray.DataArray` or `xarray.Dataset` whose data are
4+
organized along shared space-time axes. The most common arrangement in this
5+
project is `(time, y, x)` for single-band cubes or `(time, y, x, band)` for
6+
multi-band collections. Each pixel stores the value of an environmental
7+
variable (e.g., reflectance, temperature, precipitation) measured at a given
8+
location `(y, x)` and instant `time`.
9+
10+
## Why cubes?
11+
12+
Satellite constellations (Sentinel-2, Landsat), gridded climate products
13+
(GRIDMET, PRISM), and model reanalyses are all naturally expressed as climate
14+
cubes because their data are already gridded over regular spatio-temporal
15+
coordinates. By sticking with `xarray`, we get labeled dimensions, lazy loading
16+
(with `dask`), and robust metadata handling.
17+
18+
This package focuses on *streaming* cubes rather than requiring local
19+
downloads. Utilities such as `climate_cube_math.data.sentinel2.load_s2_cube`
20+
wrap remote APIs (e.g., Cubo) so that users can request an area/time window and
21+
immediately operate on the returned `xarray` cube inside notebooks or scripts.
22+
23+
## Cube processing layers
24+
25+
The rest of the documentation walks through the primary layers of the
26+
`climate_cube_math` workflow:
27+
28+
1. **Data layer** – load space-time cubes (`load_s2_cube`).
29+
2. **Indices & anomalies layer** – derive vegetation indices and z-scores
30+
(`compute_ndvi_from_s2`, `zscore_over_time`).
31+
3. **Synchrony layer** – measure rolling correlation and tail dependence versus
32+
a reference pixel (`rolling_corr_vs_center`, `rolling_tail_dep_vs_center`).
33+
4. **Visualization layer** – explore cubes interactively with the Lexcube
34+
widget (`show_cube_lexcube`) and QA plots (`plot_median_over_space`).
35+
36+
## Conceptual cube example
37+
38+
```python
39+
import xarray as xr
40+
41+
# Generic climate cube shape
42+
# time: T time steps, y: rows, x: columns
43+
cube = xr.DataArray(
44+
data, # shape (T, Y, X)
45+
coords={"time": time_coords, "y": y_coords, "x": x_coords},
46+
dims=("time", "y", "x"),
47+
name="my_variable",
48+
)
49+
```
50+
51+
Once data are in this form, every operation in `climate_cube_math` simply
52+
composes transformations on the cube without ever breaking the labeled axes.

docs/concepts/cube_math.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Climate cube math primitives
2+
3+
The `climate_cube_math` package collects reusable *cube math* primitives that
4+
operate directly on `xarray` objects without breaking their labeled dimensions.
5+
These primitives fall into three categories.
6+
7+
## Temporal operations
8+
9+
- **Z-scores & anomalies**`stats.anomalies.zscore_over_time` and related
10+
helpers standardize or demean each pixel.
11+
- **Rolling reductions**`stats.rolling` defines moving-window statistics that
12+
are later specialized by the correlation/tail modules.
13+
- **Lag/lead transforms** – differencing, smoothing, and other future helpers can
14+
be layered on time-centered cubes to highlight rate-of-change or persistence.
15+
16+
## Spatial operations
17+
18+
- **Coarsening & striding**`utils.chunking.coarsen_and_stride` reduces spatial
19+
resolution and sub-samples time for performance.
20+
- **Masks & neighborhoods** – boolean masks (e.g., from QA bands or external
21+
polygons) can gate where calculations run. Neighborhood operations such as
22+
smoothing or gradients preserve the `y`/`x` axes while aggregating across
23+
nearby pixels.
24+
25+
## Metadata conventions
26+
27+
All primitives expect the standard `(time, y, x)` dimension order (with optional
28+
band/variable axis). Coordinates should be named `time`, `y`, `x`, and any extra
29+
bands should be labeled via the `band` or `variable` dimension. Attributes are
30+
carried through operations so that downstream plots know the data source,
31+
projection, or scaling applied.
32+
33+
## Putting it together
34+
35+
By composing these primitives we can:
36+
37+
1. Load a cube.
38+
2. Apply temporal standardization (z-scores).
39+
3. Reduce spatial resolution or mask invalid data.
40+
4. Derive rolling synchrony metrics.
41+
5. Visualize the resulting cubes via Lexcube and QA plots.
42+
43+
The abstraction lets you swap in other backends (e.g., GRIDMET temperature
44+
cubes) while keeping the same math pipeline.

docs/concepts/ndvi_zscore.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# NDVI z-scores
2+
3+
## What is NDVI?
4+
5+
The **Normalized Difference Vegetation Index (NDVI)** measures vegetation
6+
vigour by contrasting the high reflectance of healthy plants in the near
7+
infrared (NIR) with their low reflectance in the red band:
8+
9+
\[
10+
NDVI = \frac{\text{NIR} - \text{Red}}{\text{NIR} + \text{Red}}
11+
\]
12+
13+
Values range from -1 to 1. Dense, photosynthetically active vegetation
14+
produces high NDVI, while bare soil, snow, or water produce lower values.
15+
16+
## Why standardize NDVI?
17+
18+
Each pixel experiences its own seasonal cycle and lighting geometry. To detect
19+
unusual conditions (drought, disturbance, phenology shifts) we standardize each
20+
pixel relative to its own history. This is done with a **z-score** over time:
21+
22+
\[
23+
z = \frac{x_t - \mu_{pixel}}{\sigma_{pixel}}
24+
\]
25+
26+
where \(\mu_{pixel}\) and \(\sigma_{pixel}\) are the pixel's mean and
27+
standard deviation across the available time series. The resulting NDVI
28+
z-score cube highlights anomalies rather than absolute greenness.
29+
30+
## Mapping to `climate_cube_math`
31+
32+
The package provides two key helpers:
33+
34+
- `climate_cube_math.indices.vegetation.compute_ndvi_from_s2` turns Sentinel-2
35+
Level-2A surface reflectance cubes into NDVI cubes.
36+
- `climate_cube_math.stats.anomalies.zscore_over_time` standardizes each pixel
37+
along the time dimension.
38+
39+
Together they produce the NDVI z-score cubes that downstream statistics and
40+
visualizations consume.
41+
42+
## End-to-end example
43+
44+
```python
45+
from climate_cube_math.data.sentinel2 import load_s2_cube
46+
from climate_cube_math.indices.vegetation import compute_ndvi_from_s2
47+
from climate_cube_math.stats.anomalies import zscore_over_time
48+
49+
s2 = load_s2_cube(
50+
lat=43.89,
51+
lon=-102.18,
52+
start="2023-06-01",
53+
end="2023-06-30",
54+
edge_size=256,
55+
)
56+
57+
ndvi = compute_ndvi_from_s2(s2)
58+
ndvi_z = zscore_over_time(ndvi)
59+
```

docs/concepts/rolling_stats.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Rolling correlation & tail dependence
2+
3+
## Rolling windows in time
4+
5+
Rolling statistics look at a fixed-width window (e.g., 90 days) that slides
6+
through time. At each step we compute a summary from only the values inside the
7+
window, producing a new cube aligned to the window center. This approach keeps
8+
temporal context while remaining responsive to recent dynamics.
9+
10+
## Correlation vs a reference pixel
11+
12+
When studying spatial synchrony we often compare every pixel to a single
13+
reference location. In `climate_cube_math` the default reference is the center
14+
pixel in the requested Sentinel-2 chip. The function
15+
`climate_cube_math.stats.correlation.rolling_corr_vs_center` computes the
16+
Pearson correlation between each pixel and the reference pixel within every
17+
rolling window. The resulting cube reveals how tightly each pixel's NDVI
18+
fluctuations track the anchor over time.
19+
20+
## Tail dependence
21+
22+
Mean correlation can miss asymmetric extremes. Tail dependence focuses on the
23+
probability that two pixels jointly experience unusually low (bottom tail) or
24+
high (top tail) values. The helper
25+
`climate_cube_math.stats.tails.rolling_tail_dep_vs_center` implements a rolling
26+
partial Spearman correlation restricted to the lower or upper tail of the data.
27+
It returns three cubes: bottom-tail, top-tail, and their difference (bottom -
28+
top). Large positive differences indicate areas that co-experience stress with
29+
the center pixel more often than they share unusually high NDVI.
30+
31+
## Conceptual snippets
32+
33+
```python
34+
from climate_cube_math.stats.correlation import rolling_corr_vs_center
35+
from climate_cube_math.stats.tails import rolling_tail_dep_vs_center
36+
37+
corr_cube = rolling_corr_vs_center(ndvi_z, window_days=90, min_t=5)
38+
39+
bottom_tail, top_tail, diff_tail = rolling_tail_dep_vs_center(
40+
ndvi_z, window_days=90, min_t=5, b=0.5
41+
)
42+
```
43+
44+
By chaining these functions onto an NDVI z-score cube we transform the original
45+
vegetation signal into diagnostics of synchrony and shared extremes.

docs/recipes/s2_corr_center.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Rolling correlation cube vs center pixel (Sentinel-2 NDVI z-scores)
2+
3+
This guide extends the NDVI z-score workflow by computing rolling correlation
4+
between every pixel and the center pixel of the cube. The output highlights
5+
areas that move in sync with the reference location.
6+
7+
Steps:
8+
9+
1. Load Sentinel-2 data and compute NDVI z-scores (same as previous recipe).
10+
2. Downsample for faster rolling statistics (optional but recommended).
11+
3. Use `rolling_corr_vs_center` to compute correlation cubes.
12+
4. Visualize with Lexcube and inspect QA summaries.
13+
14+
```python
15+
from climate_cube_math.data.sentinel2 import load_s2_cube
16+
from climate_cube_math.indices.vegetation import compute_ndvi_from_s2
17+
from climate_cube_math.stats.anomalies import zscore_over_time
18+
from climate_cube_math.stats.correlation import rolling_corr_vs_center
19+
from climate_cube_math.utils.chunking import coarsen_and_stride
20+
from climate_cube_math.viz.lexcube_viz import show_cube_lexcube
21+
from climate_cube_math.viz.qa_plots import plot_median_over_space
22+
23+
s2 = load_s2_cube(
24+
lat=43.89,
25+
lon=-102.18,
26+
start="2023-06-01",
27+
end="2024-09-30",
28+
edge_size=1028,
29+
resolution=10,
30+
cloud_lt=40,
31+
)
32+
33+
ndvi = compute_ndvi_from_s2(s2)
34+
ndvi_z = zscore_over_time(ndvi)
35+
36+
# Downsample for performance
37+
ndvi_z_ds = coarsen_and_stride(ndvi_z, coarsen_factor=4, time_stride=2)
38+
39+
# Rolling correlation vs center pixel
40+
corr_cube = rolling_corr_vs_center(
41+
ndvi_z_ds,
42+
window_days=90,
43+
min_t=5,
44+
)
45+
46+
# Lexcube visualization of correlation cube
47+
corr_widget = show_cube_lexcube(
48+
corr_cube.clip(-1, 1),
49+
title="Rolling correlation vs center pixel (NDVI z-scores)",
50+
cmap="RdBu_r",
51+
vmin=-1,
52+
vmax=1,
53+
)
54+
55+
# In a notebook: corr_widget.plot()
56+
57+
# QA: median correlation over space
58+
ax = plot_median_over_space(
59+
corr_cube,
60+
ylabel="Median correlation",
61+
title="Median corr vs center (rolling 90 days)",
62+
ylim=(-1, 1),
63+
)
64+
```
65+
66+
Use the resulting cube to identify coherent ecological zones, potential
67+
teleconnections, or areas where vegetation dynamics are decoupled from the
68+
reference site.

0 commit comments

Comments
 (0)