Your First Detection

This tutorial mirrors the examples/01_quickstart.ipynb notebook and shows the minimum set of steps required to detect changepoints with Fast-BOCPD.

1. Create a toy dataset

import numpy as np
np.random.seed(7)

regime_lengths = [120, 150, 90]
regime_means = [0.0, 1.5, -0.2]

data = np.concatenate([
    np.random.normal(loc=mu, scale=0.2, size=n)
    for n, mu in zip(regime_lengths, regime_means)
])

2. Instantiate a model and hazard

Fast-BOCPD provides conjugate observation models out of the box. For continuous, roughly Gaussian data we use fast_bocpd.models.GaussianNIG. The hazard controls expected segment length.

from fast_bocpd import BOCPD, GaussianNIG, ConstantHazard

obs_model = GaussianNIG(mu0=0.0, kappa0=1.0, alpha0=0.2, beta0=0.2)
hazard = ConstantHazard(lambda_=150)  # Expect ≈150 samples per regime
bocpd = BOCPD(obs_model=obs_model, hazard=hazard, max_run_length=600)

3. Stream observations

BOCPD.update(x) returns the posterior run-length distribution and the probability that the current point is a changepoint.

cp_probs = []
map_run_lengths = []

for x in data:
    posterior_r, cp_prob = bocpd.update(float(x))
    cp_probs.append(cp_prob)
    map_run_lengths.append(bocpd.get_map_run_length())

4. Simple alerting

Raise an alert whenever cp_prob crosses a threshold from below or the MAP run length resets to zero.

alerts = []
THRESHOLD = 0.3

for i, (cp_prob, map_r) in enumerate(zip(cp_probs, map_run_lengths)):
    if cp_prob >= THRESHOLD and map_r <= 2:
        alerts.append(i)

print(f\"Detected {len(alerts)} changepoints: {alerts}\")

5. Visualize

Plot the time series, P(r_t=0) and MAP run length to sanity check the detector. See examples/_helpers.py for Plotly helpers or use Matplotlib:

import matplotlib.pyplot as plt

fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True, figsize=(12, 6))
ax1.plot(data, color=\"#4E79A7\", linewidth=1.5)
for idx in alerts:
    ax1.axvline(idx, color=\"red\", linestyle=\"--\", alpha=0.6)
ax1.set_ylabel(\"Observation\")

ax2.plot(cp_probs, color=\"#F28E2B\", linewidth=1.5, label=\"P(CP)\")
ax2_twin = ax2.twinx()
ax2_twin.plot(map_run_lengths, color=\"#59A14F\", linewidth=1.2, label=\"MAP run length\")
ax2.set_xlabel(\"Time step\")
ax2.set_ylabel(\"P(CP)\")
ax2_twin.set_ylabel(\"MAP run length\")
plt.tight_layout()

Next steps