# Binomial


Binomial family with logit (canonical) link.


Usage

``` python
Binomial()
```


The Binomial family models a response that is either binary (`0`/`1`) or a proportion in `[0, 1]` (e.g. the fraction of successes out of a known number of trials). The logit link maps the unit interval to the whole real line, so the linear predictor is unconstrained while the fitted mean is always a valid probability. Use it for classification-style GAMs, for aggregated success/trial proportions, or wherever the outcome represents the probability of an event. The logit link additionally gives coefficients a log-odds interpretation: a one-unit increase in a covariate changes the log-odds of success by `coefficient`, and multiplies the odds by `exp(coefficient)`.


## Notes

The canonical link is the logit function:

 g(\mu) = \log\\\left(\frac{\mu}{1-\mu}\right) 

with inverse the logistic sigmoid \mu = g^{-1}(\eta) = 1 / (1 + e^{-\eta}). The variance function is

 V(\mu) = \mu(1-\mu), 

which is largest near \mu = 0.5 and shrinks toward zero as \mu approaches either boundary. The deviance is

 D(y, \hat\mu) = 2 \sum_i \left\[ y_i \log\\\left(\frac{y_i}{\hat\mu_i}\right) + (1 - y_i) \log\\\left(\frac{1 - y_i}{1 - \hat\mu_i}\right) \right\] . 


## Examples

Fit a GAM to a binary outcome with a smooth, nonlinear log-odds relationship:


``` python
import numpy as np
import whittaker as wk
from scipy.special import expit

rng = np.random.default_rng(0)
n = 300
x = np.linspace(-3, 3, n)
p = expit(np.sin(x))
y = rng.binomial(1, p)

data = {"x": x, "y": y}

model = wk.GAM("y ~ s(x)", family=wk.Binomial())
model.fit(data, method="REML")
print(model.summary())
```


    GAM fit summary
    ============================================================
    Formula:    y ~ s(x)
    Family:     Binomial(link='logit')
    Inference:  REML
    Observations: 300
    Coefficients: 10

    Parametric coefficients:
      Term                       Estimate    Std.Err    z value    p-value
      ------------------------ ---------- ---------- ---------- ----------
      (Intercept)                 -0.0002     0.1170     -0.002     0.9984

    Approximate significance of smooth terms:
      Term                        EDF Ref.df     Chi.sq    p-value
      ------------------------ ------ ------ ---------- ----------
      s(x)                       2.79      3      9.389    0.02454

    Total EDF:  3.79
    Scale est:  1.000000
    Deviance:   404.3107
    Null dev:   415.8883
    Dev. expl:  2.8%
    GCV score:  1.382449
    AIC:        411.90
    BIC:        425.95


## Attributes

