Metadata-Version: 2.4
Name: enso-noaa-predictions
Version: 0.1.0
Summary: Python toolkit for NOAA CPC and IRI ENSO forecasts, RONI observations, phase classification, and parametric climate analytics.
Author: Massimo Poretti
License: MIT
Project-URL: Homepage, https://github.com/Mastro1/enso_noaa_predictions
Project-URL: Documentation, https://mastro1.github.io/enso_noaa_predictions
Project-URL: Repository, https://github.com/Mastro1/enso_noaa_predictions.git
Project-URL: Bug Tracker, https://github.com/Mastro1/enso_noaa_predictions/issues
Project-URL: Changelog, https://github.com/Mastro1/enso_noaa_predictions/blob/main/CHANGELOG.md
Keywords: enso,noaa,climate,roni,el-nino,la-nina,meteorology,weather,forecasting,iri,parametric-insurance
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
Classifier: Topic :: Office/Business :: Financial :: Investment
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pandas>=1.5.0
Requires-Dist: numpy>=1.20.0
Requires-Dist: requests>=2.28.0
Requires-Dist: beautifulsoup4>=4.11.0
Requires-Dist: matplotlib>=3.5.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Requires-Dist: mypy>=1.0.0; extra == "dev"
Requires-Dist: responses>=0.23.0; extra == "dev"
Provides-Extra: docs
Requires-Dist: mkdocs>=1.5.0; extra == "docs"
Requires-Dist: mkdocs-material>=9.4.0; extra == "docs"
Requires-Dist: mkdocstrings[python]>=0.24.0; extra == "docs"
Requires-Dist: pymdown-extensions>=10.0; extra == "docs"
Provides-Extra: all
Requires-Dist: enso-noaa-predictions[dev,docs]; extra == "all"
Dynamic: license-file

# `enso_noaa_predictions`

<div align="center">

