Skip to content

Operational change-point detection

camber.changedetect finds when a signal's mean shifts in time — step changes — as opposed to the change-point models (energy vs temperature) in camber.mandv. It answers the monitoring-based commissioning questions without being told the date: did a control change take effect? did a fixed measure persist or silently regress? did equipment degrade?

Binary segmentation: find the most likely single change point, gate it on a two-sample statistic, then recurse on each half.

flowchart LR
    series["series"] --> detect["detect_level_shifts"]
    detect -- "maximize CUSUM" --> cp["candidate change point"]
    cp --> gate{"two-sample z gate"}
    gate -- "significant" --> recurse["recurse on each half"]
    recurse --> detect
    gate -- "not significant" --> done["shifts: before/after from adjacent segments"]
    done --> largest["largest_shift"]
from camber.changedetect import detect_level_shifts, largest_shift

shifts = detect_level_shifts(series, z=4.0, min_delta=5.0)
for s in shifts:
    print(s.at, s.before_mean, "->", s.after_mean, f"(Δ{s.delta:+.1f}, score {s.score})")
big = largest_shift(series)  # the single most significant shift, or None

Transparent binary segmentation: the most likely single change point maximizes the CUSUM of the mean-centered signal; a standardized two-sample statistic (z) gates significance; recurse on each half. Once all breakpoints are found, each shift's before/after levels are computed from the adjacent segments (an early shift isn't blurred by a later regime). Flags: min_segment, max_shifts, z, min_delta. Pairs with camber.rcx.before_after (which verifies a change across a known date — this finds the dates). numpy/pandas only.