Metadata-Version: 2.4
Name: whittaker
Version: 0.2.0
Summary: A next-generation Generalized Additive Model (GAM) library for Python.
Author-email: Richard Iannone <riannone@me.com>
Maintainer-email: Richard Iannone <riannone@me.com>
License: MIT License
        
        Copyright (c) 2026 Richard Iannone
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://github.com/rich-iannone/whittaker
Project-URL: Repository, https://github.com/rich-iannone/whittaker
Project-URL: Documentation, https://rich-iannone.github.io/whittaker/
Project-URL: Bug Tracker, https://github.com/rich-iannone/whittaker/issues
Keywords: generalized additive models,GAM,splines,smoothing,penalized regression,REML,P-splines,thin plate splines,distributional regression,GAMLSS,statistics,machine learning
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
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: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: narwhals>=2.0
Requires-Dist: numpy>=1.24
Requires-Dist: scipy>=1.11
Requires-Dist: typing_extensions>=4.0
Provides-Extra: pd
Requires-Dist: pandas>=2.0; extra == "pd"
Provides-Extra: pl
Requires-Dist: polars>=1.0; extra == "pl"
Provides-Extra: pa
Requires-Dist: pyarrow>=15; extra == "pa"
Provides-Extra: formula
Requires-Dist: formulaic>=1.0; extra == "formula"
Provides-Extra: altair
Requires-Dist: altair>=6; extra == "altair"
Requires-Dist: vl-convert-python>=1.9; extra == "altair"
Provides-Extra: tables
Requires-Dist: great-tables>=0.22; extra == "tables"
Provides-Extra: fast
Requires-Dist: numba>=0.59; extra == "fast"
Provides-Extra: all
Requires-Dist: whittaker[altair,formula,pa,pd,pl,tables]; extra == "all"
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Requires-Dist: pytest-snapshot; extra == "dev"
Requires-Dist: pytest-randomly; extra == "dev"
Requires-Dist: hypothesis>=6.100; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: pyright; extra == "dev"
Requires-Dist: build; extra == "dev"
Requires-Dist: pandas>=2.0; extra == "dev"
Requires-Dist: polars>=1.0; extra == "dev"
Requires-Dist: pyarrow>=15; extra == "dev"
Requires-Dist: formulaic>=1.0; extra == "dev"
Requires-Dist: altair>=6; extra == "dev"
Requires-Dist: vl-convert-python>=1.9; extra == "dev"
Provides-Extra: docs
Requires-Dist: jupyter; extra == "docs"
Requires-Dist: nbclient; extra == "docs"
Requires-Dist: nbformat; extra == "docs"
Requires-Dist: pandas>=2.0; extra == "docs"
Requires-Dist: polars>=1.0; extra == "docs"
Requires-Dist: pyarrow>=15; extra == "docs"
Requires-Dist: altair>=6; extra == "docs"
Requires-Dist: vl-convert-python>=1.9; extra == "docs"
Requires-Dist: formulaic>=1.0; extra == "docs"
Requires-Dist: great-tables>=0.22; extra == "docs"
Requires-Dist: duckdb>=1.0; extra == "docs"
Requires-Dist: scikit-learn>=1.0; extra == "docs"
Dynamic: license-file

