Combining Distributions with Mixtures

Weighted blends of univariate distributions

Overview

A UvMixture represents a weighted blend of univariate distributions. This is useful when one process has distinct regimes. For example, most delivery times might follow a bounded PERT estimate, while a smaller share of outcomes come from a long-tailed delay process.

import numpy as np

import drisk as dr

Define component distributions

quick_delivery = dr.PERT.elicit(
    min=1,
    mode=1.1,
    max=8,
    name="Quick delivery",
)

typical_delivery = dr.PERT.elicit(
    min=6,
    mode=9,
    max=14,
    name="Typical delivery",
)

delayed_delivery = dr.LogNormal.elicit(
    lower=14,
    upper=21,
    confidence=0.8,
    name="Delayed delivery",
)

Create the mixture

delivery_mixture = dr.UvMixture.elicit(
    components=[quick_delivery, typical_delivery, delayed_delivery],
    weights=[1, 3, 1],
    name="Delivery time with delays",
)

delivery_mixture
UvMixture(dist_type='uv_mixture', name='Delivery time with delays', elicitation_params={'weights': (1, 3, 1)}, params={}, components=(PERT(dist_type='pert', name='Quick delivery', elicitation_params={'min': 1, 'mode': 1.1, 'max': 8, 'concentration': 4.0}, params={'min': 1.0, 'max': 8.0, 'alpha': 1.0571428571428572, 'beta': 4.942857142857143, 'mode': 1.1, 'concentration': 4.0}), PERT(dist_type='pert', name='Typical delivery', elicitation_params={'min': 6, 'mode': 9, 'max': 14, 'concentration': 4.0}, params={'min': 6.0, 'max': 14.0, 'alpha': 2.5, 'beta': 3.5, 'mode': 9.0, 'concentration': 4.0}), LogNormal(dist_type='lognormal', name='Delayed delivery', elicitation_params={'lower': 14, 'upper': 21, 'confidence': 0.8}, params={'mu': 2.8417898836693407, 'sigma': 0.15819305247224313})), weights=(0.2, 0.6, 0.2))

The weights are normalized during construction, so they can be provided as proportions or relative frequencies.

delivery_mixture.weights
(0.2, 0.6, 0.2)

Sample and plot the mixture

Mixtures support direct sampling plus PDF/CDF evaluation.

mixture_samples = delivery_mixture.sample(size=10_000, seed=123)
np.percentile(mixture_samples, [10, 50, 90, 99])
array([ 1.97953541,  9.16634637, 16.84419091, 22.14657182])
delivery_mixture.plot(color="seagreen")

Because a generic mixture usually has no simple inverse CDF, UvMixture.ppf(...) is not implemented. That also means mixtures are not currently usable as copula marginals, since the copula sampler maps correlated uniform values through each marginal’s ppf.