Generic indices recipes#

The generic index API lets you compute any climate indicator by combining a reducer function (e.g. count_occurrences, sum, average) with a threshold (e.g. "> 25 degC").

See also

Thresholds and Operators Reference — full table of operators and threshold types.

Quick-start reference#

Threshold mini-cheat-sheet#

Threshold string

Meaning

"> 25 degC"

Fixed scalar: strictly above 25 °C

">= 1 mm/day"

Fixed scalar: at least 1 mm/day

"> 90 doy_per"

Day-of-year percentile (temperature indices): 90th per-calendar-day percentile

"> 75 period_per"

Period percentile (precip indices): 75th percentile of the whole period

">= 18 degC AND <= 30 degC"

Bounded (AND): value must satisfy both conditions simultaneously

"< 0 degC OR > 35 degC"

Bounded (OR): value must satisfy at least one condition

See Thresholds and Operators Reference for threshold_min_value, per-grid-cell thresholds, and all available operators.

Imports and in-memory sample data#

All examples below that use in-memory data share the same setup:

import numpy as np
import pandas as pd
import xarray as xr
import icclim
from icclim import build_threshold

# 5 years of daily data, single grid cell
time = pd.date_range("2000-01-01", periods=365 * 5, freq="D")

# Temperature: 30 °C everywhere (300+ K)
tas = xr.DataArray(
    np.full(len(time), 303.15),   # 30 °C in K
    coords={"time": time},
    dims=["time"],
    attrs={"units": "K"},
)

# Precipitation: alternating 5 mm/day and 0.5 mm/day
pr_vals = np.where(np.arange(len(time)) % 2 == 0, 5e-5, 5e-6)
pr = xr.DataArray(
    pr_vals,
    coords={"time": time},
    dims=["time"],
    attrs={"units": "kg m-2 s-1"},
)

For file-based examples, set:

data = "netcdf_files/gridded.1991-2010.nc"

Count occurrences#

Count days per year when temperature exceeds a fixed threshold:

# In-memory example: count days when tas > 25 °C
result = icclim.count_occurrences(
    in_files=tas,
    var_name="tas",
    threshold="> 25 degC",
).compute()
print(result.count_occurrences)

Count days when both temperature AND precipitation exceed their respective thresholds (multivariable index):

okay_temp = build_threshold(">= 20 degC") & build_threshold("<= 30 degC")
some_rain = build_threshold("> 3 mm/day")

result = icclim.count_occurrences(
    in_files={
        "tas": {"study": tas, "thresholds": okay_temp},
        "pr":  {"study": pr,  "thresholds": some_rain},
    }
).compute()

Using a day-of-year percentile threshold (equivalent to TX99p):

tx99p = icclim.count_occurrences(
    in_files=tas, var_name="tas", threshold=">= 99 doy_per"
).count_occurrences.compute()

Using out_unit="%" to get the result as a fraction of days:

result_pct = icclim.count_occurrences(
    in_files=tas, var_name="tas",
    threshold="> 25 degC", out_unit="%",
).count_occurrences.compute()

File-based example:

result_file = icclim.count_occurrences(
    in_files=data, var_name="tasmax", threshold=">= 99 doy_per"
).count_occurrences.compute()

Sum#

Sum of precipitation values above 4 mm/day per year:

# In-memory example
rain_sum = icclim.sum(
    in_files=pr, var_name="pr", threshold="> 4 mm/day"
).sum.compute()

File-based:

rain_sum_file = icclim.sum(
    in_files=data, var_name="precip", threshold="> 4 mm/day"
).sum.compute()

Standard Deviation#

Interannual variability of daily temperature:

# In-memory example
tas_std = icclim.std(in_files=tas, var_name="tas").std.compute()

File-based:

tas_std_file = icclim.std(in_files=data, var_name="tas").std.compute()

Average#

Annual mean temperature:

# In-memory example
tas_avg = icclim.average(in_files=tas, var_name="tas").average.compute()

Mean of values above the 87th period percentile:

tas_hot_avg = icclim.average(
    in_files=tas, var_name="tas", threshold="> 87 period_per"
).average.compute()

Monthly means (slice_mode="month"):

