MovingWindow#

class MovingWindow(change_score: BaseIntervalScorer | None = None, penalty: ArrayLike | float | None = None, penalty_scale: float = 1.0, agg: str = 'sum', bandwidth: ArrayLike | int | None = None, min_bandwidth: int = 5, selection_method: str = 'local_optimum', min_detection_fraction: float = 0.2, local_optimum_fraction: float = 0.8)[source][source]#

Moving window algorithm for multiple change-point detection.

The MOSUM (moving sum) algorithm [1], but generalized to allow for any penalised change score. The basic algorithm runs a test statistic for a single change-point across the data in a moving window fashion. In each window, the data is split into two equal halves with bandwidth samples on either side of a candidate change-point. This process generates a time series of penalised scores, which are used to generate candidate change-points as local maxima within intervals where the penalised scores are all above zero. The final set of change-points is selected from the candidate change-points using one of the selection methods described in [2].

Several of the extensions available in the mosum R package [2] are also available in this implementation, including the ability to use multiple bandwidths. The CUSUM-type boundary extension for computing the test statistic for candidate change- points less than bandwidth samples from the start and end of the data is also implemented by default.

Parameters:
change_scoreBaseIntervalScorer or None, default=None

Change score to use in the algorithm. Must be an instance of BaseIntervalScorer with score_type="change_score". If None, defaults to CUSUM().

Standard usage is to pass an unpenalised score and set penalty separately. If the score already has tag penalised=True, it owns its own aggregation and penalty, and penalty / penalty_scale / agg are ignored.

penaltyfloat, array-like of shape (n_features,) or None, default=None

Penalty subtracted from the aggregated feature-wise score. A candidate changepoint is accepted only when the penalised score is positive.

  • float: scalar penalty subtracted from the aggregated score (see agg).

  • array-like of length n_features, non-decreasing: element i is the penalty for i+1 features jointly affected; the detector picks the k largest feature scores maximising sum(top_k) - penalty[k-1] (handles sparse changes). A strictly linear array uses a faster code path. Only consistent with the default agg="sum".

  • None: defaults to change_score.get_default_penalty() (BIC-based) at fit time.

Ignored when change_score is already penalised.

penalty_scalefloat, default=1.0

Multiplicative factor applied to the effective penalty (whether penalty is user-provided or the default). Use as a single scalar tuning knob that preserves the shape of array penalties. Must be positive. Ignored when change_score is already penalised.

agg{“sum”, “max”}, default=”sum”

How feature-wise raw scores are aggregated into a single score per interval before subtracting a scalar penalty:

  • "sum": sum(scores, axis=1) - penalty (dense change assumption).

  • "max": max(scores, axis=1) - penalty (a single, unknown feature changes).

Ignored when change_score is already aggregated. Must be "sum" when penalty is array-valued (array penalties imply top-k aggregation).

bandwidthint, list of int, or None, default=None

