Skip to content

Primitives — Layer 0 Pure Functions

core — Base Infrastructure

def compose(*operators: Callable) -> Callable:
    """Compose multiple callables left-to-right."""

def identity(ds: xr.Dataset) -> xr.Dataset:
    """Pass-through operator. Useful as a default/placeholder."""

validation — Data Harmonization

def validate_longitude(ds, target_range="-180_to_180") -> xr.Dataset
def validate_latitude(ds) -> xr.Dataset
def validate_time(ds, calendar="standard") -> xr.Dataset
def rename_coords(ds, mapping: dict) -> xr.Dataset
def sort_coords(ds, dims: list[str] | None = None) -> xr.Dataset
def set_coord_attrs(ds, coord, units, standard_name, long_name) -> xr.Dataset

crs — Coordinate Reference Systems

def assign_crs(ds, crs="EPSG:4326") -> xr.Dataset
def reproject(ds, target_crs, resolution=None) -> xr.Dataset
def get_crs(ds) -> pyproj.CRS | None

subset — Spatial and Temporal Selection

def subset_bbox(ds, lon_bnds, lat_bnds) -> xr.Dataset
def subset_time(ds, time_min, time_max) -> xr.Dataset
def subset_where(ds, mask: xr.DataArray, drop=False) -> xr.Dataset
def subset_variables(ds, variables: list[str]) -> xr.Dataset

masks — Spatial Masks

def add_land_mask(ds) -> xr.Dataset
def add_ocean_mask(ds, ocean="global") -> xr.Dataset
def add_country_mask(ds, country="spain") -> xr.Dataset
def add_region_mask(ds, region_name, regions=None) -> xr.Dataset

regrid — Grid Transformations

def regrid_linear(ds, target_lon, target_lat) -> xr.Dataset
def regrid_nearest(ds, target_lon, target_lat) -> xr.Dataset
def regrid_cubic(ds, target_lon, target_lat) -> xr.Dataset
def coarsen(ds, factor: dict, method="mean") -> xr.Dataset
def refine(ds, factor: dict, method="linear") -> xr.Dataset

Implementation strategy: extract lat/lon/values as numpy arrays, use scipy.interpolate.RegularGridInterpolator for rectilinear grids and scipy.interpolate.griddata for irregular grids. For nearest-neighbor and radius-based queries, use sklearn.neighbors.BallTree or KDTree which handle haversine distance natively.


interpolation — Gap Filling and Resampling

def fillnan_spatial(ds, method="linear", max_gap=None) -> xr.Dataset
def fillnan_temporal(ds, method="linear", max_gap=None) -> xr.Dataset
def fillnan_rbf(ds, kernel="thin_plate_spline", neighbors=None) -> xr.Dataset
def resample_time(ds, freq="1D", method="mean") -> xr.Dataset

Uses scipy.interpolate.griddata for spatial, scipy.interpolate.RBFInterpolator for RBF-based filling, and xarray's built-in .resample() for temporal.


detrend — Climatology, Anomalies, and Filtering

def calculate_climatology(ds, freq="day") -> xr.Dataset
def calculate_climatology_smoothed(ds, freq="day", window=60) -> xr.Dataset
def remove_climatology(ds, climatology) -> xr.Dataset
def add_climatology(ds, climatology) -> xr.Dataset
def calculate_anomalies(ds, freq="day", smoothing=None) -> xr.Dataset
def lowpass_filter(ds, dim="time", cutoff=30, order=3) -> xr.Dataset

encoders — Coordinate Encodings

# Spatial coordinate transforms
def lonlat_to_cartesian(ds) -> xr.Dataset
def cartesian_to_lonlat(ds) -> xr.Dataset
def lon_360_to_180(coord: np.ndarray) -> np.ndarray
def lon_180_to_360(coord: np.ndarray) -> np.ndarray

# Cyclical / positional encodings (DataArray-native; feature encoders add a trailing feature_dim)
def cyclical_encode(da: xr.DataArray, *, period: float) -> xr.Dataset  # sin / cos
def fourier_features(da: xr.DataArray, *, num_freqs: int, scale: float = 1.0, feature_dim: str = "feature") -> xr.DataArray
def random_fourier_features(da: xr.DataArray, *, num_features: int, sigma: float = 1.0, seed: int | None = None, input_dim: Hashable | None = None, feature_dim: str = "feature") -> xr.DataArray
def positional_encoding(da: xr.DataArray, *, num_freqs: int, include_input: bool = True, feature_dim: str = "feature") -> xr.DataArray

# Temporal encodings (DataArray-native; take the time coordinate / variable)
def encode_time_cyclical(time: xr.DataArray, *, components=("dayofyear", "hour")) -> xr.Dataset
def encode_time_ordinal(time: xr.DataArray, *, reference_date=None, unit: str = "D") -> xr.DataArray

discretize — Binning and Gridding

def bin_2d(da, target_lon, target_lat, statistic="mean") -> xr.DataArray
def bin_2d_time(da, target_lon, target_lat, target_time, statistic="mean") -> xr.DataArray
def histogram_2d(da, bins_x, bins_y) -> xr.DataArray
def points_to_grid(lons, lats, values, target_lon, target_lat, method="nearest") -> xr.DataArray

