Climate indices for drought monitoring
This project contains Python implementations of various climate index algorithms which provide a geographical and temporal picture of the severity and duration of precipitation and temperature anomalies useful for climate monitoring and research.
The following indices are provided:
This Python implementation of the above climate index algorithms is being developed with the following goals in mind:
This is a developmental/forked version of code that was originally developed by NIDIS/NCEI/NOAA. See drought.gov.
Install the released package from PyPI:
pip install "climate-indices>=2.3"
uv users can run uv pip install "climate-indices>=2.3". The 2.3.0 floor matches the
quickstart,
which documents the latest release and uses the xarray API added in 2.3.0; see
Supported Python Versions for interpreter support.
This project uses trunk-based development. main is the trunk and should always
be releasable.
git switch main && git pull --ff-only origin maingit switch -c feature/uv run ruff check src/ tests/
uv run ruff format --check src/ tests/
uv run mypy src/ tests/test_type_checking.py
uv run pytestmain.Use feature/, fix/, docs/, chore/, or
hotfix/ branch names. Release branches are avoided; use maintenance
branches only for approved older-version support.
Releases are tag-based. The Git tag, package version, GitHub Release, and PyPI version must match.
v1.2.31.2.3v1.2.31.2.3pyproject.toml, CHANGELOG.md,
and release notes/docs; the maintainer merges it after review and passing
CI.main is green.main.Tag creation and publishing require maintainer approval. See
docs/release-process.md for the full checklist.
Read-only preflight:
git status --short
git branch --show-current
git log --oneline --decorate -5
uv run pytest tests/test_release_integrity.py
Safe PR branch setup:
git switch main
git pull --ff-only origin main
git switch -c chore/issue-667-release-docs
Approval-required release tag commands:
git switch main
git pull --ff-only origin main
git tag -a vX.Y.Z -m "Release vX.Y.Z"
git push origin vX.Y.Z
| Python Version | Status | Notes |
|---|---|---|
| 3.10 | Supported | Minimum supported version |
| 3.11 | Supported | |
| 3.12 | Supported | |
| 3.13 | Supported | |
| 3.14 | Supported | Latest supported version |
All versions are tested on Linux (ubuntu-latest). Python 3.10 and 3.14 are additionally tested on macOS. Both latest and minimum declared dependency versions are tested in CI.
This project provides 12 months notice before dropping support for a Python version. When a version approaches end-of-life, removal will be announced via the CHANGELOG and a GitHub issue, and implemented no sooner than 12 months after announcement with a version bump.
Python 3.9 support was dropped in v2.2.0 (August 2025) due to scipy>=1.15.3 requiring 3.10+.
| API Surface | Status | Guarantee |
|---|---|---|
NumPy array functions (indices.spi, indices.spei, indices.pet) |
Stable | No breaking changes in minor versions |
xarray DataArray functions (spi(), spei(), pet_thornthwaite(), pet_hargreaves()) |
Beta | No breaking changes in patch versions |
Stable API: The NumPy-based computation functions follow strict semantic versioning.
Beta API: The xarray adapter layer provides automatic parameter inference, coordinate
preservation, CF metadata, and Dask support. While beta, computation results are identical
to the stable NumPy API — only the interface surface (parameter names, metadata attributes,
coordinate handling) may evolve. Beta features are tagged with BetaFeatureWarning and
marked in docstrings.
See docs/xarray_compatibility.md for the 3.0.0 compatibility matrix, including
Dask chunking constraints, metadata behavior, and the current Palmer xarray
workflow.
The 3.0.0 validation status is tracked in VALIDATION.md. EDDI is validated
against committed paired NOAA PSL monthly reference ET/EDDI fixtures for 1-, 3-,
and 6-month Timescales (1979–2023); the maximum observed error is 2.43e-6.
Palmer tests cover the committed regression fixtures for PDSI, PHDI, PMDI, and
Z-Index, plus Wells-lineage reference fixtures for scPDSI's four
self-calibrating outputs and fitted duration factors. These fixtures are treated
as regression coverage, not independent authoritative scientific validation.
Breaking Change: Exception-Based Error Handling
Version 2.2.0 introduces a significant architectural improvement in error handling. The library now uses exception-based error handling instead of returning None tuples for error conditions.
Before (v2.1.x and earlier):
# Old behavior - functions returned None tuples on failure
result = some_internal_function(data)
if result == (None, None, None, None):
# Handle error case
pass
After (v2.2.0+):
# New behavior - functions raise specific exceptions
try:
result = some_internal_function(data)
except climate_indices.compute.InsufficientDataError as e:
# Handle insufficient data case
print(f"Not enough data: {e.non_zero_count} values found, {e.required_count} required")
except climate_indices.compute.PearsonFittingError as e:
# Handle fitting failure case
print(f"Fitting failed: {e}")
DistributionFittingError (base class)InsufficientDataError - raised when there are too few non-zero values for statistical fittingPearsonFittingError - raised when L-moments calculation fails for Pearson Type III distributionNone return values from internal functions, update to use try/catch blocksVersion 2.2.0 also addresses floating point comparison issues (python:S1244) throughout the codebase:
Floating Point Comparisons:
# ❌ OLD: Direct equality checks (unreliable)
if values == 0.0:
handle_zero_case()
# ✅ NEW: Safe comparison using numpy.isclose()
if np.isclose(values, 0.0, atol=1e-8):
handle_zero_case()
Benefits:
docs/floating_point_best_practices.md for comprehensive guidelinesYou can cite climate_indices in your projects and research papers via the BibTeX
entry below.
@misc {climate_indices,
author = "James Adams",
title = "climate_indices, an open source Python library providing reference implementations of commonly used climate indices",
url = "https://github.com/monocongo/climate_indices",
month = "may",
year = "2017--"
}
No open issues yet, or sync has not completed.