<p align="center">
<a href="https://rich-iannone.github.io/whittaker/">
<img src="https://rich-iannone.github.io/whittaker/assets/whittaker_logo.png" alt="Whittaker" width="350">
</a>
</p>
<p align="center">Next-generation GAMs for Python: flexible smoothing, principled inference, beautiful output.</p>
<p align="center">
<a href="https://pypi.org/project/whittaker/"><img src="https://img.shields.io/pypi/v/whittaker?logo=python&logoColor=white&color=orange" alt="PyPI"></a>
<a href="https://pypi.org/project/whittaker/"><img src="https://img.shields.io/pypi/pyversions/whittaker.svg" alt="Python versions"></a>
<a href="https://choosealicense.com/licenses/mit/"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="MIT License"></a>
<a href="https://github.com/rich-iannone/whittaker/actions/workflows/ci.yml"><img src="https://github.com/rich-iannone/whittaker/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI"></a>
</p>
<p align="center">
<a href="https://www.repostatus.org/#active"><img src="https://www.repostatus.org/badges/latest/active.svg" alt="Repo Status"></a>
</p>
<p align="center">
<a href="https://rich-iannone.github.io/whittaker/"><img src="https://img.shields.io/badge/docs-project_website-blue.svg" alt="Documentation"></a>
<a href="https://github.com/rich-iannone/whittaker/graphs/contributors"><img src="https://img.shields.io/github/contributors/rich-iannone/whittaker" alt="Contributors"></a>
<a href="https://www.contributor-covenant.org/version/3/0/"><img src="https://img.shields.io/badge/Contributor%20Covenant-v3.0%20adopted-ff69b4.svg" alt="Contributor Covenant"></a>
</p>
<p align="center">
<img src="https://rich-iannone.github.io/whittaker/assets/readme_strip.png" alt="GAM smooth fits" width="700">
</p>

---

## What is Whittaker?

Whittaker is a Python library for Generalized Additive Models (GAMs), the flexible regression models that replace rigid linear assumptions with smooth, data-driven functions. Whether you're fitting a dose-response curve, modeling spatial patterns, or building prediction intervals, Whittaker gives you the smooth catalog, inference machinery, and diagnostic tools to do it right!

### Why Whittaker?

