--- title: "Run lengths and detection delays" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Run lengths and detection delays} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 4.5) ``` ```{r setup} library(edetect) ``` Two numbers describe a chart: the **average run length to a false alarm** (ARL), which the e-detector guarantees to be at least the target for every pre-change distribution in the class, and the **detection delay** after a change, which depends on the size of the change and on the mixture of shifts the detector bets on. `edetect_arl()` estimates both by Monte Carlo, running the recursion on many streams at once. ## Checking the guarantee A sub-Gaussian design with target ARL 100 and Gaussian pre-change data. The estimate must be at least 100; it is usually well above, because the guarantee is an inequality and the e-values are conservative. ```{r arl} design <- edetect_design(arl = 100, class = "subgaussian", center = 0, sigma = 1, direction = "up") set.seed(1) no_change <- edetect_arl(design, pre = function(n) rnorm(n), n_rep = 100, max_t = 3000) no_change$summary ``` The guarantee holds for every distribution in the class, not only the Gaussian one. A bounded design on [0, 1] with centre 0.5 is checked here against three pre-change distributions with that mean: uniform, a symmetric Beta and a two-point distribution at the bounds. ```{r arl-bounded} design_b <- edetect_design(arl = 100, class = "bounded", center = 0.5, bounds = c(0, 1), direction = "up") set.seed(2) generators <- list( uniform = function(n) runif(n), beta = function(n) rbeta(n, 4, 4), two_point = function(n) rbinom(n, 1, 0.5)) sapply(generators, function(g) edetect_arl(design_b, pre = g, n_rep = 60, max_t = 3000)$summary$arl_estimate) ``` ## Measuring the delay With a change at time 50 from mean 0 to mean 1, the delay is the number of observations from the change to the alarm. Runs that alarm before the change are counted as false alarms and excluded from the delay. ```{r delay} set.seed(3) shift <- edetect_arl(design, pre = function(n) rnorm(n), post = function(n) rnorm(n, 1), change_at = 50, n_rep = 100, max_t = 500) shift$summary ``` The delay grows as the shift shrinks. A design whose mixture concentrates on small shifts detects them sooner and pays for it on large ones; the default grid spans both. ```{r delay-grid} set.seed(4) delays <- sapply(c(0.5, 1, 2), function(mu) { edetect_arl(design, pre = function(n) rnorm(n), post = function(n) rnorm(n, mu), change_at = 50, n_rep = 60, max_t = 800)$summary$delay_mean }) data.frame(shift = c(0.5, 1, 2), delay = round(delays, 1)) ``` ## Shiryaev--Roberts versus CUSUM type Both types are e-detectors with the same guarantee. The Shiryaev--Roberts type sums the e-processes started at every time; the CUSUM type keeps the largest. Their delays are close; the CUSUM type is slightly faster for changes early in the stream and the Shiryaev--Roberts type for changes after a long stable period. ```{r types} set.seed(5) types <- sapply(c("sr", "cusum"), function(type) { d <- edetect_design(arl = 100, class = "subgaussian", center = 0, sigma = 1, direction = "up", type = type) edetect_arl(d, pre = function(n) rnorm(n), post = function(n) rnorm(n, 1), change_at = 50, n_rep = 60, max_t = 500)$summary$delay_mean }) round(types, 1) ``` ## Bounded data: betting versus sub-Bernoulli e-values On bounded data two e-value families are available. The betting e-value `1 + lambda (x - centre)` is tight for data concentrated inside the bounds; the sub-Bernoulli exponential e-value is the Hoeffding bound and is slower unless the data sit at the bounds. ```{r families} set.seed(6) families <- sapply(c("betting", "hoeffding"), function(ev) { d <- edetect_design(arl = 100, class = "bounded", center = 0.5, bounds = c(0, 1), direction = "up", evalue = ev) edetect_arl(d, pre = function(n) runif(n, 0.3, 0.7), post = function(n) runif(n, 0.45, 0.85), change_at = 50, n_rep = 60, max_t = 800)$summary$delay_mean }) round(families, 1) ```