tas_monthly = icclim.average(
    in_files=tas, var_name="tas", slice_mode="month"
).average.compute()

Maximum Consecutive Occurrences#

Longest dry spell per year (consecutive days when pr < 1 mm/day), equivalent to ECA&D’s CDD:

# In-memory example
dry_spell = icclim.max_consecutive_occurrence(
    in_files=pr, var_name="pr", threshold="< 1 mm/day"
).max_consecutive_occurrence.compute()

Use run_index="last" to stamp the spell at its end date (Climdex convention):

dry_spell_last = icclim.max_consecutive_occurrence(
    in_files=pr, var_name="pr",
    threshold="< 1 mm/day", run_index="last"
).max_consecutive_occurrence.compute()

File-based:

CDD = icclim.max_consecutive_occurrence(
    in_files=data, var_name="precip", threshold="< 1.3 mm/day"
).max_consecutive_occurrence.compute()

Sum of Spell Lengths#

Total duration of all heat spells per year where tasmax > 28 °C for at least 6 consecutive days (equivalent parameter to ECA&D’s WSDI):

# In-memory example: spell ≥ 3 consecutive days above 25 °C
spell_total = icclim.sum_of_spell_lengths(
    in_files=tas,
    var_name="tas",
    threshold="> 25 degC",
    min_spell_length=3,
).sum_of_spell_lengths.compute()

Combine a percentile threshold via a bounded string:

custom_wsdi = icclim.sum_of_spell_lengths(
    in_files=data, var_name="tasmax", threshold="> 90 doy_per"
).sum_of_spell_lengths.compute()

run_index is also supported here ("first" or "last").

Excess#

Cumulative degree-days above a threshold (sum of value - threshold for each exceedance day). Equivalent to ECA&D’s GD4 when threshold is 4 °C.

# In-memory example: degree-days above 25 °C
gd25 = icclim.excess(
    in_files=tas, var_name="tas",
    threshold="25 degC",
).excess.compute()

With a day-of-year percentile reference period:

jja_excess = icclim.excess(
    in_files=data,
    var_name="tmin",
    threshold=build_threshold(
        "22 doy_per",
        base_period_time_range=["1991-01-01", "1995-12-31"],
    ),
    slice_mode="jja",
).excess.compute()

Deficit#

Cumulative degree-days below a threshold (sum of threshold - value). Equivalent to ECA&D’s HD17 when threshold is 17 °C.

# In-memory example: heating degree-days relative to 17 °C
hd17 = icclim.deficit(
    in_files=tas, var_name="tas",
    threshold="17 degC",
).deficit.compute()

File-based with the icclim.index entry point:

result = icclim.index(
    in_files=data,
    var_name=["tmin"],
    index_name="deficit",
    threshold=build_threshold("17 degC"),
).compute()

Fraction of Total#

Fraction of total precipitation contributed by days above the 75th period percentile (wet days only), equivalent to ECA&D’s R75pTOT:

# In-memory example
r75ptot = icclim.fraction_of_total(
    in_files=pr,
    var_name="pr",
    threshold=build_threshold("> 75 period_per", threshold_min_value="1 mm/day"),
).fraction_of_total.compute()

# Return as mm instead of % using out_unit
r75ptot_mm = icclim.fraction_of_total(
    in_files=pr,
    var_name="pr",
    threshold=build_threshold("> 75 period_per", threshold_min_value="1 mm/day"),
    out_unit="mm",
).fraction_of_total.compute()

File-based:

result_frac = icclim.fraction_of_total(
    in_files=data,
    var_name=["precip"],
    threshold=build_threshold("> 75 period_per", threshold_min_value="1 mm/day"),
).fraction_of_total.compute()

Maximum#

Annual maximum temperature:

# In-memory example
txx = icclim.maximum(in_files=tas, var_name="tas").maximum.compute()

Monthly maximum with event date:

txx_monthly = icclim.maximum(
    in_files=tas, var_name="tas",
    slice_mode="month", date_event=True,
).compute()

Minimum#

Annual minimum temperature:

# In-memory example
tnn = icclim.minimum(in_files=tas, var_name="tas").minimum.compute()

Monthly minimum:

tnn_monthly = icclim.minimum(
    in_files=data, var_name="tas", slice_mode="month"
).minimum.compute()