- it works with your dataframe library: Pandas, Polars, PyArrow, or anything supported by [Narwhals](https://narwhals-dev.github.io/narwhals/)
- it gives you the full smooth catalog from R's mgcv: thin plate regression splines, cubic splines, P-splines, tensor products, cyclic splines, random effects, factor smooths, and more
- smoothness selection is principled: REML by default, with GCV, ML, and fREML as alternatives
- you get beautiful, interactive plots: partial effects, diagnostics, term comparisons, and prediction intervals, all powered by [Altair](https://altair-viz.github.io/)
- it goes well beyond the mean: distributional regression (GAMLSS), quantile regression, conformal prediction, causal inference, streaming GAMs, and functional regression are all built in
- it offers principled Bayesian inference: variational inference for fast approximate posteriors, and NUTS (No-U-Turn Sampler) MCMC for exact posterior sampling with full convergence diagnostics

## What's included

Core GAM fitting:

- **GAM**: the central class that can fit penalized regression splines with automatic smoothness selection via REML, GCV, or ML. Full summary, diagnostics, and partial-effect visualization
- **Formula syntax**: R-style formulas like `"y ~ s(x1) + s(x2, k=20) + te(x3, x4) + x5"` with smooth terms, tensor products, linear terms, interactions, offsets, and by-variable smooths
- **Response families**: Gaussian, Poisson, Binomial, Gamma, Negative Binomial, Beta, Tweedie, Inverse Gaussian, Cox PH, and more. Each with appropriate link functions and variance structure
- **Smooth basis types**: TPRS (default), cubic regression splines, P-splines, cyclic variants, shrinkage smooths, thin plate splines, Duchon splines, Gaussian processes, soap film smooths, Markov random fields, random effects, and factor smooths
- **Shape constraints**: monotone increasing/decreasing, convex, and concave smooths via constrained P-splines with PAVA projection

Prediction and inference:

- **Prediction**: point estimates, standard errors, confidence intervals (pointwise and simultaneous), prediction intervals, and term-level contributions. All on response or link scale
- **Diagnostics**: `model.summary()` for EDF and significance tests, `model.check()` for basis dimension adequacy (k-index test), concurvity analysis, and residual plots
- **Cross-validation**: k-fold CV with deviance, MSE, or MAE scoring via `cross_validate()`

Advanced models:

- **Bayesian inference**: variational inference (`method="VI"`) for fast approximate posteriors, and NUTS MCMC (`method="MCMC"`) for exact posterior sampling (both with R-hat, ESS, and divergence diagnostics)
- **Distributional regression** (`GAMLSS`): model location, scale, and shape simultaneously (Gaussian, Gamma, and Beta location-scale families, plus zero-inflated Poisson and Negative Binomial)
- **Quantile regression** (`QuantileGAM`): fit conditional quantiles with ELF loss, optional non-crossing constraints, and sigma calibration
- **Conformal prediction** (`ConformalPredictor`): distribution-free prediction intervals via split, CV+, and jackknife+ methods
- **Causal inference** (`CausalGAM`): double/debiased machine learning for ATE and CATE estimation, with mediation analysis
- **Streaming GAMs** (`StreamingGAM`): incremental fitting via sufficient statistics with exponential decay for tracking distribution shift
- **Multi-response GAMs** (`MultiResponseGAM`): joint fitting of multiple responses with optional residual correlation modeling
- **Functional regression** (`FunctionalGAM`): scalar-on-function regression with B-spline or Fourier bases for functional covariates

Scalability and deployment:

- **Large datasets**: `BigGAM` (discretized P-IRLS), `PolarsGAM` (streaming from Polars/files), and `DuckDBGAM` (SQL-native streaming) for datasets that exceed memory
- **Serialization**: `save_gam()` / `load_gam()` for compact `.npz` archives, and `to_mgcv_dict` / `from_mgcv_dict` for R interoperability
- **scikit-learn integration**: `GAMRegressor` and `GAMClassifier` for use in pipelines and grid search

## Get started

Here's a simple example: fit a smooth to noisy data, inspect the fit, and predict on new observations.

```python
import numpy as np
import whittaker as wk

# Generate some data
rng = np.random.default_rng(23)
x = np.linspace(0, 2 * np.pi, 200)
y = np.sin(x) + rng.normal(0, 0.3, 200)

# Fit a GAM with automatic smoothness selection
model = wk.GAM("y ~ s(x)")
model.fit({"x": x, "y": y}, method="REML")
model.summary()
```

```python
# Predict with standard errors
preds = model.predict({"x": np.linspace(0, 2 * np.pi, 50)}, se=True)
```

<p align="center">
<img src="https://rich-iannone.github.io/whittaker/assets/readme_partial_effects.png" alt="Partial effects plot" width="700">
</p>

```python
# Check model adequacy
model.check()
```

<p align="center">
<img src="https://rich-iannone.github.io/whittaker/assets/readme_diagnostics.png" alt="Diagnostic plots" width="700">
</p>

See the [user guide](https://rich-iannone.github.io/whittaker/user-guide/get-started.html) for a comprehensive tour of all features, from basic smoothing to distributional regression and causal inference.

## See more

A more complete example showing multiple predictors, a non-Gaussian family, and model comparison:

```python
import numpy as np
import whittaker as wk

# Simulate Poisson count data with two smooth effects
rng = np.random.default_rng(23)
n = 500
x1 = rng.uniform(0, 2 * np.pi, n)
x2 = rng.uniform(0, 1, n)
mu = np.exp(0.5 + 0.8 * np.sin(x1) + 2 * x2)
y = rng.poisson(mu).astype(float)

data = {"x1": x1, "x2": x2, "y": y}

# Fit a Poisson GAM
model = wk.GAM("y ~ s(x1) + s(x2)", family=wk.Poisson())
model.fit(data, method="REML")
model.summary()

# Cross-validate to check out-of-sample performance
cv = wk.cross_validate("y ~ s(x1) + s(x2)", data, family=wk.Poisson(), n_folds=5)
print(f"CV score: {cv.cv_score:.4f} (SE: {cv.cv_se:.4f})")

# Predict on new data with confidence intervals
preds = model.predict(
    {"x1": np.linspace(0, 2 * np.pi, 100), "x2": np.full(100, 0.5)},
    interval="confidence", level=0.95,
)

# Save the fitted model for deployment
wk.save_gam(model, "poisson_model.npz")
loaded = wk.load_gam("poisson_model.npz")
```

## Installation

```bash
pip install whittaker
```

For optional backends and visualization:

```bash
pip install "whittaker[all]"       # Pandas, Polars, PyArrow, Altair
pip install "whittaker[pl,altair]" # Just Polars and Altair
```

## License

MIT (c) Richard Iannone.
