Skip to main content

Distribution Families

The default outcome is Gaussian. Pass families= when an outcome is a probability, a count, an overdispersed count, or a continuous variable with heavy tails. The family changes the likelihood and the link; it does not change the structural equation you write in the spec. Predictor transforms are the other extension, and the two combine: a transformed predictor can feed a non-Gaussian outcome.

Family Syntax Link Extra parameters
Gaussian (default) identity σ
Bernoulli families={"Y": "bernoulli"} logit —
Poisson families={"Y": "poisson"} log —
NegBinomial families={"Y": "negbinomial"} log dispersion α
StudentT families={"Y": "studentt"} identity scale σ, degrees of freedom ν
model = pathmc.model(
    """
    click ~ ad_spend + placement
    """,
    data=df,
    families={"click": "bernoulli"},
)
model.fit(random_seed=42)

The structural equation is still on the link scale. A Bernoulli coefficient is a change in log-odds, not a change in probability. Read probability-scale effects from ate(), comparisons(), or slopes() after fitting, not from the coefficient itself.

StudentT uses the same identity link as Gaussian. sigma_{var} (for example sigma_Y) is the StudentT scale passed through to PyMC, not the residual standard deviation. When nu > 2, that residual standard deviation is sigma_{var} * sqrt(nu / (nu - 2)). simulate() accepts the scale unchanged and does not convert a supplied residual standard deviation into the scale. Cross-sectional draws use that scale with nu. Scan lag-carry reconstructs each state as mu + sigma_{var} * e, where e is a unit normal innovation, so the innovation scale is sigma_{var} and nu is not used. A Gaussian sigma_{var} is still the residual standard deviation, which equals its scale.

Scan-compiled panel models (lag() or adstock) keep that log link, then clip the Poisson or NegBinomial linear predictor to [-20, 20] before exponentiating. The clip is a numerical guard so one timestep cannot poison the recursion. pathmc warns when that linear predictor approaches the bound, including simulate() and a large intercept with small observed counts, not only when an observed count is already near exp(20).

Posterior predictive checks

After sampling, .predict() draws new outcomes from the fitted likelihood. That is the check that the family, and not only the mean structure, is plausible: simulated clicks should look like binary data, and simulated counts should look like counts.

idata = model.predict()
# idata.posterior_predictive contains simulated outcomes

See the Media Mix Models example for transforms and a posterior predictive check together.