# ShrinkageTPRS


Shrinkage Thin Plate Regression Spline.


Usage

``` python
ShrinkageTPRS(
    k=10,
    m=2,
)
```


Equivalent to mgcv's `bs="ts"` basis. Ordinary penalized smooths (such as [TPRS](TPRS.md#whittaker.TPRS)) always leave a low-dimensional null space -- typically the constant and linear terms -- completely unpenalized, so even a very large smoothing parameter `lambda` cannot remove the term from the model entirely: the fit can be flattened to a straight line, but not to exactly zero. [ShrinkageTPRS](ShrinkageTPRS.md#whittaker.ShrinkageTPRS) fixes this by adding a second penalty that acts specifically on the null space, using the double-penalty construction of Marra & Wood (2011). With two independently-chosen smoothing parameters, the fitting machinery can drive both the wiggly part and the null-space part of the smooth to zero simultaneously, so the whole term can be shrunk out of the model. This makes [ShrinkageTPRS](ShrinkageTPRS.md#whittaker.ShrinkageTPRS) a good choice over plain [TPRS](TPRS.md#whittaker.TPRS) whenever a term's inclusion is itself uncertain and you want automatic variable selection via GCV or REML rather than a separate hypothesis test.

The basis functions are identical to [TPRS](TPRS.md#whittaker.TPRS) (`bs="tp"`); only the penalty structure differs.


## Parameters


`k: int = ``10`  
Total number of basis functions, including the `M` null-space columns. The default is `10`. See [TPRS](TPRS.md#whittaker.TPRS) for guidance on choosing `k`.

`m: int = ``2`  
Spline order. Must satisfy `2m > d` where [d](TPRS.md#whittaker.TPRS.d) is the covariate dimension. The default is `2`. See [TPRS](TPRS.md#whittaker.TPRS) for guidance on choosing `m`.


## Notes

[ShrinkageTPRS](ShrinkageTPRS.md#whittaker.ShrinkageTPRS) reuses the exact basis construction of [TPRS](TPRS.md#whittaker.TPRS): the first `M` columns span the polynomial null space (degree `<= m - 1` monomials) and the remaining `k - M` columns are the truncated eigenbasis of the projected thin-plate kernel. What changes is the penalty. Instead of a single penalty matrix, `penalty_matrices()` returns **two** matrices:

 \mathbf{S}\_{\text{wiggle}} = \operatorname{diag}(0, \ldots, 0, \lambda_1, \ldots, \lambda\_{k-M}), \qquad \mathbf{S}\_{\text{null}} = \begin{pmatrix} \mathbf{I}\_M & \mathbf{0} \\ \mathbf{0} & \mathbf{0} \end{pmatrix}, 

where `S_wiggle` is exactly the ordinary [TPRS](TPRS.md#whittaker.TPRS) penalty (zero on the null-space block) and `S_null` is a projection matrix that is the identity on the null-space block and zero elsewhere. During fitting, each matrix is scaled by its own smoothing parameter, `lambda_wiggle` and `lambda_null`, and the two contributions are added together:

 \lambda\_{\text{wiggle}} \\ \boldsymbol{\beta}^\top \mathbf{S}\_{\text{wiggle}} \boldsymbol{\beta} + \lambda\_{\text{null}} \\ \boldsymbol{\beta}^\top \mathbf{S}\_{\text{null}} \boldsymbol{\beta}. 

Because `S_wiggle + S_null` is strictly positive definite (it has no zero eigenvalues once both penalties act together), the combined penalty null space is empty, which is why `null_space_dimension()` returns `0` for this basis -- none of the `k` basis functions is exempt from penalization once both smoothing parameters are positive. If `lambda_null` is estimated to be very large during fitting, the null-space coefficients are effectively zeroed and the term is excluded from the model, which is the mechanism behind automatic term selection.


## Examples


``` python
import numpy as np
from whittaker.smooths import ShrinkageTPRS

rng = np.random.default_rng(0)
x = rng.uniform(0, 1, 100)

basis = ShrinkageTPRS(k=10).fit(x)
B = basis.basis_matrix(x)
S_wiggle, S_null = basis.penalty_matrices()
B.shape, S_wiggle.shape, S_null.shape
```


    ((100, 10), (10, 10), (10, 10))


## Methods

| Name | Description |
|----|----|
| [null_space_dimension()](#null_space_dimension) | Return `0`: the combined double penalty leaves no unpenalized null space. |
| [penalty_matrices()](#penalty_matrices) | Return the two penalty matrices used for shrinkage. |

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


### null_space_dimension()


Return `0`: the combined double penalty leaves no unpenalized null space.


Usage

``` python
null_space_dimension()
```


#### Returns


`int`  
Always `0` for [ShrinkageTPRS](ShrinkageTPRS.md#whittaker.ShrinkageTPRS), since `S_wiggle + S_null` is positive definite.


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


### penalty_matrices()


Return the two penalty matrices used for shrinkage.


Usage

``` python
penalty_matrices()
```


#### Returns


`list[NDArray]`  
A two-element list `[S_wiggle, S_null]`, each of shape `(k, k)`. `S_wiggle` is the ordinary [TPRS](TPRS.md#whittaker.TPRS) wiggliness penalty (zero on the null-space block). `S_null` is a projection matrix that is the identity on the `M` null-space columns and zero elsewhere. Each matrix is intended to be paired with its own independently-estimated smoothing parameter during fitting.
