bayesian-changepoint: changepoint detection that returns probabilities
Methods to get the probability of a changepoint in a time series.
At a glance
- What is it?
- This library finds the points where a time series changes regime, online and offline, and returns calibrated posterior probabilities rather than a thresholded flag. It is a PyTorch rewrite with conjugate Normal-Gamma and Normal-Wishart likelihoods, one runtime dependency and a documented rename history that will bite anyone with the old package installed.
- Who is it for?
- Adopt bayesian-changepoint if you need change points with an uncertainty attached, if your data already lives in PyTorch tensors, or if you want the offline exact posterior rather than a heuristic split. Do not adopt it for a production service that cannot take a PyTorch dependency, and do not expect a drop-in replacement for a classical detector with a different API, because 1.1.0 changed the online interface and the distribution was renamed twice.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 29 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Probabilities where most detectors give you a flag
The project describes itself as methods to get the probability of a changepoint in a time series, and the README's framing is the interesting part: find the points where a time series changes regime, with calibrated posterior probabilities instead of a threshold.
Most change point detection code answers a yes-or-no question, and the threshold that converts a score into a yes is a tuning knob with no statistical meaning. Here the output is a probability, so the decision about what counts as a change moves to where it belongs, into the code that consumes the result.
Two detectors are provided, and they answer different questions. The online detector gives you the run-length posterior after every observation, following Adams and MacKay from 2007, which is what you want for streams and for measuring how quickly a change would have been noticed. The offline detector gives the exact posterior probability of a changepoint at every position given the whole series, following Fearnhead from 2006.
On top of those sit the calibrated outputs: probabilities you can threshold, MAP segment starts, and the single most probable segmentation returned by viterbi_changepoints.
The audience is statisticians and engineers who need to know not just where a series changed but how confident that claim is. The implementation is Python on PyTorch, the licence is MIT, and the package is published on PyPI as bayesian-changepoint with the import name bayesian_changepoint_detection. The repository is not archived, the last push was on 2026-09-24, and the listed releases are v1.2.1, v1.2.0 and v1.1.0, all in late September 2026.
Reading the run-length matrix correctly
The online detector processes the series one point at a time and keeps the posterior over the run length, the number of observations since the last change. Two helpers turn that posterior into change points, and understanding why the naive approach fails is the key to using the library correctly.
The README asks why not simply threshold R[0, :]. The answer is that under a constant hazard the posterior probability of run length 0 is the hazard rate at every step, whatever the data say. The evidence for a change at t does not appear in column t, it appears in the following columns, as mass at run length k in column t + k. changepoint_probabilities reads exactly that.
So R[r, t] is P(run length = r given the first t observations), and a change is detected by looking forward, not backward. The helper takes a lag argument, so a probability is judged a fixed number of observations after the event, and the returned array is indexed so that probs[t] refers to data index t. That is a deliberate delay built into the API, because judging a change the instant it happens would be cheating.
The other helper is get_map_changepoints, which returns the indices of the first point of each new segment on the MAP run-length path. Its min_separation argument merges starts that are closer together than that many points when the posterior hesitates between neighbours, which is the practical way to stop a noisy posterior from producing a dozen change points in a second.
viterbi_changepoints goes further and returns the single most probable run-length path, that is, the MAP segmentation of the whole series.
The example in the README makes the output concrete. With three segments of fifty points each, at means of zero, three and zero, and a prior of one change every 250 points, both the MAP helper and the thresholded probabilities return the indices 50 and 100.
The likelihoods and why they are conjugate
The model choices are what make the whole thing tractable, and the README lists them in detail.
For univariate data with unknown mean and variance, the likelihood is Student-t, which is the predictive distribution of a Normal-Gamma conjugate prior. For vector data with unknown mean and covariance, the multivariate-t comes from a Normal-Wishart prior, and there are independent-features and covariance-only variants for the cases where you want to constrain what is shared across dimensions. For counts there is a Gamma-Poisson option, and for mean changes at a known noise level, a Normal with known variance.
Conjugacy is not decoration here. It is what allows the posterior to be updated in closed form as each observation arrives, which is why the online detector can keep a full run-length distribution without any sampling or optimisation loop. The same property is what lets the test suite check the implementation against SciPy and against exhaustive enumeration of segmentations, as the README puts it, with every pinned number in the test suite saying where it comes from.
That verification claim is the one to weigh carefully in an evaluation. A changepoint library is easy to write and easy to get subtly wrong, because a plausible-looking posterior that is slightly misnormalised still produces change points in roughly the right places. Checking closed forms against SciPy and against brute-force enumeration over segmentations is the kind of check that catches that, and the project says it does it.
The offline detector uses the same likelihoods, so the two detectors are comparable in a way that separate implementations of online and offline methods usually are not.
Streaming with a bounded run-length posterior
The online detector in its function form needs the whole series, because it returns the (T+1) squared matrix R. That is fine for a file of data and useless for a socket, so the library provides a second shape for streams.
OnlineChangepointDetector takes observations one at a time and keeps only the current run-length posterior. The example shows it over any iterable, a socket, a file or a generator, with detector.update(x) per observation, and then a check on detector.changepoint_probability(lag=10) to catch a segment start ten observations back.
from bayesian_changepoint_detection import OnlineChangepointDetector
detector = OnlineChangepointDetector(
hazard, StudentT(alpha=0.1, beta=0.01, kappa=1, mu=0), max_run_length=500
)
starts = []
for x in data: # any iterable: a socket, a file, a generator
detector.update(x)
# P(a segment started 10 observations ago), as changepoint_probabilities
if detector.t > 10 and detector.changepoint_probability(lag=10) > 0.5:
starts.append(detector.t - 10)The max_run_length argument is the interesting part, and the README is precise about what it costs. Run lengths above the bound are dropped and the posterior is renormalized. That is exact until the bound is reached, and a close approximation afterwards when segments are shorter than the bound. So the bound is a memory and time control, and the approximation only bites in the case where a segment is shorter than the limit you set, which is the opposite of the case you would worry about.
One detail in the example is easy to miss: detector.t is the observation counter, and the lag arithmetic is done by the caller, subtracting 10 from the current index to recover the position of the suspected start. The library gives you the probability at a lag and leaves the bookkeeping to you.
Installing, and the two packages you have to remove
Installation is a single command:
pip install bayesian-changepointor with uv:
uv add bayesian-changepointThe import name is bayesian_changepoint_detection, whatever the distribution is called, and the examples and notebooks need the plot extra, installed as pip install "bayesian-changepoint[plot]" or ".[plot]" from a clone.
The system requirements are Python 3.9 or higher and PyTorch 2.0 or higher, installed automatically. For a CUDA build of PyTorch you install it first following the PyTorch site, and the README notes the CPU build is enough for everything it demonstrates.
Now the part that will catch people out. The distribution was renamed. bayesian-changepoint is the name from 1.1.0 on, the same project was published before as bayescd at version 0.4 in April 2022, and earlier as bayesian-changepoint-detection at 0.2.dev1. Both old names are frozen at those releases and neither gets updates, and the project tells you what to do if you have one installed:
pip uninstall bayescd bayesian-changepoint-detection
pip install bayesian-changepointThe code is the same project in the same repository, only the name on PyPI changed. And 1.1.0 is a rewrite on PyTorch relative to 0.4, which changes the online API relative to the 1.0.x line, with the CHANGELOG listing every breaking change.
So there are three separate transitions to absorb: a distribution rename, a package name that changed twice, and a backend rewrite that moved the implementation from NumPy to PyTorch. Anyone with existing code will spend their first hour in the CHANGELOG.
Performance, devices and the limits of the approach
The performance claims in the README are specific and worth reading in the original framing. Both detectors are O(T squared) with the inner work on tensors rather than Python loops, and 1000 points offline in under 4 seconds on a laptop CPU.
That number is the author's, from the README, and the shape of the algorithm explains it. The quadratic factor is inherent to a full run-length posterior, and the constant factor is what the vectorisation attacks. If your series is long, the quadratic term wins and no amount of tensor work saves you, so the honest summary is that this library suits series of hundreds to low thousands of points and becomes expensive well before it becomes wrong.
The device story is that it runs on CPU, CUDA or Apple MPS through one device argument, and the README says it includes measured guidance on when an accelerator is not worth it. That guidance is worth reading before you move a workload onto a GPU, because for a workload with this much overhead per step, transfer costs can dominate.
Dependency-wise the library is disciplined: one dependency, torch. NumPy, SciPy and Matplotlib are needed only for the tests and examples, and the dev extra in the project metadata adds pytest, pytest-cov, numpy, scipy, mpmath, ruff and pre-commit tooling. mpmath is there as a high-precision reference in the test suite for exact marginals, which is a detail that tells you the verification is taken seriously.
The real limits are statistical rather than computational. A constant hazard prior assumes change points arrive at a fixed rate, and if your process has bursts of change, a different hazard model is needed. The likelihoods assume the segments are contiguous runs in the data rather than recurring patterns, so a seasonal series with the same shape every period is a poor fit for a run-length model. And a probability is only as good as the prior you choose, which is why the library exposes the hazard as a parameter rather than fixing it.
Editorial conclusion
Adopt bayesian-changepoint if you need change points with an uncertainty attached, if your data already lives in PyTorch tensors, or if you want the offline exact posterior rather than a heuristic split. Do not adopt it for a production service that cannot take a PyTorch dependency, and do not expect a drop-in replacement for a classical detector with a different API, because 1.1.0 changed the online interface and the distribution was renamed twice. Before you start, uninstall bayescd and bayesian-changepoint-detection if either is present, install bayesian-changepoint, and check the CHANGELOG for the breaking changes between the version you used and the one you are installing.
Frequently asked questions
How do I install the bayesian changepoint detection library?
Run pip install bayesian-changepoint, or uv add bayesian-changepoint. The import name is bayesian_changepoint_detection regardless of the distribution name, and you need Python 3.9 or newer with PyTorch 2.0 or higher, which is installed automatically.
Why do I get an error after installing bayesian-changepoint?
The project was published before as bayescd at 0.4 and earlier as bayesian-changepoint-detection at 0.2.dev1, and both are frozen. Uninstall them with pip uninstall bayescd bayesian-changepoint-detection and install bayesian-changepoint, then check the CHANGELOG because 1.1.0 changed the online API.
What does R[r, t] mean in the online changepoint detector?
It is P(run length = r given the first t observations). The README explains that under a constant hazard the probability of run length 0 is the hazard rate regardless of the data, so the evidence for a change at t shows up in later columns, as mass at run length k in column t + k, which is what changepoint_probabilities reads.
How do I detect changepoints in a stream with bounded memory?
Use OnlineChangepointDetector, which keeps only the current run-length posterior and accepts observations one at a time. The max_run_length argument bounds memory and time per observation, dropping longer run lengths and renormalizing, which is exact until the bound is reached and a close approximation afterwards.
Which likelihoods does bayesian-changepoint support and what licence does it use?
Student-t for univariate data with unknown mean and variance, multivariate-t for vector data with unknown mean and covariance, plus independent-features and covariance-only variants, Gamma-Poisson for counts, and Normal with known variance. The licence is MIT, the current version is 1.2.1, and the last push was on 2026-09-24.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/hildensia-bayesian-changepoint-detection)Was this useful?