--- title: "Distribution-Based Drift Detection" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Distribution-Based Drift Detection} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ```{r load} library(deriva) ``` ## Error-based vs. distribution-based `vignette("deriva")` covers `signal_type = "error"` methods (DDM and friends): they need a labelled 0/1 (or continuous) error signal, usually built from a model's predictions with `add_prediction_error()`. `signal_type = "distribution"` methods are different: they watch a raw numeric stream directly — no labels, no baseline error rate — and flag a change in the stream's distribution itself. This is the right family when you want to monitor an input feature or a sensor reading for drift, not just a model's error. ## The running example: a drifting sensor `sensor_monitoring`, shipped with the package, is a numeric sensor-reading stream: 500 stable observations centred at 0, then 500 after the sensor drifted out of calibration and the mean shifted to 2. ```{r data} head(sensor_monitoring) ``` ## Detecting the shift with KSWIN `"kswin"` (Kolmogorov-Smirnov Windowing) is a `signal_type = "distribution"` method: it repeatedly compares a recent window of the stream against an older one with a Kolmogorov-Smirnov test. KSWIN is stochastic — it draws a random sub-sample from its window at every test. Pass `seed` to `drift_detector()` (or, as here, straight into `detect_drift()`, which forwards it) to get a result that is reproducible and does not depend on the state of your session's random number generator. ```{r kswin} result <- detect_drift(sensor_monitoring, .col = value, method = "kswin", seed = 7) subset(result, .drift) ``` No detection fires in the 500 stable observations. Unlike DDM, which settles into a new stable state after one detection, KSWIN keeps comparing windows as they slide past the change point — so it fires **several times** while its window catches up to the new distribution, not just once. That is expected behaviour for a windowed method, not noise: every one of these detections comes after the true drift point, as the window repeatedly re-compares against the now-shifted data. ## Other distribution-based methods `"adwin"` (Adaptive Windowing) solves the same problem with an adaptively-sized window instead of a fixed one, and is deterministic — no `seed` needed: ```{r adwin} detect_drift(sensor_monitoring, .col = value, method = "adwin") |> subset(.drift) ``` On this stream ADWIN settles the way DDM did in `vignette("deriva")`: a single detection, no repeated firing while the window catches up. Which method fires once versus several times is a property of the algorithm, not of one being more "correct" than the other — see `drift_detector("adwin")` for its hyperparameters, and `vignette("deriva")` for the full tidy workflow (`fit()`/`advance()`/`augment()`/...), which works identically for distribution-based methods.