# `BaseDataDriftDetector`

### *class* capymoa.drift.detectors.BaseDataDriftDetector[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/drift/detectors/data_drift/base.py#L100)

Bases: [`BaseDriftDetector`](capymoa.drift.base_detector.BaseDriftDetector.md#capymoa.drift.base_detector.BaseDriftDetector)

Base class for detectors that monitor the input data distribution.

These detectors have [`REQUIRES_FIT`](#capymoa.drift.detectors.BaseDataDriftDetector.REQUIRES_FIT) set to `True`: they need
a reference distribution. Provide it with [`fit()`](#capymoa.drift.detectors.BaseDataDriftDetector.fit), or construct
with `auto_fit_samples` so [`add_element()`](#capymoa.drift.detectors.BaseDataDriftDetector.add_element) collects it.

Subclasses set the class variable [`IS_UNIVARIATE`](#capymoa.drift.detectors.BaseDataDriftDetector.IS_UNIVARIATE) and implement:

* [`_fit()`](#capymoa.drift.detectors.BaseDataDriftDetector._fit) – store or preprocess the reference data.
* [`_test()`](#capymoa.drift.detectors.BaseDataDriftDetector._test) – run the test (on one feature when univariate, on
  all features when multivariate).
* [`get_params()`](#capymoa.drift.detectors.BaseDataDriftDetector.get_params) – return detector hyper-parameters.

The base class handles the sliding test window, feature-wise looping for
univariate tests, and detection bookkeeping. For univariate tests that
produce p-values, it also applies Bonferroni correction
<sup>[1](#rabanser2019)</sup> across features. Univariate tests that compare a
distance or score to a fixed threshold instead (no p-value) supply
their own per-feature decisions directly; `correction` has no effect
on those.

By default the reference window is fixed after [`fit()`](#capymoa.drift.detectors.BaseDataDriftDetector.fit) (or after
auto-fit) <sup>[2](#cerqueira2023)</sup> <sup>[3](#lukats2025)</sup>. Call [`fit()`](#capymoa.drift.detectors.BaseDataDriftDetector.fit) again
along the stream to use a sliding reference.

* <a id='rabanser2019'>**[1]**</a> Rabanser, S., Günnemann, S., and Lipton, Z. (2019). Failing loudly: An empirical study of methods for detecting dataset shift. Advances in Neural Information Processing Systems, 32.
* <a id='cerqueira2023'>**[2]**</a> Cerqueira, V., Gomes, H. M., Bifet, A., and Torgo, L. (2023). STUDD: A student-teacher method for unsupervised concept drift detection. Machine Learning, 112(11), 4351-4378.
* <a id='lukats2025'>**[3]**</a> Lukats, D., Zielinski, O., Hahn, A., and Stahl, F. (2025). A benchmark and survey of fully unsupervised concept drift detectors on real-world data streams. International Journal of Data Science and Analytics, 19(1), 1-31.

#### \_\_init_\_(window_size: [int](https://docs.python.org/3/builtins/functions.html#int), alpha: [float](https://docs.python.org/3/builtins/functions.html#float) = 0.05, correction: [Literal](https://docs.python.org/3/library/typing.html#typing.Literal)['bonferroni', 'none'] = 'bonferroni', auto_fit_samples: [int](https://docs.python.org/3/builtins/functions.html#int) | [None](https://docs.python.org/3/builtins/constants.html#None) = None)[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/drift/detectors/data_drift/base.py#L148)

Create a data drift detector.

* **Parameters:**
  * **window_size** – Number of observations to collect before
    running a comparison against the reference. The detector
    returns no result (and `detected_change` is `False`)
    until the window is full.
  * **alpha** – Significance level. For p-value tests, drift is
    declared when the (corrected) p-value falls below `alpha`.
  * **correction** – Multiple-testing correction for univariate
    tests that produce p-values, across features. `"bonferroni"`
    (default) divides `alpha` by the number of features;
    `"none"` uses `alpha` directly. Ignored for multivariate
    tests, and for univariate tests that compare a distance or
    score to a fixed `threshold` instead of a p-value (those
    supply their own per-feature decisions, uncorrected).
  * **auto_fit_samples** – If set, the first *auto_fit_samples*
    observations are used as the reference (auto-fit mode).
    No explicit [`fit()`](#capymoa.drift.detectors.BaseDataDriftDetector.fit) call is needed; [`add_element()`](#capymoa.drift.detectors.BaseDataDriftDetector.add_element)
    collects the reference. If `None` (default), [`fit()`](#capymoa.drift.detectors.BaseDataDriftDetector.fit)
    must be called before [`add_element()`](#capymoa.drift.detectors.BaseDataDriftDetector.add_element) or [`compare()`](#capymoa.drift.detectors.BaseDataDriftDetector.compare).
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If *window_size* is not a positive integer,
  *alpha* is not in `(0, 1]`, *correction* is unknown, or
  *auto_fit_samples* is not a positive integer when set.

#### *abstract* \_fit(X: [ndarray](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)) → [None](https://docs.python.org/3/builtins/constants.html#None)[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/drift/detectors/data_drift/base.py#L453)

Store or preprocess the reference data.

Called by [`fit()`](#capymoa.drift.detectors.BaseDataDriftDetector.fit) after validation. *X* is guaranteed to be a
2-D `np.ndarray` with shape `(n_samples, n_features)`.

At minimum, implementations should set `self._X_ref = X`.

#### *abstract* \_test(X_ref: [ndarray](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray), X_test: [ndarray](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)) → [DataDriftResult](capymoa.drift.detectors.DataDriftResult.md#capymoa.drift.detectors.DataDriftResult)[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/drift/detectors/data_drift/base.py#L465)

Run the underlying statistical test or distance measure.

When `IS_UNIVARIATE = True` this receives **one feature** at a
time: both arrays are 1-D with shapes `(n_ref,)` and
`(n_test,)`. The subclass should set `statistic` and
`p_value` (if available) on the returned result.

If `p_value` is set, the base class applies the configured
`correction` and ignores the per-feature `is_drift` flag.
Example:

```default
def _test(self, x_ref, x_test):
    stat, p = scipy.stats.ks_2samp(x_ref, x_test)
    return DataDriftResult(
        is_drift=False, statistic=stat, p_value=p
    )
```

If `p_value` is left `None` (distance- or score-based tests),
the base class uses the per-feature `is_drift` flag directly and
`correction` has no effect – the subclass is responsible for its
own per-feature decision (e.g. comparing a distance to a
threshold).

When `IS_UNIVARIATE = False` this receives **all features**:
both arrays are 2-D with shapes `(n_ref, n_features)` and
`(n_test, n_features)`. The subclass is responsible for
the full `DataDriftResult` including `is_drift`.

#### add_element(element: [float](https://docs.python.org/3/builtins/functions.html#float) | [ndarray](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)) → [None](https://docs.python.org/3/builtins/constants.html#None)[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/drift/detectors/data_drift/base.py#L300)

Add one observation and check for drift.

The observation is appended to a sliding window of size
[`window_size`](#capymoa.drift.detectors.BaseDataDriftDetector.window_size). Once full, the detector compares the window
against the reference on every call.

If the detector is not yet fitted and [`auto_fit_samples`](#capymoa.drift.detectors.BaseDataDriftDetector.auto_fit_samples)
is set, those initial observations build the reference.
No comparison happens until then. If it is not fitted and
auto-fit is not enabled, this raises [`RuntimeError`](https://docs.python.org/3/builtins/exceptions.html#RuntimeError).

* **Parameters:**
  **element** – A single observation – a scalar for univariate
  data, or a 1-D array for multivariate data.
* **Raises:**
  * [**RuntimeError**](https://docs.python.org/3/builtins/exceptions.html#RuntimeError) – If the detector is not fitted and
    auto-fit mode is not enabled. Call [`fit()`](#capymoa.drift.detectors.BaseDataDriftDetector.fit) first, or
    construct with `auto_fit_samples`.
  * [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If the observation has a different number of
    features than the reference.

#### compare(X_test: [ndarray](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)) → [DataDriftResult](capymoa.drift.detectors.DataDriftResult.md#capymoa.drift.detectors.DataDriftResult)[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/drift/detectors/data_drift/base.py#L354)

One-shot batch comparison against the reference.

Unlike [`add_element()`](#capymoa.drift.detectors.BaseDataDriftDetector.add_element), this does not update the internal
window or detection history. It is useful for offline evaluation.
`auto_fit_samples` does not apply here; call [`fit()`](#capymoa.drift.detectors.BaseDataDriftDetector.fit) first.

* **Parameters:**
  **X_test** – Test data with the same number of features as the
  reference.
* **Raises:**
  * [**RuntimeError**](https://docs.python.org/3/builtins/exceptions.html#RuntimeError) – If [`fit()`](#capymoa.drift.detectors.BaseDataDriftDetector.fit) has not been called.
  * [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If *X_test* has a different number of features
    than the reference.
* **Returns:**
  Comparison result.

#### detected_change() → [bool](https://docs.python.org/3/builtins/functions.html#bool)[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/drift/base_detector.py#L52)

Is the detector currently detecting a concept drift?

#### detected_warning() → [bool](https://docs.python.org/3/builtins/functions.html#bool)[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/drift/base_detector.py#L56)

Is the detector currently warning of an upcoming concept drift?

#### fit(X: [ndarray](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) | [Any](https://docs.python.org/3/library/typing.html#typing.Any), feature_names: [Sequence](https://docs.python.org/3/library/collections.abc.html#collections.abc.Sequence)[[str](https://docs.python.org/3/builtins/stdtypes.html#str)] | [None](https://docs.python.org/3/builtins/constants.html#None) = None) → [None](https://docs.python.org/3/builtins/constants.html#None)[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/drift/detectors/data_drift/base.py#L254)

Set the reference distribution.

This detector has [`REQUIRES_FIT`](#capymoa.drift.detectors.BaseDataDriftDetector.REQUIRES_FIT) set to `True`. Call
[`fit()`](#capymoa.drift.detectors.BaseDataDriftDetector.fit) before [`add_element()`](#capymoa.drift.detectors.BaseDataDriftDetector.add_element) or [`compare()`](#capymoa.drift.detectors.BaseDataDriftDetector.compare), unless
`auto_fit_samples` was set so [`add_element()`](#capymoa.drift.detectors.BaseDataDriftDetector.add_element) can collect
the reference. Calling [`fit()`](#capymoa.drift.detectors.BaseDataDriftDetector.fit) again replaces the reference
(a sliding reference window).

* **Parameters:**
  * **X** – Reference data, shape `(n_samples,)` or
    `(n_samples, n_features)`. May also be a pandas DataFrame,
    in which case column names are extracted automatically.
  * **feature_names** – Optional names for each feature. If
    provided, must have length equal to the number of features.
    Overrides column names extracted from a DataFrame.
    When set, per-feature dicts in [`DataDriftResult`](capymoa.drift.detectors.DataDriftResult.md#capymoa.drift.detectors.DataDriftResult) use
    these names as keys instead of integer indices.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If *X* is empty or *feature_names* length
  does not match the number of features.

#### *classmethod* from_params(schema: [Any](https://docs.python.org/3/library/typing.html#typing.Any) = None, params: [dict](https://docs.python.org/3/builtins/stdtypes.html#dict)[[str](https://docs.python.org/3/builtins/stdtypes.html#str), [Any](https://docs.python.org/3/library/typing.html#typing.Any)] | [None](https://docs.python.org/3/builtins/constants.html#None) = None, random_seed: [int](https://docs.python.org/3/builtins/functions.html#int) = 1) → [Any](https://docs.python.org/3/library/typing.html#typing.Any)[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/base/_learner_params.py#L170)

Construct an instance from parameters produced by `get_params`.

#### *abstract* get_params() → [dict](https://docs.python.org/3/builtins/stdtypes.html#dict)[[str](https://docs.python.org/3/builtins/stdtypes.html#str), [Any](https://docs.python.org/3/library/typing.html#typing.Any)][[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/drift/detectors/data_drift/base.py#L498)

Return the hyper-parameters of this detector.

#### reset(clean_history: [bool](https://docs.python.org/3/builtins/functions.html#bool) = False) → [None](https://docs.python.org/3/builtins/constants.html#None)[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/drift/detectors/data_drift/base.py#L382)

Reset the detector state.

* **Parameters:**
  **clean_history** – If `True`, also clear the reference data
  and detection history. If `False` (default), only the
  sliding window and current result are cleared; the reference
  and detection indices are preserved.

#### IS_UNIVARIATE *: [bool](https://docs.python.org/3/builtins/functions.html#bool)* *= True*

If `True` the test is applied to each feature separately and
results are combined. If `False` the test runs on the joint
distribution.

#### REQUIRES_FIT *: [bool](https://docs.python.org/3/builtins/functions.html#bool)* *= True*

Data drift detectors need a reference distribution. Call
[`fit()`](#capymoa.drift.detectors.BaseDataDriftDetector.fit) or set `auto_fit_samples` so [`add_element()`](#capymoa.drift.detectors.BaseDataDriftDetector.add_element)
can collect it.

#### *property* X_ref *: [ndarray](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) | [None](https://docs.python.org/3/builtins/constants.html#None)*

The reference data set with `fit`.

#### *property* alpha *: [float](https://docs.python.org/3/builtins/functions.html#float)*

Significance level for drift decisions.

#### *property* auto_fit_samples *: [int](https://docs.python.org/3/builtins/functions.html#int) | [None](https://docs.python.org/3/builtins/constants.html#None)*

Number of samples for auto-fit, or `None` if explicit fit.

#### *property* correction *: [str](https://docs.python.org/3/builtins/stdtypes.html#str)*

Multiple-testing correction across features.

#### *property* feature_names *: [list](https://docs.python.org/3/builtins/stdtypes.html#list)[[str](https://docs.python.org/3/builtins/stdtypes.html#str)] | [None](https://docs.python.org/3/builtins/constants.html#None)*

Feature names passed to [`fit()`](#capymoa.drift.detectors.BaseDataDriftDetector.fit), or `None`.

#### *property* is_fitted *: [bool](https://docs.python.org/3/builtins/functions.html#bool)*

Whether the reference distribution has been set.

`True` after [`fit()`](#capymoa.drift.detectors.BaseDataDriftDetector.fit), or after [`add_element()`](#capymoa.drift.detectors.BaseDataDriftDetector.add_element) has
collected [`auto_fit_samples`](#capymoa.drift.detectors.BaseDataDriftDetector.auto_fit_samples) observations.

#### *property* n_features *: [int](https://docs.python.org/3/builtins/functions.html#int) | [None](https://docs.python.org/3/builtins/constants.html#None)*

Number of features in the reference data, or `None` before fit.

#### *property* result *: [DataDriftResult](capymoa.drift.detectors.DataDriftResult.md#capymoa.drift.detectors.DataDriftResult) | [None](https://docs.python.org/3/builtins/constants.html#None)*

Most recent comparison result, or `None` during warm-up.

#### *property* window_size *: [int](https://docs.python.org/3/builtins/functions.html#int)*

Size of the sliding test window.