[![CI](https://github.com/Mastro1/enso_noaa_predictions/actions/workflows/ci.yml/badge.svg)](https://github.com/Mastro1/enso_noaa_predictions/actions/workflows/ci.yml)
[![Docs](https://github.com/Mastro1/enso_noaa_predictions/actions/workflows/docs.yml/badge.svg)](https://mastro1.github.io/enso_noaa_predictions)
[![Python Version](https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue.svg)](https://pypi.org/project/enso-noaa-predictions/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Code Style: Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)

**A Python toolkit for NOAA CPC and IRI El Niño–Southern Oscillation (ENSO) forecasts, Relative Oceanic Niño Index (RONI) observations, phase classification, and parametric climate risk analytics.**

[Key Features](#key-features) •
[Installation](#installation) •
[Quickstart](#quickstart) •
[User Guide](#user-guide) •
[Documentation](https://mastro1.github.io/enso_noaa_predictions) •
[License](#license)

</div>

---

## Overview

The **El Niño–Southern Oscillation (ENSO)** is the most prominent driver of year-to-year global climate variability. In 2024–2025, NOAA Climate Prediction Center (CPC) adopted the **Relative Oceanic Niño Index (RONI)** to isolate equatorial Pacific sea surface temperature anomalies from global tropical warming trends.

`enso_noaa_predictions` provides an intuitive, typed Python interface to:
1. **Live NOAA CPC RONI Forecasts**: Fetch official 9-season RONI percentile fan charts (5% to 95%), categorical ENSO probabilities, and 9-category strength distributions.
2. **Historical Archives & Observations**: Access retrospective forecast archives and continuous historical RONI observations since 1950.
3. **IRI Multi-Model Plume**: Retrieve dynamical and statistical model plumes with automatic local caching.
4. **Parametric & Insurance Pricing**: Classify historical crop seasons across calendar boundaries and weight burning cost or loss ratios by seasonal forecast probabilities.
5. **Publication-Ready Visualizations**: Generate Matplotlib charts replicating official climate agency visual standards.

---

## Installation

### From PyPI (once published)
```bash
pip install enso-noaa-predictions
```

### From GitHub (direct)
```bash
pip install git+https://github.com/Mastro1/enso_noaa_predictions.git
```

### For Local Development
```bash
git clone https://github.com/Mastro1/enso_noaa_predictions.git
cd enso_noaa_predictions
pip install -e ".[dev,docs]"
```

---

## Quickstart

### 1. Object-Oriented Client (Recommended)

```python
import matplotlib.pyplot as plt
from enso_noaa import ENSOClient

# Initialize client (NOAA CPC by default)
client = ENSOClient(source="noaa")

# 1. Fetch live NOAA CPC RONI Outlook
outlook = client.get_forecast(product="outlook")
print(f"Issuance: {outlook.init_month} {outlook.init_year}")
print(outlook.to_dataframe())

# 2. Render NOAA Fan Chart
fig_ax = outlook.plot()
plt.show()

# 3. Fetch ENSO Probabilities (El Niño / Neutral / La Niña)
probs = client.get_forecast(product="probabilities")
probs.plot()
plt.show()

# 4. Fetch 9-Category ENSO Strength Probabilities
strengths = client.get_forecast(product="strengths")
strengths.plot()
plt.show()
```

### 2. Functional Facade (100% Backward Compatible)

```python
from enso_noaa import get_enso_predictions, plot_enso_predictions

# Fetch 50th percentile (median) prediction
df, init_month, init_year = get_enso_predictions(source="noaa", product="outlook", model="50%")

# Plot prediction
plot_enso_predictions(df, init_month, init_year)
```

---

## Key Features

### 🌊 NOAA CPC RONI Outlook Fan Charts
Extracts the official 9-season probabilistic forecast table including 5%, 15%, 25%, 50% (median), 75%, 85%, and 95% percentiles, and generates shaded uncertainty fan charts.

```python
from enso_noaa import ENSOClient

client = ENSOClient(source="noaa")
outlook = client.get_forecast(product="outlook")
p50_row = outlook.get_percentile("50%")
outlook.plot()
```

<div align="center">
  <img src="docs/assets/roni_outlook.png" alt="NOAA CPC RONI Outlook Fan Chart" width="85%" />
</div>

---

### 📊 Seasonal ENSO & Strength Probabilities
Access seasonal likelihoods for La Niña, Neutral, and El Niño, alongside the 9-category granular strength breakdown (e.g. *Very Strong El Niño*, *Moderate La Niña*).

```python
probs = client.get_forecast(product="probabilities")
probs.plot()

strengths = client.get_forecast(product="strengths")
strengths.plot()
```

<div align="center">
  <img src="docs/assets/enso_probabilities.png" alt="NOAA CPC ENSO Probabilities" width="48%" />
  <img src="docs/assets/enso_strengths.png" alt="NOAA CPC ENSO Strengths" width="48%" />
</div>

---

### 🗓️ Historical RONI Observations & Archives
Retrieve continuous historical 3-month running mean RONI observations from 1950 to the present, or query retrospective forecast archives:

```python
# Historical RONI table
historical_df = client.get_historical_roni()

# Retrospective forecast from a specific issuance (e.g. January 2024)
archive = client.get_forecast(product="outlook", year=2024, month=1)
```

---

### 🌾 Agricultural & Parametric Risk Weighting
Classify historical years by ENSO phase over custom crop windows—even those straddling the December–January boundary (e.g., Austral summer `NDJ`, `DJF`, `JFM`)—and compute forecast-weighted loss metrics:

```python
from enso_noaa import classify_enso_phases, enso_weighted_value, phase_conditional_metric, plot_enso_phase_metric

# 1. Classify historical years based on crop season RONI
phase_history = classify_enso_phases(
    historical_df, 
    crop_seasons=["NDJ", "DJF", "JFM"],
    cool_thresh=-0.5, 
    warm_thresh=0.5
)

# 2. Compute phase-conditional loss ratio summary
merged, cond_summary, unconditional = phase_conditional_metric(
    crop_df, 
    phase_history, 
    value_col="loss_ratio", 
    weight_col="premium"
)

# 3. Blend conditional values using upcoming seasonal forecast probabilities
forecast_probs = {"El Nino": 0.15, "Neutral": 0.65, "La Nina": 0.20}
weighted_lr = enso_weighted_value(cond_summary, forecast_probs)
print(f"ENSO-Weighted Expected Loss Ratio: {weighted_lr:.2%}")

# 4. Plot two-panel diagnostic
plot_enso_phase_metric(
    merged, cond_summary,
    value_col="loss_ratio",
    weighted_value=weighted_lr,
    unconditional_value=unconditional
)
```

<div align="center">
  <img src="docs/assets/phase_metric.png" alt="ENSO Phase Conditional Diagnostic" width="85%" />
</div>

---

### 📈 IRI Multi-Model Plume (Legacy / Discontinued by Upstream)

> [!WARNING]
> **Live IRI Fetching Discontinued**: The Columbia Climate School IRI website **no longer publicly shares ENSO plume table values**.
> Calling `ENSOClient(source="iri").get_forecast()` will raise a `RuntimeError` unless a local CSV cache is present at `src/enso_noaa/data/enso_predictions.csv`.
> **Please use NOAA CPC (`source="noaa"`, default) for active operational forecasts.**

```python
# IRI client (requires local cache, live fetching is discontinued upstream):
from enso_noaa import ENSOClient

try:
    iri_client = ENSOClient(source="iri")
    iri_forecast = iri_client.get_forecast()
    iri_forecast.plot()
except RuntimeError as exc:
    print(f"IRI discontinued: {exc}")
```

<div align="center">
  <img src="docs/assets/iri_plume.png" alt="IRI Multi-Model Plume" width="85%" />
</div>

---

## Module Architecture

| Module | Description |
| :--- | :--- |
| [`enso_noaa.client`](src/enso_noaa/client.py) | High-level `ENSOClient` orchestrator & functional wrappers (`get_enso_predictions`). |
| [`enso_noaa.noaa`](src/enso_noaa/noaa.py) | `NOAAENSOProvider` for live scraping and parsing of CPC RONI tables & archives. |
| [`enso_noaa.iri`](src/enso_noaa/iri.py) | `IRIENSOProvider` for IRI plume downloads, parsing, and caching. |
| [`enso_noaa.base`](src/enso_noaa/base.py) | Abstract `BaseENSOProvider` and rich `ENSOForecastResult` domain object. |
| [`enso_noaa.analysis`](src/enso_noaa/analysis.py) | Cross-boundary phase classification, probability aggregation, and parametric weighting. |
| [`enso_noaa.plot`](src/enso_noaa/plot.py) | Clean Matplotlib charting routines for outlooks, probabilities, and phase metrics. |
| [`enso_noaa.sources`](src/enso_noaa/sources.py) | URL constants and official data endpoint registry. |

---

## Documentation

Full interactive documentation, API references, and tutorials are available at:
👉 **[https://mastro1.github.io/enso_noaa_predictions](https://mastro1.github.io/enso_noaa_predictions)**

To run documentation locally:
```bash
mkdocs serve
```

---

## Testing

Run the test suite with `pytest` or Python `unittest`:
```bash
# Run fast offline test suite (mock fixtures)
pytest tests/ -v

# Run live network integration tests
pytest tests/ -m network
```

---

## Scientific References

- **NOAA Climate Prediction Center (CPC)**: [ENSO Monitoring & RONI Data](https://www.cpc.ncep.noaa.gov/products/analysis_monitoring/enso/roni/)
- **L'Heureux et al. (2024)**: *The Relative Oceanic Niño Index (RONI)*, Journal of Climate.
- **Columbia Climate School IRI**: [ENSO Multi-Model Plume & Forecasts](https://iri.columbia.edu/our-expertise/climate/forecasts/enso/)

---

## License

This project is licensed under the **MIT License** - see the [LICENSE](LICENSE) file for details.
