Model Comparison
Choosing an observation model amounts to picking the likelihood–prior pair that best matches your data. The following table summarises the trade-offs.
Model |
Data assumptions |
Robustness |
Computational cost |
Recommended use cases |
|---|---|---|---|---|
Gaussian-NIG |
Continuous, approximately Gaussian |
Moderate (sensitive to outliers) |
Fast (baseline) |
Sensor drift, financial spreads, streaming KPIs |
Student-t (fixed ν) |
Continuous with occasional outliers |
High (ν controls tail weight) |
~20% slower than Gaussian |
Industrial monitoring, heavy-tailed returns |
Student-t (ν grid) |
Continuous, unknown tail weight |
Very high (adapts ν online) |
6–7× slower (mixture update) |
Critical systems where robustness outweighs speed |
Poisson-Gamma |
Non-negative integer counts |
Moderate (handles overdispersion) |
Comparable to Student-t |
Event counts, telemetry hits, arrivals |
Bernoulli-Beta |
Binary indicators |
High (exact conjugacy) |
Fastest model |
Success/failure streams, anomaly flags |
Binomial-Beta |
Counts out of N trials per time step |
High |
Midrange (binomial coefficients) |
Conversion rates, aggregated proportions |
Gamma-Gamma |
Positive continuous values (rates/durations) |
Moderate (depends on fixed shape k) |
Slightly slower than Gaussian |
Transaction sizes, waiting times |
Hazard Interaction
The hazard controls how aggressively we expect changepoints. A low \(\lambda\) => frequent changes => shorter regimes. Observation models rely on sufficient statistics whose variance scales with run length. Consequently:
Heavy-tailed models (Student-t) tolerate higher \(\lambda\) (longer regimes) without false alarms.
Discrete models (Bernoulli/Binomial/Poisson) are less sensitive to the hazard choice because their statistics saturate quickly (counts).
Implementation Notes
Each Python class in
fast_bocpd.modelsmirrors the C implementation to keep parameter names consistent between theory and code.Hazard functions live in
fast_bocpd.hazard; currently only the constant hazard is exposed, but the recursion supports any \(P(r_t \mid r_{t-1})\).Switching models requires only substituting the Python wrapper; the C engine handles the rest via virtual dispatch.