| Name | Description |
|----|----|
| [scale_known](#scale_known) | Whether the dispersion parameter is fixed. Always `True` for Binomial. |

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


### scale_known


Whether the dispersion parameter is fixed. Always `True` for Binomial.


`scale_known: bool`


The Bernoulli/Binomial distribution has no free dispersion parameter, so the scale is fixed at `1` and is never estimated during fitting. This affects how [GAM.summary()](GAM.md#whittaker.GAM.summary) reports the scale and how many degrees of freedom are attributed to dispersion.


## Methods

| Name | Description |
|----|----|
| [deviance()](#deviance) | Total binomial deviance, the (weighted) sum of `unit_deviance`. |
| [initialize()](#initialize) | Starting values for `mu`: a constant shrunk toward the overall mean of `y`. |
| [link()](#link) | Apply the logit link: \eta = \log(\mu / (1-\mu)). |
| [link_derivative()](#link_derivative) | Derivative of the logit link: g'(\mu) = 1 / (\mu(1-\mu)). |
| [link_inverse()](#link_inverse) | Apply the inverse logit link (logistic sigmoid): \mu = 1 / (1 + e^{-\eta}). |
| [log_likelihood()](#log_likelihood) | Bernoulli/binomial log-likelihood \ell_i = y_i \log(\mu_i) + (1-y_i)\log(1-\mu_i). |
| [simulate()](#simulate) | Simulate Bernoulli response values with success probability `mu`. |
| [unit_deviance()](#unit_deviance) | Per-observation binomial deviance contributions. |
| [variance()](#variance) | Binomial variance function: V(\mu) = \mu(1-\mu). |

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


### deviance()


Total binomial deviance, the (weighted) sum of `unit_deviance`.


Usage

``` python
deviance(
    y,
    mu,
    *,
    weights=None,
)
```


#### Parameters


`y: NDArray`  
Observed response values (0/1 or proportions in `[0, 1]`), shape `(n,)`.

`mu: NDArray`  
Fitted conditional mean values (probabilities), shape `(n,)`.

`weights: NDArray | None = None`  
Optional prior weights, shape `(n,)`.


#### Returns


`float`  
The total (weighted) deviance.


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


### initialize()


Starting values for `mu`: a constant shrunk toward the overall mean of `y`.


Usage

``` python
initialize(y)
```


Every observation is initialized to `(mean(y) + 0.5) / 2`, a value strictly inside `(0, 1)` regardless of whether `y` contains only `0`s, only `1`s, or a mix, avoiding `logit(0)` or `logit(1)` on the first P-IRLS iteration.


#### Parameters


`y: NDArray`  
Observed response values, shape `(n,)`.


#### Returns


`NDArray`  
Starting values for `mu`, shape `(n,)`.


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


### link()


Apply the logit link: \eta = \log(\mu / (1-\mu)).


Usage

``` python
link(mu)
```


#### Parameters


`mu: NDArray`  
Conditional mean values (probabilities), shape `(n,)`. Must lie in `(0, 1)`.


#### Returns


`NDArray`  
Linear predictor (log-odds) values, shape `(n,)`.


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


### link_derivative()


Derivative of the logit link: g'(\mu) = 1 / (\mu(1-\mu)).


Usage

``` python
link_derivative(mu)
```


#### Parameters


`mu: NDArray`  
Conditional mean values (probabilities), shape `(n,)`; clipped away from `0` and `1` to avoid division by zero.


#### Returns


`NDArray`  
Derivative values, shape `(n,)`.


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


### link_inverse()


Apply the inverse logit link (logistic sigmoid): \mu = 1 / (1 + e^{-\eta}).


Usage

``` python
link_inverse(eta)
```


#### Parameters


`eta: NDArray`  
Linear predictor (log-odds) values, shape `(n,)`.


#### Returns


`NDArray`  
Conditional mean values (probabilities), shape `(n,)`, always in `(0, 1)`.


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


### log_likelihood()


Bernoulli/binomial log-likelihood \ell_i = y_i \log(\mu_i) + (1-y_i)\log(1-\mu_i).


Usage

``` python
log_likelihood(
    y,
    mu,
    scale,
    *,
    weights=None,
)
```


The `scale` argument is accepted for interface compatibility but ignored, since the Binomial distribution has no free dispersion parameter (`scale_known` is `True`).


#### Parameters


`y: NDArray`  
Observed response values, shape `(n,)`.

`mu: NDArray`  
Fitted conditional mean values (probabilities), shape `(n,)`.

`scale: float`  
Ignored.

`weights: NDArray | None = None`  
Optional prior weights, shape `(n,)`.


#### Returns


`float`  
The total log-likelihood.


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


### simulate()


Simulate Bernoulli response values with success probability `mu`.


Usage

``` python
simulate(
    mu,
    scale,
    rng,
)
```


#### Parameters


`mu: NDArray`  
Success probabilities (fitted values), shape `(n,)`.

`scale: float`  
Ignored (the Binomial distribution has no free dispersion parameter).

`rng: np.random.Generator`  
A `numpy.random.Generator` instance.


#### Returns


`NDArray`  
Simulated 0/1 response values, shape `(n,)`.


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


### unit_deviance()


Per-observation binomial deviance contributions.


Usage

``` python
unit_deviance(
    y,
    mu,
)
```


Computes d_i = 2 \left\[ y_i \log(y_i/\hat\mu_i) + (1-y_i)\log((1-y_i)/(1-\hat\mu_i)) \right\], with the usual conventions that the y_i \log(\cdot) term vanishes when y_i = 0 and the (1-y_i)\log(\cdot) term vanishes when y_i = 1.


#### Parameters


`y: NDArray`  
Observed response values, shape `(n,)`.

`mu: NDArray`  
Fitted conditional mean values (probabilities), shape `(n,)`.


#### Returns


`NDArray`  
Per-observation deviance contributions, shape `(n,)`.


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


### variance()


Binomial variance function: V(\mu) = \mu(1-\mu).


Usage

``` python
variance(mu)
```


Largest at \mu = 0.5 and shrinking toward zero as \mu approaches either boundary.


#### Parameters


`mu: NDArray`  
Conditional mean values (probabilities), shape `(n,)`.


#### Returns


`NDArray`  
Variance values \mu(1-\mu), shape `(n,)`.
