# save_gam()


Save a fitted GAM to a `.npz` archive.


Usage

``` python
save_gam(
    model,
    path,
)
```


Serializes everything needed to reconstruct a fitted `~whittaker.gam.GAM` for prediction and inference without recomputing basis fits or re-running P-IRLS. Use this to persist a model between sessions, ship a fitted model to another machine, or cache an expensive fit. The archive is a standard `numpy` `.npz` file (produced with `numpy.savez_compressed`) and can, in principle, be inspected with `numpy.load` alone, though [load_gam](load_gam.md#whittaker.load_gam) is the supported way to read it back.

Internally the archive stores two kinds of data under one file:

- A single JSON-encoded metadata blob under the key `"__metadata__"`, containing the formula (response, intercept flag, and each term), the family (class name and any extra parameters such as a Tweedie power or negative-binomial dispersion), fit statistics (smoothing parameters, scale, GCV score, EDF, deviance, iteration count, convergence flag, AIC/BIC, etc.), model-matrix metadata (column names, intercept/parametric counts, offset expressions), and, per smooth term, its formula term, coefficient column range, null-space dimension, penalty indices, and basis state (attribute values of the fitted `~whittaker.smooths.base.SmoothBasis`).
- Raw `numpy` arrays stored alongside the metadata: `coefficients`, `linear_predictor`, `fitted_values`, `residuals`, the training design matrix `X`, the [response](FunctionalGAM.md#whittaker.FunctionalGAM.response) vector, and, when present, `weights`, `prior_weights`, `pseudo_data`, and `offset`. Each smooth's penalty matrix is stored as `penalty_{i}` (one array per penalty block, in the order the smooths contribute penalties). Any array-valued attribute of a smooth's fitted basis (e.g. knot locations, training covariate values) is stored under a key of the form `smooth_{idx}_basis_{attr}` (or `smooth_{idx}_basis_{attr}_{subkey}` for nested dict attributes), with a `{"__ndarray__": key}` pointer left in the metadata blob so [load_gam](load_gam.md#whittaker.load_gam) can find it.

Note that the archive has no explicit format-version field: there is currently no mechanism to detect or migrate across schema changes, so a saved archive is only guaranteed to load correctly with a `whittaker` version compatible with the one that wrote it.


## Parameters


`model: GAM`  
A fitted `~whittaker.gam.GAM` instance, i.e. one on which `fit()` has already been called.

`path: str or pathlib.Path`  
Output file path. `numpy.savez_compressed` appends a `.npz` extension automatically if the given path does not already end in one.


## Raises


`TypeError`  
If `model` is not a [GAM](GAM.md#whittaker.GAM) instance.

`RuntimeError`  
If `model` has not been fitted (`model.is_fitted` is `False`).


## Examples


``` python
import numpy as np
import whittaker as wt
from whittaker.io import save_gam, load_gam

rng = np.random.default_rng(0)
x = np.sort(rng.uniform(0, 1, 200))
y = np.sin(2 * np.pi * x) + rng.normal(scale=0.2, size=200)

model = wt.GAM("y ~ s(x)").fit({"x": x, "y": y})

save_gam(model, "gam_model.npz")
reloaded = load_gam("gam_model.npz")

new_x = np.linspace(0, 1, 5)
np.allclose(
    model.predict({"x": new_x}).values,
    reloaded.predict({"x": new_x}).values,
)
```


    True
