LogNormalPrior#

class pymc_marketing.special_priors.LogNormalPrior(dims=None, centered=True, **parameters)[source]#

Lognormal prior parameterized by positive-scale mean and std.

This prior differs from the standard LogNormal distribution, which takes log-scale parameters (mu_log, sigma_log). Instead, it is parameterized directly in terms of the mean and standard deviation (mean, std) on the positive scale, making it more intuitive and suitable for hierarchical modeling.

To achieve this, the lognormal parameters are computed internally from the positive-domain parameters:

\[\begin{split}\mu_{\text{log}} &= \ln \left( \frac{\text{mean}^2}{\sqrt{\text{mean}^2 + \text{std}^2}} \right) \\ \sigma_{\text{log}} &= \sqrt{ \ln \left( 1 + \frac{\text{std}^2}{\text{mean}^2} \right) }\end{split}\]

where \(\text{mean} > 0\) and \(\text{std} > 0\).

The prior is then defined as:

\[\phi \sim \text{LogNormal}(\mu_{\text{log}}, \sigma_{\text{log}})\]

This construction ensures that the resulting random variable has approximately the intended mean and variance on the positive scale, even when \(\text{mean}\) and \(\text{std}\) are themselves random variables.

Parameters:
meanPrior, float, int, array_like, optional

The mean of the distribution on the positive scale. It can be omitted only when the instance is used as a likelihood, in which case the mean is supplied later as mu by create_likelihood_variable().

stdPrior, float, int, array_like

The standard deviation of the distribution on the positive scale.

dimstuple[str, …], optional

The dimensions of the distribution, by default None.

centeredbool, optional

Whether to use the centered parameterization, by default True. Ignored when the instance is used as a likelihood, since an observed variable has no non-centered form.

References

Examples

Build a non-centered hierarchical model where information is shared across groups:

from pymc_marketing.special_priors import LogNormalPrior

prior = LogNormalPrior(
    mean=Prior("Gamma", mu=1.0, sigma=1.0),
    std=Prior("HalfNormal", sigma=1.0),
    dims=("geo",),
    centered=False,
)

Use it as an MMM likelihood under link="identity" by configuring only std. The linear predictor mu becomes the response-scale mean, so unlike a plain LogNormal likelihood the additive decomposition stays in the units of the target. Both the target and the fitted mu must be strictly positive; during sampling a non-positive mu is rejected with -inf log-probability rather than being silently folded to |mu|. This guard protects only log-probability evaluation: forward sampling is rewritten so that a non-positive mu produces draws of exactly exp(-inf) = 0, with no error (see create_likelihood_variable()); the MMM warns when prior or posterior predictive draws show this signature. The MMM’s default identity-link intercept prior is centered at zero, so roughly a third of prior-predictive draws hit this path out of the box; pair this likelihood with a positive intercept prior (for example Prior("HalfNormal", sigma=2) or a Normal centered well above zero) so the prior puts mu on the positive side.

This parameterization also changes the noise model. Holding std constant across dates makes sigma_log shrink as mu grows, so the likelihood models (approximately) constant additive noise of size std in the units of the target. A plain LogNormal likelihood with fixed sigma instead models constant relative noise, with a standard deviation proportional to the mean. Note the likelihood scale variable is named y_std, not y_sigma as with the default Normal likelihood.

from pymc_extras.prior import Prior
from pymc_marketing.special_priors import LogNormalPrior

likelihood = LogNormalPrior(
    std=Prior("HalfNormal", sigma=0.5, dims=("country",)),
    dims=("date", "country"),
)
mmm = MMM(..., model_config={"likelihood": likelihood})

Methods

LogNormalPrior.__init__([dims, centered])

LogNormalPrior.create_likelihood_variable(...)

Create an observed LogNormal variable whose response-scale mean is mu.

LogNormalPrior.create_variable(name[, xdist])

Create a variable from the prior distribution.

LogNormalPrior.from_dict(data)

Create a SpecialPrior prior from a dictionary.

LogNormalPrior.sample_prior([coords, name])

Sample from the prior distribution.

LogNormalPrior.to_dict([_orig])

Convert the SpecialPrior to a dictionary.

LogNormalPrior.validate_observed(y)

Raise if observed data lies outside the LogNormal support.