| Title: | Drift Adaptable Models |
|---|---|
| Description: | In streaming data analysis, it is crucial to detect significant shifts in the data distribution or the accuracy of predictive models over time, a phenomenon known as concept drift. The package aims to identify when concept drift occurs and provide methodologies for adapting models in non-stationary environments. It offers a range of state-of-the-art techniques for detecting concept drift and maintaining model performance. Additionally, the package provides tools for adapting models in response to these changes, ensuring continuous and accurate predictions in dynamic contexts. Methods for concept drift detection are described in Tavares (2022) <doi:10.1007/s12530-021-09415-z>. |
| Authors: | Lucas Tavares [aut], Leonardo Carvalho [aut], Rodrigo Machado [aut], Diego Carvalho [ctb], Esther Pacitti [ctb], Fabio Porto [ctb], Eduardo Ogasawara [aut, ths, cre] (ORCID: <https://orcid.org/0000-0002-0466-0626>), CEFET/RJ [cph] |
| Maintainer: | Eduardo Ogasawara <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 1.4.0 |
| Built: | 2026-10-16 12:44:25 UTC |
| Source: | https://github.com/cefet-rj-dal/heimdall |
ADWIN (Adaptive Windowing) is a sequential change detector that maintains a variable-length window and tests whether the means of two subwindows differ significantly. In this package, the implementation is primarily used for virtual concept drift when it monitors a numeric feature stream, although the same mechanism can also detect real concept drift if applied to an error or loss stream. The theoretical basis follows Bifet and Gavalda (2007) https://doi.org/10.1137/1.9781611972771.42.
dfr_adwin(target_feat = NULL, delta = 2e-05)dfr_adwin(target_feat = NULL, delta = 2e-05)
target_feat |
Feature to be monitored. |
delta |
The significance parameter for the ADWIN algorithm. |
The implementation is written in pure R and follows the
exponential-histogram version of ADWIN distributed with scikit-multiflow.
The window is compressed into buckets of exponentially growing size (at most
five buckets per size), and the cut points are checked every 32 observations,
once the window holds more than 10 observations. All the state lives in the
object itself, so a dfr_adwin object can be serialized with
base::saveRDS().
dfr_adwin object
Bifet, A., and Gavalda, R. (2007). Learning from time-changing data with adaptive windowing. In Proceedings of the 2007 SIAM International Conference on Data Mining, 443-448. https://doi.org/10.1137/1.9781611972771.42
library(daltoolbox) library(heimdall) data(st_drift_examples) data <- st_drift_examples$uv_virtual_drift data$event <- NULL model <- dfr_adwin(target_feat = "serie") detection <- NULL output <- list(obj = model, drift = FALSE) for (i in seq_along(data$serie)) { output <- update_state(output$obj, data$serie[i]) if (output$drift) { type <- "drift" output$obj <- reset_state(output$obj) } else { type <- "" } detection <- rbind(detection, data.frame(idx = i, event = output$drift, type = type)) } print(detection[detection$type == "drift", ])library(daltoolbox) library(heimdall) data(st_drift_examples) data <- st_drift_examples$uv_virtual_drift data$event <- NULL model <- dfr_adwin(target_feat = "serie") detection <- NULL output <- list(obj = model, drift = FALSE) for (i in seq_along(data$serie)) { output <- update_state(output$obj, data$serie[i]) if (output$drift) { type <- "drift" output$obj <- reset_state(output$obj) } else { type <- "" } detection <- rbind(detection, data.frame(idx = i, event = output$drift, type = type)) } print(detection[detection$type == "drift", ])
AEDD is an unsupervised multivariate detector that compares reconstruction errors produced by an autoencoder on reference and recent windows. Because it monitors changes in the input distribution rather than classifier performance, this implementation is primarily aimed at virtual concept drift. The method follows Kaminskyi, Li, and Muller (2022) https://doi.org/10.1109/ICDMW58026.2022.00109.
dfr_aedd( encoding_size, ae_class = NULL, ae_params = list(batch_size = 32, num_epochs = 1000, learning_rate = 0.001, error_function = "mae"), window_size = 100, window_type = "sliding", monitoring_step = floor(window_size/2), criteria = "mann_whitney", alpha = 0.01, threshold = 0.01, target_feat = NULL )dfr_aedd( encoding_size, ae_class = NULL, ae_params = list(batch_size = 32, num_epochs = 1000, learning_rate = 0.001, error_function = "mae"), window_size = 100, window_type = "sliding", monitoring_step = floor(window_size/2), criteria = "mann_whitney", alpha = 0.01, threshold = 0.01, target_feat = NULL )
encoding_size |
Encoding Size |
ae_class |
Autoencoder constructor. When |
ae_params |
Named list of arguments forwarded to |
window_size |
Number of most recent rows kept by the detector. The oldest half is the reference window and the newest half the recent window. |
window_type |
Window policy. |
monitoring_step |
Number of rows between two consecutive checks. The default checks twice per window. |
criteria |
The method used to check for a drift. One of |
alpha |
Significance level of the hypothesis-test criteria ( |
threshold |
Drift threshold of the |
target_feat |
Names of the columns to be monitored. |
The autoencoder expects inputs in the [0, 1] range. Values
outside that range are clipped to it, and the detector warns the first time
this happens, so the data should be normalized beforehand (for example with
daltoolbox::minmax()). ae_class must be a constructor accepting the
arguments input_size and encoding_size plus the entries of ae_params,
whose objects implement fit() and transform().
The autoencoder is trained on the reference window the first time the window is full, and again after every drift or whenever new columns appear.
The "levene" criterion tests every reconstructed feature and
combines the per-feature p-values with a Bonferroni correction, instead of
looking at the first feature only.
dfr_aedd object
Kaminskyi, D., Li, B., and Muller, E. (2022). Reconstruction-based unsupervised drift detection over multivariate streaming data. In 2022 IEEE International Conference on Data Mining Workshops (ICDMW). https://doi.org/10.1109/ICDMW58026.2022.00109
# Training an autoencoder is too slow for an inline example. A rendered # walkthrough is available at # https://github.com/cefet-rj-dal/heimdall/blob/main/examples/1_detection/dfr_aedd.md# Training an autoencoder is too slow for an inline example. A rendered # walkthrough is available at # https://github.com/cefet-rj-dal/heimdall/blob/main/examples/1_detection/dfr_aedd.md
CUSUM is a sequential analysis procedure that accumulates deviations in a monitored signal and raises an alarm when the cumulative evidence exceeds a threshold. In this package, the detector is implemented as an error-based monitor, so it is primarily intended for real concept drift affecting predictive performance. The concept-drift adaptation follows the sequential change-detection literature discussed by Muthukrishnan, Berg, and Wu (2007) https://doi.org/10.1109/ICDMW.2007.89.
dfr_cusum(lambda = 50, delta = 0.005, min_instances = 30)dfr_cusum(lambda = 50, delta = 0.005, min_instances = 30)
lambda |
Detection threshold: a drift is reported when the cumulative sum exceeds it. Lower values react faster at the cost of more false alarms. |
delta |
Tolerance subtracted at every step, i.e. the increase of the error rate that is still considered acceptable. |
min_instances |
Minimum number of observations before a drift can be reported. |
The detector follows the CusumDM detector of MOA: it accumulates
max(0, sum + x - mean - delta), where mean is the running error rate, so
it reacts to an increase of the error rate whatever its level. After a drift
is reported the statistics restart from scratch. Missing values are treated
as correct predictions (0).
Up to version 1.3.0, dfr_cusum() accumulated +1 for every error and -1
for every correct prediction, so it could only detect error rates above 50%.
dfr_cusum object
Muthukrishnan, S., Berg, E., and Wu, Y. (2007). Sequential change detection on data streams. In Seventh IEEE International Conference on Data Mining Workshops (ICDMW 2007). https://doi.org/10.1109/ICDMW.2007.89
# Load Heimdall and the synthetic benchmark stream. library(heimdall) # Fix the seed for reproducibility. seed <- 1 set.seed(seed) # Load the stream and derive a simple binary error-like signal. data(st_drift_examples) data <- st_drift_examples$uv_virtual_drift data$prediction <- st_drift_examples$uv_virtual_drift$serie > 4 # Plot the binary monitored stream used by CUSUM. plot(x=seq_len(nrow(data)), y=data$prediction) # Instantiate the CUSUM detector. model <- dfr_cusum() # Update the detector sequentially over the binary stream. detection <- NULL output <- list(obj=model, drift=FALSE) for (i in seq_len(nrow(data))){ output <- update_state(output$obj, data$prediction[i]) if (output$drift){ type <- 'drift' output$obj <- reset_state(output$obj) } else { type <- '' } detection <- rbind(detection, data.frame(idx=i, event=output$drift, type=type)) } # Print the drift alarms produced by the detector. detection[detection$type == 'drift',] # Map the drift alarms back onto the original numeric series. plot(x=seq_len(nrow(data)), y=data$serie) for (drift_index in detection[detection$type == 'drift', 'idx']) { abline(v=drift_index, col='red', lty=2) }# Load Heimdall and the synthetic benchmark stream. library(heimdall) # Fix the seed for reproducibility. seed <- 1 set.seed(seed) # Load the stream and derive a simple binary error-like signal. data(st_drift_examples) data <- st_drift_examples$uv_virtual_drift data$prediction <- st_drift_examples$uv_virtual_drift$serie > 4 # Plot the binary monitored stream used by CUSUM. plot(x=seq_len(nrow(data)), y=data$prediction) # Instantiate the CUSUM detector. model <- dfr_cusum() # Update the detector sequentially over the binary stream. detection <- NULL output <- list(obj=model, drift=FALSE) for (i in seq_len(nrow(data))){ output <- update_state(output$obj, data$prediction[i]) if (output$drift){ type <- 'drift' output$obj <- reset_state(output$obj) } else { type <- '' } detection <- rbind(detection, data.frame(idx=i, event=output$drift, type=type)) } # Print the drift alarms produced by the detector. detection[detection$type == 'drift',] # Map the drift alarms back onto the original numeric series. plot(x=seq_len(nrow(data)), y=data$serie) for (drift_index in detection[detection$type == 'drift', 'idx']) { abline(v=drift_index, col='red', lty=2) }
DDM monitors the online error rate of a predictive model under the PAC-learning assumption that, in a stationary environment, the error should decrease or remain stable as more samples are observed. Because it operates on the classifier error stream, it is primarily a detector of real concept drift. The method follows Gama et al. (2004) https://doi.org/10.1007/978-3-540-28645-5_29.
dfr_ddm(min_instances = 30, warning_level = 2, out_control_level = 3)dfr_ddm(min_instances = 30, warning_level = 2, out_control_level = 3)
min_instances |
The minimum number of instances before detecting change |
warning_level |
Necessary level for warning zone (2 standard deviation) |
out_control_level |
Necessary level for a positive drift detection |
The detector only reacts to an increase of the error rate (a
degradation of the model); improvements are not reported. Values must be 1
(error) or 0 (correct prediction), and missing values are treated as
correct predictions (0).
dfr_ddm object
Gama, J., Medas, P., Castillo, G., and Rodrigues, P. P. (2004). Learning with drift detection. In Advances in Artificial Intelligence - SBIA 2004, 286-295. https://doi.org/10.1007/978-3-540-28645-5_29
# Load Heimdall and the built-in synthetic data stream. library(heimdall) # Fix the seed to make the example reproducible. seed <- 1 set.seed(seed) # Load the univariate stream and derive the binary monitored signal. data(st_drift_examples) data <- st_drift_examples$uv_virtual_drift data$prediction <- st_drift_examples$uv_virtual_drift$serie > 4 # Plot the binary stream monitored by DDM. plot(x=seq_len(nrow(data)), y=data$prediction) # Instantiate the DDM detector. model <- dfr_ddm() # Process the stream sequentially and store the drift alarms. detection <- NULL output <- list(obj=model, drift=FALSE) for (i in seq_len(nrow(data))){ output <- update_state(output$obj, data$prediction[i]) if (output$drift){ type <- 'drift' output$obj <- reset_state(output$obj) } else { type <- '' } detection <- rbind(detection, data.frame(idx=i, event=output$drift, type=type)) } # Print the positions where DDM detected drift. detection[detection$type == 'drift',] # Show the detected drifts over the original numeric signal. plot(x=seq_len(nrow(data)), y=data$serie) for (drift_index in detection[detection$type == 'drift', 'idx']) { abline(v=drift_index, col='red', lty=2) }# Load Heimdall and the built-in synthetic data stream. library(heimdall) # Fix the seed to make the example reproducible. seed <- 1 set.seed(seed) # Load the univariate stream and derive the binary monitored signal. data(st_drift_examples) data <- st_drift_examples$uv_virtual_drift data$prediction <- st_drift_examples$uv_virtual_drift$serie > 4 # Plot the binary stream monitored by DDM. plot(x=seq_len(nrow(data)), y=data$prediction) # Instantiate the DDM detector. model <- dfr_ddm() # Process the stream sequentially and store the drift alarms. detection <- NULL output <- list(obj=model, drift=FALSE) for (i in seq_len(nrow(data))){ output <- update_state(output$obj, data$prediction[i]) if (output$drift){ type <- 'drift' output$obj <- reset_state(output$obj) } else { type <- '' } detection <- rbind(detection, data.frame(idx=i, event=output$drift, type=type)) } # Print the positions where DDM detected drift. detection[detection$type == 'drift',] # Show the detected drifts over the original numeric signal. plot(x=seq_len(nrow(data)), y=data$serie) for (drift_index in detection[detection$type == 'drift', 'idx']) { abline(v=drift_index, col='red', lty=2) }
ECDD applies an exponentially weighted moving average (EWMA) control chart to the online classification error stream. Since it monitors predictive errors directly, it is primarily designed to detect real concept drift. The method follows Ross et al. (2012), who adapted EWMA charts for concept-drift detection in streaming classifiers https://doi.org/10.1016/j.patrec.2011.08.019.
dfr_ecdd(lambda = 0.2, min_run_instances = 30, average_run_length = 400)dfr_ecdd(lambda = 0.2, min_run_instances = 30, average_run_length = 400)
lambda |
EWMA smoothing parameter |
min_run_instances |
The minimum number of instances before detecting change |
average_run_length |
Desired Average Run Length (ARL), i.e. the expected number of observations between two false alarms. The published control-limit polynomials exist for 100, 400 and 1000; other values use the next one up, and values above 1000 are rejected. The default changed from 100 to 400 in the development version, which reduced false alarms about sevenfold. |
Missing values are treated as correct predictions (0).
dfr_ecdd object
Ross, G. J., Adams, N. M., Tasoulis, D. K., and Hand, D. J. (2012). Exponentially weighted moving average charts for detecting concept drift. Pattern Recognition Letters, 33(2), 191-198. https://doi.org/10.1016/j.patrec.2011.08.019
# Load Heimdall and the synthetic example stream. library(heimdall) # Fix the seed for reproducibility. seed <- 1 set.seed(seed) # Load the stream and derive the binary monitored signal. data(st_drift_examples) data <- st_drift_examples$uv_virtual_drift data$prediction <- st_drift_examples$uv_virtual_drift$serie > 4 # Plot the binary stream that ECDD will monitor. plot(x=seq_len(nrow(data)), y=data$prediction) # Instantiate the ECDD detector. model <- dfr_ecdd() # Update the detector sequentially and store the detected alarms. detection <- NULL output <- list(obj=model, drift=FALSE) for (i in seq_len(nrow(data))){ output <- update_state(output$obj, data$prediction[i]) if (output$drift){ type <- 'drift' output$obj <- reset_state(output$obj) } else { type <- '' } detection <- rbind(detection, data.frame(idx=i, event=output$drift, type=type)) } # Print the drift points signaled by ECDD. detection[detection$type == 'drift',] # Overlay the alarms on the original numeric signal. plot(x=seq_len(nrow(data)), y=data$serie) for (drift_index in detection[detection$type == 'drift', 'idx']) { abline(v=drift_index, col='red', lty=2) }# Load Heimdall and the synthetic example stream. library(heimdall) # Fix the seed for reproducibility. seed <- 1 set.seed(seed) # Load the stream and derive the binary monitored signal. data(st_drift_examples) data <- st_drift_examples$uv_virtual_drift data$prediction <- st_drift_examples$uv_virtual_drift$serie > 4 # Plot the binary stream that ECDD will monitor. plot(x=seq_len(nrow(data)), y=data$prediction) # Instantiate the ECDD detector. model <- dfr_ecdd() # Update the detector sequentially and store the detected alarms. detection <- NULL output <- list(obj=model, drift=FALSE) for (i in seq_len(nrow(data))){ output <- update_state(output$obj, data$prediction[i]) if (output$drift){ type <- 'drift' output$obj <- reset_state(output$obj) } else { type <- '' } detection <- rbind(detection, data.frame(idx=i, event=output$drift, type=type)) } # Print the drift points signaled by ECDD. detection[detection$type == 'drift',] # Overlay the alarms on the original numeric signal. plot(x=seq_len(nrow(data)), y=data$serie) for (drift_index in detection[detection$type == 'drift', 'idx']) { abline(v=drift_index, col='red', lty=2) }
EDDM extends DDM by monitoring the distance between classification errors instead of only the error rate, which makes it more sensitive to gradual degradation. Because it operates on the model error stream, it is primarily intended for real concept drift. The method follows Baena-Garcia et al. (2006), who proposed EDDM for improved detection of gradual drift.
dfr_eddm( min_instances = 30, min_num_errors = 30, warning_level = 0.95, out_control_level = 0.9 )dfr_eddm( min_instances = 30, min_num_errors = 30, warning_level = 0.95, out_control_level = 0.9 )
min_instances |
The minimum number of instances before detecting change |
min_num_errors |
The minimum number of errors before detecting change |
warning_level |
Necessary level for warning zone |
out_control_level |
Necessary level for a positive drift detection |
EDDM monitors the distance between consecutive errors, which makes
it sensitive to gradual drifts but also prone to false alarms when the error
rate is high. warning_level must not be lower than out_control_level.
Values must be 1 (error) or 0 (correct prediction), and missing values
are treated as correct predictions (0).
dfr_eddm object
Baena-Garcia, M., del Campo-Avila, J., Fidalgo, R., Bifet, A., Gavalda, R., and Morales-Bueno, R. (2006). Early drift detection method. In Fourth International Workshop on Knowledge Discovery from Data Streams.
# Load Heimdall and the built-in synthetic stream. library(heimdall) # Fix the seed so the results are reproducible. seed <- 1 set.seed(seed) # Load the stream and derive the binary monitored signal. data(st_drift_examples) data <- st_drift_examples$uv_virtual_drift data$prediction <- st_drift_examples$uv_virtual_drift$serie > 4 # Visualize the binary stream that EDDM will monitor. plot(x=seq_len(nrow(data)), y=data$prediction) # Instantiate the EDDM detector. model <- dfr_eddm() # Process the stream sequentially and store each detected alarm. detection <- NULL output <- list(obj=model, drift=FALSE) for (i in seq_len(nrow(data))){ output <- update_state(output$obj, data$prediction[i]) if (output$drift){ type <- 'drift' output$obj <- reset_state(output$obj) } else { type <- '' } detection <- rbind(detection, data.frame(idx=i, event=output$drift, type=type)) } # Print the drift alarms returned by EDDM. detection[detection$type == 'drift',] # Show those alarms on top of the original numeric series. plot(x=seq_len(nrow(data)), y=data$serie) for (drift_index in detection[detection$type == 'drift', 'idx']) { abline(v=drift_index, col='red', lty=2) }# Load Heimdall and the built-in synthetic stream. library(heimdall) # Fix the seed so the results are reproducible. seed <- 1 set.seed(seed) # Load the stream and derive the binary monitored signal. data(st_drift_examples) data <- st_drift_examples$uv_virtual_drift data$prediction <- st_drift_examples$uv_virtual_drift$serie > 4 # Visualize the binary stream that EDDM will monitor. plot(x=seq_len(nrow(data)), y=data$prediction) # Instantiate the EDDM detector. model <- dfr_eddm() # Process the stream sequentially and store each detected alarm. detection <- NULL output <- list(obj=model, drift=FALSE) for (i in seq_len(nrow(data))){ output <- update_state(output$obj, data$prediction[i]) if (output$drift){ type <- 'drift' output$obj <- reset_state(output$obj) } else { type <- '' } detection <- rbind(detection, data.frame(idx=i, event=output$drift, type=type)) } # Print the drift alarms returned by EDDM. detection[detection$type == 'drift',] # Show those alarms on top of the original numeric series. plot(x=seq_len(nrow(data)), y=data$serie) for (drift_index in detection[detection$type == 'drift', 'idx']) { abline(v=drift_index, col='red', lty=2) }
HDDM_A is a sequential detector based on Hoeffding's inequality that tests whether the mean of the monitored error stream has increased beyond statistically expected fluctuations. Because this implementation is error-based, it is primarily targeted at real concept drift. The theoretical basis follows Frias-Blanco et al. (2015) https://doi.org/10.1109/TKDE.2014.2345382.
dfr_hddm( drift_confidence = 0.001, warning_confidence = 0.005, two_side_option = TRUE )dfr_hddm( drift_confidence = 0.001, warning_confidence = 0.005, two_side_option = TRUE )
drift_confidence |
Confidence to the drift |
warning_confidence |
Confidence to the warning |
two_side_option |
Option to monitor error increments and decrements (two-sided) or only increments (one-sided) |
The detector reports increases of the error rate. With
two_side_option = TRUE, a significant decrease restarts the statistics but
is not reported as a drift. Values must be 1 (error) or 0 (correct
prediction), and missing values are treated as correct predictions (0).
dfr_hddm object
Frias-Blanco, I., del Campo-Avila, J., Ramos-Jimenez, G., Morales-Bueno, R., Ortiz-Diaz, A., and Caballero-Mota, Y. (2015). Online and nonparametric drift detection methods based on Hoeffding's bounds. IEEE Transactions on Knowledge and Data Engineering, 27(3), 810-823. https://doi.org/10.1109/TKDE.2014.2345382
# Load Heimdall and the synthetic stream example. library(heimdall) # Fix the seed for reproducibility. seed <- 1 set.seed(seed) # Load the stream and derive the binary monitored signal. data(st_drift_examples) data <- st_drift_examples$uv_virtual_drift data$prediction <- st_drift_examples$uv_virtual_drift$serie > 4 # Plot the binary stream that HDDM_A will monitor. plot(x=seq_len(nrow(data)), y=data$prediction) # Instantiate the HDDM_A detector. model <- dfr_hddm() # Process the stream sequentially and record the detector alarms. detection <- NULL output <- list(obj=model, drift=FALSE) for (i in seq_len(nrow(data))){ output <- update_state(output$obj, data$prediction[i]) if (output$drift){ type <- 'drift' output$obj <- reset_state(output$obj) } else { type <- '' } detection <- rbind(detection, data.frame(idx=i, event=output$drift, type=type)) } # Print the detected drift points. detection[detection$type == 'drift',] # Overlay the alarms on the original numeric stream. plot(x=seq_len(nrow(data)), y=data$serie) for (drift_index in detection[detection$type == 'drift', 'idx']) { abline(v=drift_index, col='red', lty=2) }# Load Heimdall and the synthetic stream example. library(heimdall) # Fix the seed for reproducibility. seed <- 1 set.seed(seed) # Load the stream and derive the binary monitored signal. data(st_drift_examples) data <- st_drift_examples$uv_virtual_drift data$prediction <- st_drift_examples$uv_virtual_drift$serie > 4 # Plot the binary stream that HDDM_A will monitor. plot(x=seq_len(nrow(data)), y=data$prediction) # Instantiate the HDDM_A detector. model <- dfr_hddm() # Process the stream sequentially and record the detector alarms. detection <- NULL output <- list(obj=model, drift=FALSE) for (i in seq_len(nrow(data))){ output <- update_state(output$obj, data$prediction[i]) if (output$drift){ type <- 'drift' output$obj <- reset_state(output$obj) } else { type <- '' } detection <- rbind(detection, data.frame(idx=i, event=output$drift, type=type)) } # Print the detected drift points. detection[detection$type == 'drift',] # Overlay the alarms on the original numeric stream. plot(x=seq_len(nrow(data)), y=data$serie) for (drift_index in detection[detection$type == 'drift', 'idx']) { abline(v=drift_index, col='red', lty=2) }
Implements a dummy detector that never reports a drift. Useful as a lower baseline when evaluating adaptation strategies.
dfr_inactive()dfr_inactive()
Drifter object
model <- dfr_inactive() output <- update_state(model, 1) output$driftmodel <- dfr_inactive() output <- update_state(model, 1) output$drift
This detector compares a reference and a recent window through the symmetrised Kullback-Leibler divergence of their binned empirical distributions, sum((p - q) * log(p / q)), which is the quantity also known as the Jeffreys divergence or, in the credit-risk literature, the Population Stability Index. It is multivariate: every monitored column contributes to the score. Because it monitors the distribution of the observed features rather than predictive error, it is primarily used for virtual concept drift. The statistical foundation is the divergence introduced by Kullback and Leibler (1951).
dfr_kldist( target_feat = NULL, window_size = 100, threshold = 0.12, window_type = "sliding", breaks = 20, monitoring_step = 1, data = NULL, p_th = NULL )dfr_kldist( target_feat = NULL, window_size = 100, threshold = 0.12, window_type = "sliding", breaks = 20, monitoring_step = 1, data = NULL, p_th = NULL )
target_feat |
Names of the columns to be monitored. |
window_size |
Size of the window |
threshold |
Drift threshold applied to the divergence. The default
( |
window_type |
Window policy. |
breaks |
Number of bins used to discretise each feature before the divergence is computed. |
monitoring_step |
Number of rows between two consecutive evaluations. The default ( |
data |
Already collected data to avoid cold start. |
p_th |
Deprecated name of |
Bins are built over the reference and recent halves together, so the two histograms are always comparable. Empty bins are smoothed before the logarithm is taken, and the score is averaged over bins and over features.
The detector consumes one row of a data frame at a time (a single value is also accepted). Rows whose monitored values are all missing are skipped instead of being imputed.
dfr_kldist object
Kullback, S., and Leibler, R. A. (1951). On information and sufficiency. The Annals of Mathematical Statistics, 22(1), 79-86. https://doi.org/10.1214/aoms/1177729694
# Load Heimdall and the example data stream. library(heimdall) # Fix the seed to keep the example reproducible. seed <- 1 set.seed(seed) # Load the univariate stream monitored in this walkthrough. data(st_drift_examples) serie <- st_drift_examples$uv_virtual_drift # Plot the monitored numeric signal before detection. plot(x=seq_len(nrow(serie)), y=serie$serie) # Instantiate the KL-divergence-based detector. model <- dfr_kldist(window_size=100, threshold=0.10) # Update the detector sequentially and store drift alarms. detection <- NULL output <- list(obj=model, drift=FALSE) for (i in seq_len(nrow(serie))){ output <- update_state(output$obj, serie[i, 'serie', drop=FALSE]) if (output$drift){ type <- 'drift' output$obj <- reset_state(output$obj) } else { type <- '' } detection <- rbind(detection, data.frame(idx=i, event=output$drift, type=type)) } # Print the points where KLDIST detected drift. detection[detection$type == 'drift',] # Overlay the detected drifts on the original numeric stream. plot(x=seq_len(nrow(serie)), y=serie$serie) for (drift_index in detection[detection$type == 'drift', 'idx']) { abline(v=drift_index, col='red', lty=2) }# Load Heimdall and the example data stream. library(heimdall) # Fix the seed to keep the example reproducible. seed <- 1 set.seed(seed) # Load the univariate stream monitored in this walkthrough. data(st_drift_examples) serie <- st_drift_examples$uv_virtual_drift # Plot the monitored numeric signal before detection. plot(x=seq_len(nrow(serie)), y=serie$serie) # Instantiate the KL-divergence-based detector. model <- dfr_kldist(window_size=100, threshold=0.10) # Update the detector sequentially and store drift alarms. detection <- NULL output <- list(obj=model, drift=FALSE) for (i in seq_len(nrow(serie))){ output <- update_state(output$obj, serie[i, 'serie', drop=FALSE]) if (output$drift){ type <- 'drift' output$obj <- reset_state(output$obj) } else { type <- '' } detection <- rbind(detection, data.frame(idx=i, event=output$drift, type=type)) } # Print the points where KLDIST detected drift. detection[detection$type == 'drift',] # Overlay the detected drifts on the original numeric stream. plot(x=seq_len(nrow(serie)), y=serie$serie) for (drift_index in detection[detection$type == 'drift', 'idx']) { abline(v=drift_index, col='red', lty=2) }
KSWIN applies a Kolmogorov-Smirnov test between a recent window and a reference sample drawn from older observations. In this package, the method is primarily used for virtual concept drift, because it monitors distributional changes in a numeric feature stream. The method follows Raab et al. (2020) https://doi.org/10.1016/j.neucom.2019.11.111.
dfr_kswin( target_feat = NULL, window_size = 300, stat_size = 100, alpha = 1e-04, window_type = "sliding", monitoring_step = 1, exact = NULL, data = NULL, seed = NULL )dfr_kswin( target_feat = NULL, window_size = 300, stat_size = 100, alpha = 1e-04, window_type = "sliding", monitoring_step = 1, exact = NULL, data = NULL, seed = NULL )
target_feat |
Feature to be monitored. |
window_size |
Size of the sliding window. It must be greater than
|
stat_size |
Size of the statistic window |
alpha |
Probability for the test statistic of the Kolmogorov-Smirnov-Test The alpha parameter is very sensitive, therefore should be set below 0.01. |
window_type |
Window policy. |
monitoring_step |
Number of observations between two consecutive tests. The default ( |
exact |
Passed to |
data |
Already collected data to avoid cold start. |
seed |
Seed of the random number stream used to subsample the reference
window. When |
The reference window is randomly subsampled to stat_size
observations. The detector keeps its own random number stream, so it does not
change the global random state while it runs; set seed (or call
base::set.seed() before creating the detector) for reproducible results.
A drift requires both a significant p-value and a KS statistic above
sqrt(-log(alpha) / stat_size), as defined by Raab et al. Missing
observations are skipped instead of being imputed.
dfr_kswin object
Raab, C., Heusinger, M., and Schleif, F.-M. (2020). Reactive soft prototype computing for concept drift streams. Neurocomputing, 416, 340-351. https://doi.org/10.1016/j.neucom.2019.11.111
# Load Heimdall and the synthetic univariate stream. library(heimdall) # Fix the seed for reproducibility. seed <- 1 set.seed(seed) # Load the numeric stream used by the detector. data(st_drift_examples) serie <- st_drift_examples$uv_virtual_drift # Plot the monitored variable before detection. plot(x=seq_len(nrow(serie)), y=serie$serie) # Instantiate KSWIN with explicit window parameters. model <- dfr_kswin(target_feat='serie', window_size=100, stat_size=50) # Update the detector sequentially and store the alarm positions. detection <- NULL output <- list(obj=model, drift=FALSE) for (i in seq_len(nrow(serie))){ output <- update_state(output$obj, serie$serie[i]) if (output$drift){ type <- 'drift' output$obj <- reset_state(output$obj) } else { type <- '' } detection <- rbind(detection, data.frame(idx=i, event=output$drift, type=type)) } # Print the points where KSWIN raised drift alarms. detection[detection$type == 'drift',] # Overlay the detected drifts on the original stream. plot(x=seq_len(nrow(serie)), y=serie$serie) for (drift_index in detection[detection$type == 'drift', 'idx']) { abline(v=drift_index, col='red', lty=2) }# Load Heimdall and the synthetic univariate stream. library(heimdall) # Fix the seed for reproducibility. seed <- 1 set.seed(seed) # Load the numeric stream used by the detector. data(st_drift_examples) serie <- st_drift_examples$uv_virtual_drift # Plot the monitored variable before detection. plot(x=seq_len(nrow(serie)), y=serie$serie) # Instantiate KSWIN with explicit window parameters. model <- dfr_kswin(target_feat='serie', window_size=100, stat_size=50) # Update the detector sequentially and store the alarm positions. detection <- NULL output <- list(obj=model, drift=FALSE) for (i in seq_len(nrow(serie))){ output <- update_state(output$obj, serie$serie[i]) if (output$drift){ type <- 'drift' output$obj <- reset_state(output$obj) } else { type <- '' } detection <- rbind(detection, data.frame(idx=i, event=output$drift, type=type)) } # Print the points where KSWIN raised drift alarms. detection[detection$type == 'drift',] # Overlay the detected drifts on the original stream. plot(x=seq_len(nrow(serie)), y=serie$serie) for (drift_index in detection[detection$type == 'drift', 'idx']) { abline(v=drift_index, col='red', lty=2) }
LBDD is a window-based detector that compares the variability of a reference and a recent sample using Levene's test. Because it monitors changes in the distribution of an observed feature rather than model performance, it is primarily aimed at virtual concept drift. In this package, the detector follows the statistical-testing approach discussed by Giusti et al. (2021) for drift analysis, using Levene's variance test as its core mechanism.
dfr_lbdd( target_feat = NULL, alpha = 0.001, window_size = 300, window_type = "sliding", monitoring_step = 1, data = NULL )dfr_lbdd( target_feat = NULL, alpha = 0.001, window_size = 300, window_type = "sliding", monitoring_step = 1, data = NULL )
target_feat |
Feature to be monitored |
alpha |
Probability threshold for the test statistic |
window_size |
Size of the window |
window_type |
Window policy. |
monitoring_step |
Number of observations between two consecutive tests. The default ( |
data |
Already collected data to avoid cold start. |
LBDD only reacts to changes of spread (variance); a shift of the mean
with the same spread is not detected, so combine it with dfr_mcdd() when
both kinds of change matter. The test runs at every observation, so alpha
should be small to keep false alarms rare.
Levene's test is computed with the group spread centred on the median (the Brown-Forsythe variant).
Missing observations are skipped instead of being imputed. When a drift is
reported the window is trimmed to its most recent half; reset_state()
empties it.
dfr_lbdd object
Giusti, L., Carvalho, L., Gomes, A. T., Coutinho, R., Soares, J., and Ogasawara, E. (2021). Analysing flight delay under concept drift. Evolving Systems. https://doi.org/10.1007/s12530-021-09415-z
# Load Heimdall and the synthetic univariate stream. library(heimdall) # Fix the seed for reproducibility. seed <- 1 set.seed(seed) # Load the numeric stream monitored by the detector. data(st_drift_examples) serie <- st_drift_examples$uv_virtual_drift # Plot the series before starting drift detection. plot(x=seq_len(nrow(serie)), y=serie$serie) # Instantiate the Levene-based detector. model <- dfr_lbdd(target_feat='serie', window_size=100) # Update the detector over the stream and record each drift alarm. detection <- NULL output <- list(obj=model, drift=FALSE) for (i in seq_len(nrow(serie))){ output <- update_state(output$obj, serie$serie[i]) if (output$drift){ type <- 'drift' output$obj <- reset_state(output$obj) } else { type <- '' } detection <- rbind(detection, data.frame(idx=i, event=output$drift, type=type)) } # Print the positions where LBDD detected drift. detection[detection$type == 'drift',] # Overlay those drift points on the original numeric stream. plot(x=seq_len(nrow(serie)), y=serie$serie) for (drift_index in detection[detection$type == 'drift', 'idx']) { abline(v=drift_index, col='red', lty=2) }# Load Heimdall and the synthetic univariate stream. library(heimdall) # Fix the seed for reproducibility. seed <- 1 set.seed(seed) # Load the numeric stream monitored by the detector. data(st_drift_examples) serie <- st_drift_examples$uv_virtual_drift # Plot the series before starting drift detection. plot(x=seq_len(nrow(serie)), y=serie$serie) # Instantiate the Levene-based detector. model <- dfr_lbdd(target_feat='serie', window_size=100) # Update the detector over the stream and record each drift alarm. detection <- NULL output <- list(obj=model, drift=FALSE) for (i in seq_len(nrow(serie))){ output <- update_state(output$obj, serie$serie[i]) if (output$drift){ type <- 'drift' output$obj <- reset_state(output$obj) } else { type <- '' } detection <- rbind(detection, data.frame(idx=i, event=output$drift, type=type)) } # Print the positions where LBDD detected drift. detection[detection$type == 'drift',] # Overlay those drift points on the original numeric stream. plot(x=seq_len(nrow(serie)), y=serie$serie) for (drift_index in detection[detection$type == 'drift', 'idx']) { abline(v=drift_index, col='red', lty=2) }
MCDD is a window-based detector that compares the location of a reference and a recent sample by means of a hypothesis test on their central tendency. Because it monitors the distribution of an observed feature rather than predictive errors, it is primarily intended for virtual concept drift. In this package, the detector follows the statistical-testing perspective adopted by Giusti et al. (2021) for drift analysis.
dfr_mcdd( target_feat = NULL, alpha = 1e-04, normality_alpha = 0.05, window_size = 300, window_type = "sliding", monitoring_step = 1, data = NULL )dfr_mcdd( target_feat = NULL, alpha = 1e-04, normality_alpha = 0.05, window_size = 300, window_type = "sliding", monitoring_step = 1, data = NULL )
target_feat |
Feature to be monitored |
alpha |
Probability threshold for the comparison test |
normality_alpha |
Significance level of the Shapiro-Wilk normality test used to decide between the parametric and the nonparametric comparison. It is deliberately separate from |
window_size |
Size of the window |
window_type |
Window policy. |
monitoring_step |
Number of observations between two consecutive tests. The default ( |
data |
Already collected data to avoid cold start. |
MCDD only reacts to changes of location (mean or median); a change of
spread with the same centre is not detected, so combine it with dfr_lbdd()
when both kinds of change matter.
Normality of both halves of the window is checked with
stats::shapiro.test(); when it holds, a t-test is applied, otherwise a
Wilcoxon rank-sum test is used. Shapiro-Wilk is only defined for samples of at
most 5000 observations, so larger windows always fall back to the
nonparametric test.
Missing observations are skipped instead of being imputed.
dfr_mcdd object
Giusti, L., Carvalho, L., Gomes, A. T., Coutinho, R., Soares, J., and Ogasawara, E. (2021). Analysing flight delay under concept drift. Evolving Systems. https://doi.org/10.1007/s12530-021-09415-z
# Load Heimdall and the synthetic stream. library(heimdall) # Fix the seed to keep the walkthrough reproducible. seed <- 1 set.seed(seed) # Load the univariate series monitored in this example. data(st_drift_examples) serie <- st_drift_examples$uv_virtual_drift # Plot the monitored signal before running the detector. plot(x=seq_len(nrow(serie)), y=serie$serie) # Instantiate the mean-comparison detector. model <- dfr_mcdd(target_feat='serie', window_size=100) # Update the detector sequentially and collect the alarm positions. detection <- NULL output <- list(obj=model, drift=FALSE) for (i in seq_len(nrow(serie))){ output <- update_state(output$obj, serie$serie[i]) if (output$drift){ type <- 'drift' output$obj <- reset_state(output$obj) } else { type <- '' } detection <- rbind(detection, data.frame(idx=i, event=output$drift, type=type)) } # Print the detected drift points. detection[detection$type == 'drift',] # Overlay the detected drifts on the original series. plot(x=seq_len(nrow(serie)), y=serie$serie) for (drift_index in detection[detection$type == 'drift', 'idx']) { abline(v=drift_index, col='red', lty=2) }# Load Heimdall and the synthetic stream. library(heimdall) # Fix the seed to keep the walkthrough reproducible. seed <- 1 set.seed(seed) # Load the univariate series monitored in this example. data(st_drift_examples) serie <- st_drift_examples$uv_virtual_drift # Plot the monitored signal before running the detector. plot(x=seq_len(nrow(serie)), y=serie$serie) # Instantiate the mean-comparison detector. model <- dfr_mcdd(target_feat='serie', window_size=100) # Update the detector sequentially and collect the alarm positions. detection <- NULL output <- list(obj=model, drift=FALSE) for (i in seq_len(nrow(serie))){ output <- update_state(output$obj, serie$serie[i]) if (output$drift){ type <- 'drift' output$obj <- reset_state(output$obj) } else { type <- '' } detection <- rbind(detection, data.frame(idx=i, event=output$drift, type=type)) } # Print the detected drift points. detection[detection$type == 'drift',] # Overlay the detected drifts on the original series. plot(x=seq_len(nrow(serie)), y=serie$serie) for (drift_index in detection[detection$type == 'drift', 'idx']) { abline(v=drift_index, col='red', lty=2) }
Combines the output of several drift detectors into a single decision.
dfr_multi_criteria(drifter_list, combination = "or", fuzzy_window = 10)dfr_multi_criteria(drifter_list, combination = "or", fuzzy_window = 10)
drifter_list |
Named list of drifters to combine. Every element must
inherit from either |
combination |
How the drifters will be combined. One of |
fuzzy_window |
Number of observations during which a detection is
still taken into account by the |
'or' reports a drift as soon as any detector does;
'and' reports a drift when every detector has reported one within
the last fuzzy_window observations;
'fuzzy' gives each detection a weight that decays linearly from 1 to
0 over fuzzy_window observations, and reports a drift when the sum of the
weights exceeds half the number of detectors.
After a combined drift is reported, the past detections are forgotten, so a
single change is reported once. Detectors with a target_feat receive that
column of each row; the other univariate detectors receive the row mean.
obj$detections counts the drifts reported by each detector and
obj$last_drifts holds their decisions on the last observation.
Drifter object
library(daltoolbox) library(heimdall) set.seed(1) data <- data.frame(serie = c(stats::rnorm(100), stats::rnorm(100, mean = 10))) model <- dfr_multi_criteria( drifter_list = list( ph = dfr_page_hinkley(), ph_sensitive = dfr_page_hinkley(threshold = 10) ), combination = "or" ) model <- fit(model, data) model$drifted model$detectionslibrary(daltoolbox) library(heimdall) set.seed(1) data <- data.frame(serie = c(stats::rnorm(100), stats::rnorm(100, mean = 10))) model <- dfr_multi_criteria( drifter_list = list( ph = dfr_page_hinkley(), ph_sensitive = dfr_page_hinkley(threshold = 10) ), combination = "or" ) model <- fit(model, data) model$drifted model$detections
The Page-Hinkley test is a sequential change-point detector that monitors the cumulative deviation of a signal from its running mean and signals a change when that deviation departs from its running minimum by more than a threshold. In this package, the implementation is primarily used for virtual concept drift when it monitors a numeric feature stream, although the same statistic can also be applied to error streams to detect real concept drift. The method is based on Page (1954).
dfr_page_hinkley( target_feat = NULL, min_instances = 30, delta = 0.005, threshold = 50, alpha = 1 - 1e-04, direction = "increase" )dfr_page_hinkley( target_feat = NULL, min_instances = 30, delta = 0.005, threshold = 50, alpha = 1 - 1e-04, direction = "increase" )
target_feat |
Feature to be monitored. |
min_instances |
The minimum number of instances before detecting change |
delta |
The delta factor for the Page Hinkley test |
threshold |
The change detection threshold (lambda) |
alpha |
The forgetting factor, used to weight the observed value and the mean |
direction |
Which changes of the mean are detected: |
Missing observations are skipped instead of being imputed.
dfr_page_hinkley object
Page, E. S. (1954). Continuous inspection schemes. Biometrika, 41(1/2), 100-115. https://doi.org/10.2307/2333009
# Load Heimdall and the synthetic stream example. library(heimdall) # Fix the seed for reproducibility. seed <- 1 set.seed(seed) # Load the univariate numeric stream monitored in this example. data(st_drift_examples) serie <- st_drift_examples$uv_virtual_drift # Plot the monitored signal before detection. plot(x=seq_len(nrow(serie)), y=serie$serie) # Instantiate the Page-Hinkley detector. model <- dfr_page_hinkley(target_feat='serie') # Update the detector sequentially and record every drift alarm. detection <- NULL output <- list(obj=model, drift=FALSE) for (i in seq_len(nrow(serie))){ output <- update_state(output$obj, serie$serie[i]) if (output$drift){ type <- 'drift' output$obj <- reset_state(output$obj) } else { type <- '' } detection <- rbind(detection, data.frame(idx=i, event=output$drift, type=type)) } # Print the detected drift points. detection[detection$type == 'drift',] # Overlay those alarms on the original numeric stream. plot(x=seq_len(nrow(serie)), y=serie$serie) for (drift_index in detection[detection$type == 'drift', 'idx']) { abline(v=drift_index, col='red', lty=2) } print(paste('Successfull run at', Sys.time()))# Load Heimdall and the synthetic stream example. library(heimdall) # Fix the seed for reproducibility. seed <- 1 set.seed(seed) # Load the univariate numeric stream monitored in this example. data(st_drift_examples) serie <- st_drift_examples$uv_virtual_drift # Plot the monitored signal before detection. plot(x=seq_len(nrow(serie)), y=serie$serie) # Instantiate the Page-Hinkley detector. model <- dfr_page_hinkley(target_feat='serie') # Update the detector sequentially and record every drift alarm. detection <- NULL output <- list(obj=model, drift=FALSE) for (i in seq_len(nrow(serie))){ output <- update_state(output$obj, serie$serie[i]) if (output$drift){ type <- 'drift' output$obj <- reset_state(output$obj) } else { type <- '' } detection <- rbind(detection, data.frame(idx=i, event=output$drift, type=type)) } # Print the detected drift points. detection[detection$type == 'drift',] # Overlay those alarms on the original numeric stream. plot(x=seq_len(nrow(serie)), y=serie$serie) for (drift_index in detection[detection$type == 'drift', 'idx']) { abline(v=drift_index, col='red', lty=2) } print(paste('Successfull run at', Sys.time()))
Implements a dummy detector that always reports a drift. Useful as an upper baseline, since it forces the model to be retrained at every batch.
dfr_passive()dfr_passive()
Drifter object
model <- dfr_passive() output <- update_state(model, 1) output$driftmodel <- dfr_passive() output <- update_state(model, 1) output$drift
Implements univariate Distribution Based drift detectors. These detectors monitor a single numeric feature; missing observations are skipped instead of being imputed.
dist_based(target_feat)dist_based(target_feat)
target_feat |
Feature to be monitored. |
Drifter object
obj <- dist_based(target_feat = "serie") obj$target_featobj <- dist_based(target_feat = "serie") obj$target_feat
Ancestor class for drift detection.
Every detector in the package follows the same contract:
update_state(obj, value) returns a list with the updated object
(obj) and a logical flag (drift) telling whether a drift was detected
** in that call**;
obj$drifted is sticky: it stays TRUE after the first detection
until reset_state() is called;
reset_state(obj) returns the detector to its initial state;
obj$drifter_output holds the per-observation diagnostics produced by
the last call to fit(), one row per observation; rows are NA when the
detector reported nothing for that observation (for example while its window
is filling, or for a skipped missing value).
drifter()drifter()
Drifter object
model <- drifter() model$driftedmodel <- drifter() model$drifted
Ancestor class for the baseline detectors that ignore the
monitored signal, dfr_inactive() and dfr_passive().
dummy()dummy()
Drifter object
obj <- dummy() class(obj)obj <- dummy() class(obj)
Implements Error Based drift detectors. These detectors monitor
the residuals of a predictive model, where 1 (or TRUE) means a wrong
prediction and 0 (or FALSE) a correct one. Missing values are treated as
correct predictions.
error_based()error_based()
Drifter object
obj <- error_based() class(obj)obj <- error_based() class(obj)
Process Batch
## S3 method for class 'drifter' fit(obj, data, prediction = NULL, ...)## S3 method for class 'drifter' fit(obj, data, prediction = NULL, ...)
obj |
Drifter object |
data |
data batch in data frame format |
prediction |
prediction batch as vector format. Optional and unused by the default method; it is kept for compatibility with detectors that need the model output. |
... |
optional arguments |
updated Drifter object
library(daltoolbox) model <- dfr_inactive() model <- fit(model, data.frame(serie = 1:10)) model$driftedlibrary(daltoolbox) model <- dfr_inactive() model <- fit(model, data.frame(serie = 1:10)) model$drifted
Ancestor class for metric calculation
metric()metric()
Metric object
obj <- metric() class(obj)obj <- metric() class(obj)
Class for accuracy calculation. Binary labels (logical, 0/1 or factors with those labels) are compared by value; other labels are compared as text.
mt_accuracy()mt_accuracy()
Metric object
library(daltoolbox) obj <- mt_accuracy() evaluate(obj, c(TRUE, FALSE, TRUE), c(TRUE, TRUE, TRUE))library(daltoolbox) obj <- mt_accuracy() evaluate(obj, c(TRUE, FALSE, TRUE), c(TRUE, TRUE, TRUE))
Class for FScore calculation
mt_fscore(f = 1)mt_fscore(f = 1)
f |
The beta parameter of the F-beta score. |
Metric object
library(daltoolbox) obj <- mt_fscore(f = 1) evaluate(obj, c(TRUE, FALSE, TRUE), c(TRUE, TRUE, TRUE))library(daltoolbox) obj <- mt_fscore(f = 1) evaluate(obj, c(TRUE, FALSE, TRUE), c(TRUE, TRUE, TRUE))
Class for precision calculation
mt_precision()mt_precision()
Metric object
library(daltoolbox) obj <- mt_precision() evaluate(obj, c(TRUE, FALSE, TRUE), c(TRUE, TRUE, TRUE))library(daltoolbox) obj <- mt_precision() evaluate(obj, c(TRUE, FALSE, TRUE), c(TRUE, TRUE, TRUE))
Class for recall calculation
mt_recall()mt_recall()
Metric object
library(daltoolbox) obj <- mt_recall() evaluate(obj, c(TRUE, FALSE, TRUE), c(TRUE, TRUE, TRUE))library(daltoolbox) obj <- mt_recall() evaluate(obj, c(TRUE, FALSE, TRUE), c(TRUE, TRUE, TRUE))
Class for ROC AUC calculation
mt_rocauc()mt_rocauc()
Metric object
library(daltoolbox) obj <- mt_rocauc() evaluate(obj, c(0.9, 0.2, 0.8, 0.1), factor(c(TRUE, TRUE, FALSE, FALSE)))library(daltoolbox) obj <- mt_rocauc() evaluate(obj, c(0.9, 0.2, 0.8, 0.1), factor(c(TRUE, TRUE, FALSE, FALSE)))
Implements Multivariate Distribution Based drift detectors. These detectors consume one row of a data frame at a time and monitor its numeric columns.
mv_dist_based(target_feat = NULL)mv_dist_based(target_feat = NULL)
target_feat |
Names of the columns to be monitored. |
Drifter object
obj <- mv_dist_based(target_feat = c("serie1", "serie2")) obj$target_featobj <- mv_dist_based(target_feat = c("serie1", "serie2")) obj$target_feat
Ancestor class for normalization techniques.
nrm_base(norm_class)nrm_base(norm_class)
norm_class |
Normalizer class |
This constructor was named norm() up to version 1.2.x. It was
renamed to nrm_base() because norm() masks base::norm() once the
package is attached.
Norm object
library(daltoolbox) obj <- nrm_base(norm_class = minmax()) class(obj)library(daltoolbox) obj <- nrm_base(norm_class = minmax()) class(obj)
Normalizer that keeps its own memory of the data seen so far, so that the normalization parameters are estimated over the whole history rather than over the most recent batch alone.
nrm_memory(norm_class = daltoolbox::minmax(), max_size = NULL)nrm_memory(norm_class = daltoolbox::minmax(), max_size = NULL)
norm_class |
Normalizer class |
max_size |
Maximum number of rows kept in the memory; the oldest rows
are dropped first. |
Norm object
library(daltoolbox) obj <- nrm_memory(norm_class = minmax()) obj <- fit(obj, data.frame(x = c(1, 2, 3))) transform(obj, data.frame(x = c(1, 2, 3)))library(daltoolbox) obj <- nrm_memory(norm_class = minmax()) obj <- fit(obj, data.frame(x = c(1, 2, 3))) transform(obj, data.frame(x = c(1, 2, 3)))
Reset Drifter State
reset_state(obj)reset_state(obj)
obj |
Drifter object |
updated Drifter object
model <- dfr_ddm() model <- reset_state(model) model$driftedmodel <- dfr_ddm() model <- reset_state(model) model$drifted
A list of univariate and multivariate time series for drift detection. Each element is a data frame:
uv_virtual_drift: univariate series (serie) with a change in the
feature distribution at four known points, flagged in event;
uv_vct_real_drift: univariate series with a virtual change followed
by a real drift;
uv_vdp_real_drift: univariate series with a real drift and no change
in the feature distribution;
mv_real_drift: bivariate series with a real drift;
mv_vct_real_drift: bivariate series with a virtual change followed
by a real drift;
mv_vdp_real_drift: bivariate series with a real drift and no change
in the feature distribution.
Except for uv_virtual_drift, every data frame holds the series (serie,
or serie1 and serie2), a logical target, the index i and a drift
column flagging the change point.
data(st_drift_examples)data(st_drift_examples)
A list of six data frames with 500 rows each.
data(st_drift_examples) dataset <- st_drift_examples$mv_real_drift which(dataset$drift == 1)data(st_drift_examples) dataset <- st_drift_examples$mv_real_drift which(dataset$drift == 1)
Ancestor class for drift adaptive models. It wraps a predictive model and a drift detector, retraining the model whenever a drift is reported.
stealthy( model, drift_method, norm_class = daltoolbox::zscore(), warmup_size = 100, th = 0.5, target_uni_drifter = FALSE, incremental_memory = FALSE, active_warmup = FALSE, class_balance = "inactive", obsolete_model = "null", reporting = FALSE, history_size = NULL, verbose = FALSE )stealthy( model, drift_method, norm_class = daltoolbox::zscore(), warmup_size = 100, th = 0.5, target_uni_drifter = FALSE, incremental_memory = FALSE, active_warmup = FALSE, class_balance = "inactive", obsolete_model = "null", reporting = FALSE, history_size = NULL, verbose = FALSE )
model |
The algorithm object to be used for predictions |
drift_method |
The algorithm object to detect drifts |
norm_class |
Class used to perform normalization |
warmup_size |
Number of rows used to warmup the drifter. No drift will be detected during this phase |
th |
The threshold to be used with classification algorithms |
target_uni_drifter |
Passes the prediction target to the drifts as the target feat when the drifter is univariate and dist_based. The target must be binary (logical, 0/1 or a factor with those labels). |
incremental_memory |
If true, the model will retrain with all available data whenever the fit is called. If false, it only retrains when a drift is detected. |
active_warmup |
If true, the models will be retrained for each batch while the number of rows for the warmup_size is not met. |
class_balance |
Class balancing strategy. |
obsolete_model |
Strategy used by |
reporting |
If TRUE, every batch given to |
history_size |
Maximum number of rows kept in |
verbose |
if TRUE shows drift messages |
The drifter decides what it monitors through its own target_feat,
given in terms of the one-hot encoded and normalized columns: a univariate
drifter with a target_feat receives that column, a univariate drifter
without one receives the row mean of every column, and multivariate,
multi-criteria and dummy drifters receive the selected columns (every column
when their target_feat is NULL).
The batch is evaluated before the model is updated: error-based detectors see prequential residuals, computed with the model as it was at the beginning of the call, and the detector is fed the incoming batch exactly once per call.
obj$drifter_output holds the diagnostics reported by the drifter (for
example the window width of ADWIN or the p-values of KSWIN; see the
drifter_output of each detector), with one row per row of x across every
call to fit(). Rows are NA while the drifter is not monitoring (during the
warmup, or after a drift until the model is retrained) and for observations
it skipped, such as error-based rows without a prediction.
Stealthy object
# See ?dfr_ddm for a drift detector that can be plugged here, and the # online prediction walkthroughs at # https://github.com/cefet-rj-dal/heimdall/tree/main/examples/2_online_prediction# See ?dfr_ddm for a drift detector that can be plugged here, and the # online prediction walkthroughs at # https://github.com/cefet-rj-dal/heimdall/tree/main/examples/2_online_prediction
Update Drifter State with a new observation.
update_state(obj, value, ...)update_state(obj, value, ...)
obj |
Drifter object |
value |
a value that represents a processed batch |
... |
optional arguments |
a list with two elements: obj, the updated Drifter object, and
drift, a logical flag indicating whether a drift was detected in this call
model <- dfr_ddm() output <- update_state(model, 0) output$driftmodel <- dfr_ddm() output <- update_state(model, 0) output$drift