ZeroInflatedPoisson

Zero-inflated Poisson (ZIP) family for GAMLSS.

Usage

Source

ZeroInflatedPoisson()

Count data often has more zeros than a plain Poisson model can explain — for example, when some observations are structurally incapable of the event occurring at all (a “never-taker” always reports zero), in addition to the zeros that arise simply because the Poisson mean is low. ZeroInflatedPoisson models this as a mixture: with probability pi an observation is a structural zero, and with probability 1 - pi it is drawn from an ordinary Poisson(mu) distribution (which can itself still produce a zero). Both mu and pi are modeled as smooth functions of covariates through GAMLSS, so the excess-zero probability and the count intensity can each vary independently across the covariate space. If overdispersion remains even among the non-structural-zero counts, use ZeroInflatedNegativeBinomial instead.

Notes

Two distributional parameters are modeled, each with its own link:

g_{\mu}(\mu) = \log(\mu), \qquad g_{\pi}(\pi) = \log\!\left(\frac{\pi}{1-\pi}\right).

The probability mass function is a mixture of a point mass at zero and a Poisson distribution:

P(Y = 0) = \pi + (1-\pi) e^{-\mu}, \qquad P(Y = k) = (1-\pi) \frac{\mu^{k} e^{-\mu}}{k!} \quad \text{for } k > 0.

Examples

Fit a GAMLSS for count data with excess zeros:

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

rng = np.random.default_rng(0)
n = 300
x = np.linspace(0, 2 * np.pi, n)
mu = np.exp(0.5 + 0.4 * np.sin(x))
pi = expit(-1.0 + 0.8 * np.cos(x))

is_structural_zero = rng.uniform(size=n) < pi
counts = rng.poisson(mu)
y = np.where(is_structural_zero, 0, counts).astype(float)

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

model = wk.GAMLSS(
    formulas={"mu": "y ~ s(x)", "pi": "y ~ s(x)"},
    family=wk.ZeroInflatedPoisson(),
)
model.fit(data)
print(model.summary())
GAMLSS fit summary
========================================
Family: ZeroInflatedPoisson(mu=log, pi=logit)
N obs: 300
Global deviance: 886.8940
AIC: 896.9039
BIC: 915.4410
Log-likelihood: -443.4470
Converged: True (6 iterations)

--- mu ---
  EDF total: 2.00
  Smooth 1: edf = 1.00

--- pi ---
  EDF total: 3.00
  Smooth 1: edf = 2.00

Attributes

Name Description
parameter_names Names of the distributional parameters modeled by this family.

parameter_names

Names of the distributional parameters modeled by this family.

parameter_names: tuple[str, …]

Methods

Name Description
d2l_dtheta2() Compute the (negative) second derivative of the log-likelihood.
dl_dtheta() Compute the first derivative of the log-likelihood with respect to a parameter.
initialize() Generate starting values for mu and pi before the first IRLS iteration.
link() Map a distributional parameter from its natural scale to the link scale.
link_derivative() Compute the derivative of the link function with respect to the natural parameter.
link_inverse() Map a distributional parameter from the link scale back to its natural scale.
log_likelihood() Compute the total log-likelihood under the zero-inflated Poisson model.
simulate() Draw a random sample of counts from the fitted zero-inflated Poisson model.

d2l_dtheta2()

Compute the (negative) second derivative of the log-likelihood.

Usage

Source

d2l_dtheta2(
    param,
    y,
    params,
)

Implements the family-specific working weight used by GAMLSS’s Fisher-scoring updates. The returned values approximate -\partial^2 \ell / \partial \theta^2 for theta in {"mu", "pi"}, computed separately for zero and positive observations to reflect the zero-inflated mixture, and floored at _EPS to keep weights strictly positive.

Parameters

param: str

Name of the parameter, either "mu" or "pi".

y: NDArray

Observed response values.

params: dict of str to NDArray
Current fitted values of all distributional parameters ("mu" and "pi").

Returns

NDArray
Elementwise working weight for param.

dl_dtheta()

Compute the first derivative of the log-likelihood with respect to a parameter.

Usage

Source

dl_dtheta(
    param,
    y,
    params,
)

Implements the family-specific score function used by GAMLSS to build the working response at each IRLS iteration. For zero observations the derivative accounts for the mixture weight of the point mass versus the Poisson component at zero; for positive observations it reduces to the ordinary Poisson score. For mu this is \partial \ell / \partial \mu, and for pi it is \partial \ell / \partial \pi, both derived from the zero-inflated Poisson log-likelihood.

Parameters

param: str

Name of the parameter to differentiate with respect to, either "mu" or "pi".

y: NDArray

Observed response values.

params: dict of str to NDArray
Current fitted values of all distributional parameters ("mu" and "pi").

Returns

NDArray
Elementwise first derivative of the log-likelihood with respect to param.

initialize()

Generate starting values for mu and pi before the first IRLS iteration.

Usage

Source

initialize(y)

Sets mu to the mean of the strictly positive observations (or 1.0 if none are positive) for every observation, and sets pi to half the observed fraction of zeros, clipped to [0.01, 0.5], giving GAMLSS a reasonable starting point without any structural information about the covariates.

Parameters

y: NDArray
Observed response values.

Returns

dict of str to NDArray
Initial values for "mu" and "pi", each broadcast to the shape of y.




log_likelihood()

Compute the total log-likelihood under the zero-inflated Poisson model.

Usage

Source

log_likelihood(
    y,
    params,
)

Evaluates

\ell = \sum_{i: y_i = 0} \log\!\big(\pi_i + (1-\pi_i) e^{-\mu_i}\big) + \sum_{i: y_i > 0} \Big[\log(1-\pi_i) + y_i \log(\mu_i) - \mu_i - \log(y_i!)\Big],

summing the point-mass contribution for structural zeros with the Poisson contribution for observed counts.

Parameters

y: NDArray

Observed response values.

params: dict of str to NDArray
Fitted values of "mu" and "pi" for each observation.

Returns

float
Total log-likelihood summed over all observations.

simulate()

Draw a random sample of counts from the fitted zero-inflated Poisson model.

Usage

Source

simulate(
    params,
    rng,
)

For each observation, flips a pi-weighted coin to decide whether it is a structural zero; observations that are not structural zeros are drawn from Poisson(mu), so a sampled value can still be zero even when it was not selected as a structural zero.

Parameters

params: dict of str to NDArray

Fitted values of "mu" and "pi" for each observation to simulate.

rng: object
Random number generator exposing uniform and poisson methods, such as a numpy.random.Generator.

Returns

NDArray
Simulated response values, one per row of params.