Split out of #149, which named "Gamma / log-normal" frailty. The Gamma half shipped; this is the remainder, and it is much smaller than the other two pieces (#341, #342).
Current behaviour
FrailtyFitter takes a family argument and rejects anything but Gamma:
if family != "gamma":
raise ValueError(
"Only the 'gamma' frailty family is available; got {family!r}."
)
So Frailty(Weibull, family="lognormal") raises. The argument exists precisely because a second family was anticipated.
What a log-normal family needs
Gamma frailty is the easy case: the marginal likelihood after integrating out u has a closed form (the gammaln terms in frailty_fitter.py), which is why the fit is fast.
Log-normal frailty has no such closed form. The group contribution becomes a one-dimensional integral over the frailty, needing either
- Gauss-Hermite quadrature per group — natural for a log-normal, since the integral is already in the right form after substituting
u = exp(w) with w normal. A modest number of nodes is usually enough, and the whole thing stays differentiable for autograd; or
- Laplace approximation — cheaper, less accurate in small groups.
Quadrature is the better default here: groups are typically small in reliability data, which is exactly where Laplace is weakest.
Why bother
Gamma and log-normal frailty put different weight in the tail of the frailty distribution, so they disagree about how much of the spread between groups is real heterogeneity. Being able to fit both and compare on likelihood or AIC is the usual way to tell whether a conclusion depends on that choice. With only one family available there is nothing to compare against.
Scope
- Quadrature-based marginal likelihood for the log-normal family.
family="lognormal" accepted by FrailtyFitter and the Frailty factory; theta keeps its meaning as the frailty variance.
- Serialisation carries
family already, so a round-trip test should extend to the new value.
- A test that both families recover the frailty variance on data simulated from each.
Split out of #149, which named "Gamma / log-normal" frailty. The Gamma half shipped; this is the remainder, and it is much smaller than the other two pieces (#341, #342).
Current behaviour
FrailtyFittertakes afamilyargument and rejects anything but Gamma:So
Frailty(Weibull, family="lognormal")raises. The argument exists precisely because a second family was anticipated.What a log-normal family needs
Gamma frailty is the easy case: the marginal likelihood after integrating out
uhas a closed form (thegammalnterms infrailty_fitter.py), which is why the fit is fast.Log-normal frailty has no such closed form. The group contribution becomes a one-dimensional integral over the frailty, needing either
u = exp(w)withwnormal. A modest number of nodes is usually enough, and the whole thing stays differentiable for autograd; orQuadrature is the better default here: groups are typically small in reliability data, which is exactly where Laplace is weakest.
Why bother
Gamma and log-normal frailty put different weight in the tail of the frailty distribution, so they disagree about how much of the spread between groups is real heterogeneity. Being able to fit both and compare on likelihood or AIC is the usual way to tell whether a conclusion depends on that choice. With only one family available there is nothing to compare against.
Scope
family="lognormal"accepted byFrailtyFitterand theFrailtyfactory;thetakeeps its meaning as the frailty variance.familyalready, so a round-trip test should extend to the new value.