Metadata-Version: 2.4
Name: climate_diagnostics
Version: 2.2.0
Summary: One-line spatial climate diagnostics for xarray: means, percentiles, exceedances, anomalies, trends and correlations with significance stippling
Author-email: Pranay Chakraborty <pranay.chakraborty.personal@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/pranay-chakraborty/climate_diagnostics
Project-URL: Documentation, https://pranay-chakraborty.github.io/climate_diagnostics/
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: xarray>=2023.1
Requires-Dist: numpy
Requires-Dist: pandas
Requires-Dist: scipy
Requires-Dist: matplotlib
Requires-Dist: cartopy
Provides-Extra: io
Requires-Dist: netCDF4; extra == "io"
Requires-Dist: dask[array]; extra == "io"
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Provides-Extra: docs
Requires-Dist: sphinx; extra == "docs"
Requires-Dist: furo; extra == "docs"
Requires-Dist: sphinx-copybutton; extra == "docs"
Requires-Dist: sphinx-design; extra == "docs"
Dynamic: license-file

<div align="center">

<img src="docs/source/_static/logo.svg" width="110" alt="climate_diagnostics logo">

# climate_diagnostics

**Publication-ready climate maps in one line.**<br>
Means, extremes, trends, EOFs and more, with significance stippling built in.

[![CI](https://github.com/pranay-chakraborty/climate_diagnostics/actions/workflows/ci.yml/badge.svg)](https://github.com/pranay-chakraborty/climate_diagnostics/actions/workflows/ci.yml)
[![Docs](https://img.shields.io/badge/docs-online-1d62c9)](https://pranay-chakraborty.github.io/climate_diagnostics/)
![Python](https://img.shields.io/badge/python-3.10%2B-3776ab)
![License](https://img.shields.io/badge/license-MIT-green)

[**Documentation**](https://pranay-chakraborty.github.io/climate_diagnostics/) ·
[**Recipes**](https://pranay-chakraborty.github.io/climate_diagnostics/recipes.html)

</div>

## One line, one map

```python
import climate_diagnostics as cd

NCEP = "http://psl.noaa.gov/thredds/dodsC/Datasets/ncep.reanalysis"       # streamed over OPeNDAP
t2m = cd.load(f"{NCEP}.derived/surface_gauss/air.2m.mon.mean.nc")
hgt = cd.load(f"{NCEP}.derived/pressure/hgt.mon.mean.nc")

t2m.climate.mean("air", season="JJA", convert="degC")                        # climatology
t2m.climate.trend("air", season=["DJF", "JJA"], time=slice("1979", "2025"))  # two panels, stippled p<0.05
t2m.climate.anomaly("air", period=(2011, 2020), base=(1981, 2010), fdr=True) # field-significance corrected
hgt.climate.mean("hgt", level=500, season="DJF")                             # 3D data: pick a level
hgt.climate.eof("hgt", n=4, level=500, lat=slice(20, 90), season="DJF")      # leading EOFs
```

Every call draws the map **and returns the result as xarray**. Pass `plot=False` and the same line is a calculator.

## Why

Everyday climate analysis is thirty lines of season masks, area weights, significance tests and Cartopy
boilerplate. Here it is a method call.

|  |  |
|---|---|
| **Stippling by default** | Trends, anomalies, correlations, composites and biases are tested per grid point; `fdr=True` controls the false discovery rate over the map. |
| **Seasons that just work** | `'DJF'`, `'JJAS'`, `1`, `[1, 2, 3]`, or `['DJF', 'JJA']` for one panel per season. Year-wrapping seasons are handled correctly. |
| **3D and any grid** | `level=500` or a layer slice. Regular and curvilinear (2D lat/lon) grids; 0-360 and -180-180 longitudes. |
| **Grunt work included** | Unit conversion (`convert='degC'`), percentile thresholds (`above='p90'`), box indices (Niño 3.4), OPeNDAP and multi-file loading. |

## Cheat sheet

| Call | Maps |
|---|---|
| `mean` `std` `min` `max` `median` | statistic over the season |
| `percentile(q)` | q-th percentile |
| `total` | seasonal accumulation averaged over years |
| `exceedance(above= / below=)` | frequency beyond a number or a percentile like `'p90'` (from a `base=` period) |
| `spell(above= / below=)` | longest consecutive run (dry spell, heatwave length) |
| `anomaly(period, base)` | period difference, Welch t-test stippling |
| `trend(per=10)` | Theil-Sen slope, Mann-Kendall (tie-corrected) stippling |
| `correlation(index, kind='corr'\|'regress')` | correlation or regression on a time series, stippled |
| `composite(index, high, low)` | high-index minus low-index years (El Niño − La Niña), stippled |
| `bias(other)` | model minus reference, paired t-test, RMSE and pattern correlation |
| `wind(u, v)` | shaded speed with vectors |
| `eof(n)` | leading EOF patterns, PCs, explained variance with North et al. errors |
| `index(...)` | area-weighted box index (Niño 3.4, ...) to feed `correlation` / `composite` |

Shared keywords: `variable`, `season`, `level`, `lat`, `lon`, `time`, `convert`, `plot`. Plot options
(`projection`, `extent`, `cmap`, `vmin`/`vmax`/`levels`, `title`, `ax`, `save`, ...) go straight through.
Trends, anomalies and correlations first reduce to one value per season-year, so seasonality never leaks
into the statistics. Everything also works on a `DataArray`: `da.climate.mean()`.

## Install

```bash
pip install git+https://github.com/pranay-chakraborty/climate_diagnostics
pip install "climate_diagnostics[io] @ git+https://github.com/pranay-chakraborty/climate_diagnostics"   # + netCDF4 and dask: OPeNDAP, multi-file
```

Development: `pip install -e ".[io,test,docs]" && pytest`

## License

MIT