Max of Rolling Sum#

Maximum 5-day accumulated precipitation per year (equivalent to ECA&D’s RX5day):

# In-memory example (5-day rolling window)
rx5day = icclim.max_of_rolling_sum(
    in_files=pr,
    var_name="pr",
    rolling_window_width=5,
).max_of_rolling_sum.compute()

With a wet-day percentile filter:

max_rolling = icclim.max_of_rolling_sum(
    in_files=data,
    var_name="precip",
    threshold=build_threshold(">= 50 period_per", threshold_min_value="1 mm/day"),
).max_of_rolling_sum.compute()

Min of Rolling Sum#

Minimum 5-day accumulated precipitation per year:

# In-memory example
min_rolling = icclim.min_of_rolling_sum(
    in_files=pr, var_name="pr", rolling_window_width=5,
).min_of_rolling_sum.compute()

Max of Rolling Average#

Maximum 5-day rolling mean temperature:

# In-memory example
max_rolling_avg = icclim.max_of_rolling_average(
    in_files=tas, var_name="tas", rolling_window_width=5,
).max_of_rolling_average.compute()

Min of Rolling Average#

Minimum 5-day rolling mean temperature:

# In-memory example
min_rolling_avg = icclim.min_of_rolling_average(
    in_files=tas, var_name="tas", rolling_window_width=5,
).min_of_rolling_average.compute()

Mean of difference#

Mean daily difference between two variables (e.g. tasmax − tasmin), equivalent to ECA&D’s DTR:

# In-memory example: mean of (tasmax - tasmin)
tasmin = tas - 10   # artificial 10 °C spread
tasmin.attrs["units"] = "K"

dtr = icclim.mean_of_difference(
    in_files=xr.Dataset({"tasmax": tas, "tasmin": tasmin}),
    var_name=["tasmax", "tasmin"],
).mean_of_difference.compute()

File-based via icclim.index:

dtr_file = icclim.index(
    in_files=data,
    index_name="mean_of_difference",
    var_name=["tmax", "tmin"],
).mean_of_difference.compute()

Difference of extremes#

Annual maximum of tasmax minus annual minimum of tasmin, equivalent to ECA&D’s ETR:

# In-memory example
tasmin = tas - 10
tasmin.attrs["units"] = "K"

etr = icclim.difference_of_extremes(
    in_files=xr.Dataset({"tasmax": tas, "tasmin": tasmin}),
    var_name=["tasmax", "tasmin"],
).difference_of_extremes.compute()

File-based:

etr_file = icclim.index(
    in_files=data,
    index_name="difference_of_extremes",
    var_name=["tmax", "tmin"],
).difference_of_extremes.compute()

Difference of means#

Anomaly: mean of the study period minus mean of the reference period:

# In-memory example: anomaly relative to first year
anomaly = icclim.difference_of_means(
    in_files=tas,
    var_name="tas",
    base_period_time_range=["2000-01-01", "2000-12-31"],
).difference_of_means.compute()

File-based:

anomaly_file = icclim.difference_of_means(
    in_files=data,
    var_name=["tas"],
    base_period_time_range=["1991-01-01", "1995-12-31"],
).difference_of_means.compute()

Mean Of Absolute One Time Step Difference#

Mean day-to-day variability in the diurnal temperature range, equivalent to ECA&D’s vDTR. The formula is: mean(|(tasmax[t+1] - tasmin[t+1]) - (tasmax[t] - tasmin[t])|)

# In-memory example
tasmin = tas - 10 + xr.DataArray(
    np.random.default_rng(42).normal(0, 1, len(time)),
    coords={"time": time}, dims=["time"], attrs={"units": "K"},
)
vdtr = icclim.mean_of_absolute_one_time_step_difference(
    in_files=xr.Dataset({"tasmax": tas, "tasmin": tasmin}),
    var_name=["tasmax", "tasmin"],
).mean_of_absolute_one_time_step_difference.compute()

File-based:

vdtr_file = icclim.mean_of_absolute_one_time_step_difference(
    in_files=data,
    var_name=["tmax", "tmin"],
).mean_of_absolute_one_time_step_difference.compute()