marEx.detect.compute_normalised_anomaly

marEx.detect.compute_normalised_anomaly(da, method_anomaly='shifting_baseline', dimensions=None, coordinates=None, window_year_baseline=15, smooth_days_baseline=21, std_normalise=False, detrend_orders=None, force_zero_mean=True, reference_period=None, use_temp_checkpoints=False, verbose=None, quiet=None)[source]

Generate normalised anomalies using specified methodology.

Parameters:
  • da (xarray.DataArray) – Input data with dimensions matching the ‘dimensions’ parameter

  • method_anomaly (str, default='shifting_baseline') –

    Anomaly computation method. Options: - ‘detrend_harmonic’: Detrending with harmonics and polynomials (efficient, biased) - ‘shifting_baseline’: Rolling climatology (accurate, shortens time series) - ‘fixed_baseline’: Daily climatology using full time series (keeps long-term trends in the anomaly) - ‘detrend_fixed_baseline’: Polynomial detrending + fixed climatology (does not shorten time series,

    keeps trends in seasonal timing in the anomaly)

  • dimensions (dict, optional) – Mapping of conceptual dimensions to actual dimension names in the data

  • coordinates (dict, optional) – Mapping of conceptual coordinates to actual coordinate names in the data

  • window_year_baseline (int, default=15) – Number of years for rolling climatology (shifting_baseline only)

  • smooth_days_baseline (int, default=21) – Days for smoothing rolling climatology (shifting_baseline only)

  • std_normalise (bool, default=False) – Whether to normalise by 30-day rolling standard deviation (detrend_harmonic only)

  • detrend_orders (list, default=[1]) – Polynomial orders for trend removal (detrend_harmonic and detrend_fixed_baseline only)

  • force_zero_mean (bool, default=True) – Explicitly enforce zero mean in final anomalies (detrend_harmonic and detrend_fixed_baseline only)

  • reference_period (tuple of (int, int), optional) – Year range (start_year, end_year) inclusive for computing the daily climatology (fixed_baseline and detrend_fixed_baseline only). If None (default), uses all available years. Anomalies are computed for the full time series regardless.

  • use_temp_checkpoints (bool)

  • verbose (bool | None)

  • quiet (bool | None)

Returns:

Dataset containing anomalies, mask, and metadata

Return type:

xarray.Dataset

Examples

Basic detrended baseline anomaly computation:

>>> import xarray as xr
>>> import marEx
>>>
>>> # Load chunked SST data
>>> sst = xr.open_dataset('sst_data.nc', chunks={}).sst.chunk({'time': 30})
>>>
>>> # Compute anomalies using shifting baseline (default)
>>> result = marEx.compute_normalised_anomaly(sst)
>>> print(result.data_vars)
Data variables:
    dat_anomaly  (time, lat, lon) float32 dask.array<chunksize=(30, 180, 360)>
    mask         (lat, lon) bool dask.array<chunksize=(180, 360)>
>>> # Check that anomalies have approximately zero mean
>>> print(f"Mean anomaly: {result.dat_anomaly.mean().compute():.6f}")
Mean anomaly: 0.000023

Previous configuration (marEx v2.0 default) of detrended baseline with higher-order polynomials and standardisation. Note: marEx v3.0+ uses shifting_baseline as the default method:

>>> result_advanced = marEx.compute_normalised_anomaly(
...     sst,
...     method_anomaly="detrend_harmonic",
...     detrend_orders=[1, 2, 3],  # Linear, quadratic, cubic trends
...     std_normalise=True,        # Add standardised anomalies
...     force_zero_mean=True
... )
>>> print(result_advanced.data_vars)
Data variables:
    dat_anomaly  (time, lat, lon) float32 dask.array<chunksize=(30, 180, 360)>
    mask         (lat, lon) bool dask.array<chunksize=(180, 360)>
    dat_stn      (time, lat, lon) float32 dask.array<chunksize=(30, 180, 360)>
    STD          (dayofyear, lat, lon) float32 dask.array<chunksize=(366, 180, 360)>
>>> # Standardised anomalies have unit variance
>>> print(f"STD of standardised anomalies: {result_advanced.dat_stn.std().compute():.3f}")

Accurate shifting baseline method for climate-aware anomalies:

>>> result_shifting = marEx.compute_normalised_anomaly(
...     sst,
...     method_anomaly="shifting_baseline",
...     window_year_baseline=10,   # Use 10-year rolling climatology
...     smooth_days_baseline=31    # 31-day smoothing window
... )
>>> # Anomalies computed relative to recent past climatology

Processing unstructured data:

>>> # ICON ocean model with ncells dimension
>>> icon_data = xr.open_dataset('icon_sst.nc', chunks={}).to.chunk({'time': 25})
>>> result_unstructured = marEx.compute_normalised_anomaly(
...     icon_data,
...     dimensions={"time": "time", "x": "ncells"}
...     coordinates={"time": "time", "x": "lon", "y": "lat"},
... )
>>> print(result_unstructured.dims)
Frozen({'time': 1461, 'ncells': 83886})

Comparison of methods - detrended vs shifting baseline:

>>> # Detrended baseline - faster, slight bias
>>> detrended = marEx.compute_normalised_anomaly(
...     sst, method_anomaly="detrend_harmonic"
... )
>>>
>>> # Shifting baseline - slower, more accurate
>>> shifting = marEx.compute_normalised_anomaly(
...     sst, method_anomaly="shifting_baseline",
...     window_year_baseline=15
... )
>>>
>>> # Compare anomaly magnitudes
>>> print(f"Detrended RMS: {detrended.dat_anomaly.std().compute():.3f}")
>>> print(f"Shifting RMS: {shifting.dat_anomaly.std().compute():.3f}")

Fixed baseline climatology:

>>> # Use full time series for daily climatology
>>> result_fixed = marEx.compute_normalised_anomaly(
...     sst,
...     method_anomaly="fixed_baseline"
... )
>>> # Climatology computed from all available years

Fixed baseline with a restricted reference period:

>>> # Compute climatology from 1990-2020 only, but output anomalies for all years
>>> result_ref = marEx.compute_normalised_anomaly(
...     sst,
...     method_anomaly="fixed_baseline",
...     reference_period=(1990, 2020)
... )

Fixed detrended baseline:

>>> # Remove long-term trends then compute fixed climatology
>>> result_fixed_detrended = marEx.compute_normalised_anomaly(
...     sst,
...     method_anomaly="detrend_fixed_baseline",
...     detrend_orders=[1],  # Remove linear trend
...     force_zero_mean=True
... )
>>> # Combines trend removal with fixed climatology