The number of samples on either side of a candidate change-point. Must be 1 or greater. If None, bandwidths are set automatically at fit time:

  • "local_optimum" (default): up to 5 exponentially spaced bandwidths between max(scorer.min_size, min_bandwidth) and max(scorer.min_size, min_bandwidth, n_samples // 5).

  • "detection_length": a single bandwidth of max(scorer.min_size, min_bandwidth, min(50, n_samples // 5)).

User-supplied values must all be >= scorer.min_size (checked at fit). Multiple bandwidths use the bottom-up merging approach of [2].

min_bandwidthint, default=5

Lower bound on the automatically chosen bandwidth(s) when bandwidth=None. Ignored when bandwidth is provided. The effective minimum bandwidth used is max(min_bandwidth, scorer.min_size), so the actual minimum may be larger than the value provided here when the change score requires more samples per segment. The default of 5 mitigates spurious detections from very small bandwidths.

selection_methodstr, default=”local_optimum”

The method used to select the final set of change-points from a set of candidate change-points. The options are:

  • "detection_length": Accepts a candidate change-point if the min_detection_fraction * bandwidth consecutive penalised scores are above zero. Corresponds to the epsilon-criterion in [2]. This method is only available for a single bandwidth.

  • "local_optimum": Accepts a candidate change-point if it is the local maximum in the scores within a neighbourhood of size local_optimum_fraction * bandwidth. Corresponds to the eta-criterion in [2]. This method is used within the “bottom-up” merging approach if multiple bandwidths are given.

min_detection_fractionfloat, default=0.2

Minimum fraction of the bandwidth that must have consecutive positive penalised scores for a candidate to be accepted. Only used with selection_method="detection_length". Must be in (0, 0.5).

local_optimum_fractionfloat, default=0.8

Neighbourhood size (as a fraction of bandwidth) for the local-optimum criterion. A candidate is accepted if it is the maximum within local_optimum_fraction * bandwidth samples of itself. Only used with selection_method="local_optimum". Must be >= 0.

Attributes:
change_score_BaseIntervalScorer

Fitted change score. Always the unpenalised scorer (whether user-provided or resolved from None). When change_score is already penalised, it is that fitted penalised scorer.

penalty_float, np.ndarray or None

Effective penalty actually used at detection time: the resolved base penalty multiplied by penalty_scale. None when change_score is inherently penalised, in which case the scorer owns its own penalty.

bandwidth_np.ndarray

Sorted array of bandwidths actually used at detect time.

Methods

fit(X[, y])

Fit the detector to training data.

fit_predict(X[, y])

Fit to data, then predict per-sample segment labels.

get_metadata_routing()

Get metadata routing of this object.

get_params([deep])

Get parameters for this estimator.

predict(X)

Detect changepoints, returning per-sample segment labels.

predict_all(X)

Detect changepoints and return all outputs in a single pass.

predict_changepoints(X)

Detect changepoints in a time series.

predict_scores(X[, return_index])

Return the per-window moving-window scores evaluated on X.

set_params(**params)

Set the parameters of this estimator.

References

[1]

Eichinger, B., & Kirch, C. (2018). A MOSUM procedure for the estimation of multiple random change points.

[2] (1,2,3,4,5)

Meier, A., Kirch, C., & Cho, H. (2021). mosum: A package for moving sums in change-point analysis. Journal of Statistical Software, 97, 1-42.

Examples

>>> from skchange.new_api.detectors import MovingWindow
>>> from skchange.datasets import generate_alternating_data
>>> df = generate_alternating_data(n_segments=4, mean=10, segment_length=100, p=5)
>>> detector = MovingWindow()
>>> detector.fit_predict(df)
   ilocs
0    100
1    200
2    300
fit(X: ArrayLike, y: ArrayLike | None = None) Self[source][source]#

Fit the detector to training data.

Parameters:
XArrayLike of shape (n_samples, n_features)

Training time series data.

yArrayLike | None, default=None

Training labels. Exists for sklearn API compatibility (e.g. pipelines). This detector is unsupervised on single series, so it is ignored.

Returns:
self

Fitted detector instance.

predict_all(X: ArrayLike) dict[source][source]#

Detect changepoints and return all outputs in a single pass.

Parameters:
XArrayLike of shape (n_samples, n_features)

Time series to analyze for changepoints.

Returns:
resultdict with keys:
"changepoints"np.ndarray of shape (n_changepoints,)

Sorted integer indices of detected changepoints.

"scores"np.ndarray of shape (n_windows,)

Penalised score for each evaluated window across all bandwidths.

"starts"np.ndarray of shape (n_windows,)

Start index of each evaluated window.

"splits"np.ndarray of shape (n_windows,)

Candidate changepoint (split) index of each window.

"ends"np.ndarray of shape (n_windows,)

End index of each evaluated window.

"bws"np.ndarray of shape (n_windows,)

Bandwidth used for each evaluated window.

predict_changepoints(X: ArrayLike) ndarray[source][source]#

Detect changepoints in a time series.

Parameters:
XArrayLike of shape (n_samples, n_features)

Time series to analyze for changepoints.

Returns:
changepointsnp.ndarray of shape (n_changepoints,)

Sorted integer indices of detected changepoints. A changepoint at index t means sample t is the first sample of a new segment, i.e. a structural break occurs between samples t-1 and t. Empty array if no changepoints are detected.

predict_scores(X: ArrayLike, return_index: bool = False) ndarray | tuple[ndarray, dict[str, ndarray]][source][source]#

Return the per-window moving-window scores evaluated on X.

Returns the penalised score for every (start, split, end) window evaluated across all active bandwidths, as a flat 1-D array. This is the raw output that predict aggregates into a per-sample score before changepoint selection.

For penalty calibration, use the free function skchange.new_api.tuning.unpenalised_scores, which fits a clone of this detector with the penalty parameter set to zero and returns the resulting unpenalised scores.

Parameters:
XArrayLike of shape (n_samples, n_features)

Time series to evaluate.

return_indexbool, default=False

If True, also return a dict locating each score on the time axis. See the Returns section for the keys.

Returns:
scoresnp.ndarray of shape (n_windows,)

Penalised score for each evaluated window, one entry per (start, split, end) triple across all active bandwidths. Returned alone when return_index=False.

indexdict, optional

Only returned when return_index=True. Contains:

  • "starts" : np.ndarray of shape (n_windows,) Start indices of each evaluated window.

  • "splits" : np.ndarray of shape (n_windows,) Candidate changepoint (split) index of each window.

  • "ends" : np.ndarray of shape (n_windows,) End indices of each evaluated window.

fit_predict(X, y: ArrayLike | None = None, **fit_params) ndarray[source]#

Fit to data, then predict per-sample segment labels.

Equivalent to calling fit(X, y).predict(X).

Parameters:
XArrayLike of shape (n_samples, n_features)

Time series data.

yNone

Ignored. Exists for sklearn API compatibility.

**fit_paramsdict

Additional parameters passed to fit().

Returns:
labelsnp.ndarray of shape (n_samples,)

Dense integer segment labels, one per sample.

get_metadata_routing()[source]#

Get metadata routing of this object.

Please check User Guide on how the routing mechanism works.

Returns:
routingMetadataRequest

A MetadataRequest encapsulating routing information.

get_params(deep=True)[source]#

Get parameters for this estimator.

Parameters:
deepbool, default=True

If True, will return the parameters for this estimator and contained subobjects that are estimators.

Returns:
paramsdict

Parameter names mapped to their values.

predict(X: ArrayLike) ndarray[source]#

Detect changepoints, returning per-sample segment labels.

Default implementation calls predict_changepoints() and converts the changepoint indices to an array with segment labels per sample. Subclasses may override this directly when the algorithm natively produces labels.

Parameters:
XArrayLike of shape (n_samples, n_features)

Time series data.

Returns:
labelsnp.ndarray of shape (n_samples,)

Dense integer segment labels, one per sample. Segment 0 is the first segment, segment 1 the next, and so on.

Examples

>>> labels = detector.fit(X_train).predict(X_test)
>>> labels.shape
(n_samples,)
set_params(**params)[source]#

Set the parameters of this estimator.

The method works on simple estimators as well as on nested objects (such as Pipeline). The latter have parameters of the form <component>__<parameter> so that it’s possible to update each component of a nested object.

Parameters:
**paramsdict

Estimator parameters.

Returns:
selfestimator instance

Estimator instance.