# ModelMatrix


Numeric design matrix, penalties, and metadata produced by :func:[build_model_matrix](build_model_matrix.md#whittaker.build_model_matrix).


Usage

``` python
ModelMatrix(
    X,
    penalties,
    smooths=list(),
    column_names=list(),
    has_intercept=True,
    n_parametric=0,
    offset=None,
    offset_expressions=list(),
    response=(lambda: np.empty(0))(),
)
```


A [ModelMatrix](ModelMatrix.md#whittaker.ModelMatrix) is the numeric form of a fitted `~whittaker.gam.GAM`'s formula: the design matrix `X` such that the linear predictor is `eta = X @ beta` (plus an optional offset), together with one quadratic penalty matrix per smooth term and enough metadata (`smooths`, `column_names`) to later build a matching prediction matrix or attribute coefficients back to individual terms. [GAM.fit()](GAM.md#whittaker.GAM.fit) calls [build_model_matrix](build_model_matrix.md#whittaker.build_model_matrix) once and stores the result so that `predict()`, `summary()`, and [plot()](GAM.md#whittaker.GAM.plot) can all refer back to the same column layout.


## Attributes


`X: numpy.ndarray`  
Full design matrix, shape `(n, p)` where *n* is the number of observations and *p* is the total number of columns (intercept + parametric + all constrained smooth bases).

`penalties: list of numpy.ndarray`  
List of `(p, p)` penalty matrices, one per smooth term (or per marginal/null-space penalty within a term), each containing that term's penalty embedded in the appropriate block of the full model dimension so `beta.T @ S_j @ beta` depends only on that term's coefficients.

`smooths: list of SmoothInfo`  
Per-smooth metadata ([SmoothInfo](SmoothInfo.md#whittaker.SmoothInfo)) in formula order.

`column_names: list of str`  
Human-readable label for each column of `X`, in order, e.g. `"(Intercept)"`, a covariate name for a linear term, or `"s(x)[0]"` for the first basis function of a smooth term.

`has_intercept: bool`  
Whether column 0 is the intercept.

`n_parametric: int`  
Number of parametric (linear + interaction) columns, not counting the intercept.

`offset: numpy.ndarray or None`  
Offset vector of shape `(n,)`, or `None` if no offset term.

`offset_expressions: list of str`  
Column expressions summed to form `offset`, retained so `predict_offset` can reconstruct the offset for new data.

`response: numpy.ndarray`  
The response column as a 1-D float array.


## Attributes

| Name | Description |
|----|----|
| [n_coefs](#n_coefs) | Total number of model coefficients. |
| [n_obs](#n_obs) | Number of observations used to build the design matrix. |
| [penalty_matrix](#penalty_matrix) | Combined penalty `S_total = sum(S_j)` (unweighted by `lambda`). |

------------------------------------------------------------------------


### n_coefs


Total number of model coefficients.


`n_coefs: int`


Counts every column of `X`: the intercept (if present), all parametric (linear/interaction) columns, and every basis function of every smooth term after identifiability constraints have been applied.


------------------------------------------------------------------------


### n_obs


Number of observations used to build the design matrix.


`n_obs: int`


------------------------------------------------------------------------


### penalty_matrix


Combined penalty `S_total = sum(S_j)` (unweighted by `lambda`).


`penalty_matrix: NDArray`


Sums every entry of `penalties` element-wise into a single `(n_coefs, n_coefs)` matrix, without applying any per-smooth smoothing parameter. This is a convenience for inspecting the overall penalty structure; it is *not* what the fitting engine (`~whittaker.pirls.pirls_fit`) actually optimizes against, since each term's contribution should be weighted by its own `lambda_j` before being summed. Use `penalties` directly, combined with the fitted smoothing parameters, for anything that needs the weighted penalty.