Implementation replaces pyinterp.Binning2D with scipy.stats.binned_statistic_2d for the core binning and sklearn.neighbors.BallTree for nearest-neighbor assignment. Grid definition uses dataclasses instead of odc-geo:

@dataclass
class Grid:
    lon: np.ndarray
    lat: np.ndarray

    @classmethod
    def from_bounds(cls, lon_bnds, lat_bnds, resolution): ...

    @classmethod
    def from_dataset(cls, ds): ...

@dataclass
class SpaceTimeGrid(Grid):
    time: np.ndarray | pd.DatetimeIndex

    @classmethod
    def from_bounds(cls, lon_bnds, lat_bnds, resolution, time_min, time_max, freq): ...

extremes — Extreme Value Analysis

def block_maxima(da, block_size=365, side="center") -> xr.DataArray
def block_minima(da, block_size=365, side="center") -> xr.DataArray
def pot_threshold(da, quantile=0.98) -> float
def pot_exceedances(da, quantile=0.98, decluster_freq=None) -> xr.DataArray
def pp_counts(da, threshold, block_size=365) -> xr.DataArray
def pp_stats(da, threshold, block_size=365, statistic="mean") -> xr.DataArray

spectral — Spectral Analysis

def psd(ds, variable, dims, **kwargs) -> xr.Dataset
def psd_isotropic(ds, variable, dims, **kwargs) -> xr.Dataset
def cross_spectrum(ds, var_a, var_b, dims) -> xr.Dataset
def coherence(ds, var_a, var_b, dims) -> xr.Dataset

metrics — Evaluation Metrics

# Pixel metrics
def rmse(ds_pred, ds_ref, variable, dims) -> xr.DataArray
def nrmse(ds_pred, ds_ref, variable, dims) -> xr.DataArray
def mae(ds_pred, ds_ref, variable, dims) -> xr.DataArray
def bias(ds_pred, ds_ref, variable, dims) -> xr.DataArray
def correlation(ds_pred, ds_ref, variable, dims) -> xr.DataArray
def mse(ds_pred, ds_ref, variable, dims) -> xr.DataArray
def r2_score(ds_pred, ds_ref, variable, dims) -> xr.DataArray

# Spectral metrics
def psd_score(ds_pred, ds_ref, variable, dims) -> xr.Dataset
def resolved_scale(ds_pred, ds_ref, variable, dims, threshold=0.5) -> float

# Multiscale metrics
def spatial_rmse_at_scale(ds_pred, ds_ref, variable, scales) -> xr.Dataset

Wraps xskillscore where available, with pure numpy/scipy fallbacks.


kinematics — Physical Quantities

Domain-specific physical transformations. Starting with remote sensing, methane, and oceanography. Uses metpy where available, with pure numpy/scipy fallbacks.

# Oceanography
def coriolis_parameter(lat) -> xr.DataArray
def streamfunction(ds, variable="ssh", f0=None, g=None) -> xr.Dataset
def geostrophic_velocities(ds, variable="ssh") -> xr.Dataset
def relative_vorticity(ds, u_var="u", v_var="v") -> xr.Dataset
def kinetic_energy(ds, u_var="u", v_var="v") -> xr.Dataset
def enstrophy(ds, vorticity_var="vorticity") -> xr.Dataset
def strain_rate(ds, u_var="u", v_var="v") -> xr.Dataset
def okubo_weiss(ds, u_var="u", v_var="v") -> xr.Dataset
def mixed_layer_depth(ds, temp_var="temperature", depth_var="depth", threshold=0.2) -> xr.Dataset
def brunt_vaisala(ds, temp_var="temperature", salt_var="salinity", depth_var="depth") -> xr.Dataset

# Remote sensing / radiometry
def radiance_to_reflectance(ds, solar_zenith_var, solar_irradiance) -> xr.Dataset
def brightness_temperature(ds, variable, wavelength) -> xr.Dataset
def normalized_difference(ds, var_a, var_b, name="ndvi") -> xr.Dataset

# Methane-specific
def column_averaging_kernel(ds, pressure_var, kernel_var) -> xr.Dataset
def dry_air_column(ds, surface_pressure_var, humidity_var) -> xr.Dataset
def mixing_ratio_to_column(ds, variable, pressure_levels) -> xr.Dataset

# Atmospheric
def potential_temperature(ds, temp_var, pressure_var) -> xr.Dataset
def wind_speed(ds, u_var="u10", v_var="v10") -> xr.Dataset
def wind_direction(ds, u_var="u10", v_var="v10") -> xr.Dataset

sklearn — scikit-learn Interop

def to_2d(da, sample_dim) -> tuple[np.ndarray, dict]
def from_2d(arr, meta, new_feature_dim="component") -> xr.DataArray

When xarray_sklearn is installed, the to_2d / from_2d helpers are still available for low-level use, but the recommended path is XarrayEstimator (via SklearnOp at Layer 1) which handles NaN policies, shape-changing transforms, and metadata preservation automatically.

For large-domain workflows, combine with xrpatcher to apply these primitives per spatial patch rather than over the full grid — see ../examples/integration.md.