--- title: "Regression chart for non-stationary processes" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Regression chart for non-stationary processes} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} has_data <- requireNamespace("shewhartr", quietly = TRUE) knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 4.5, eval = has_data) ``` ```{r setup} library(edetect) ``` When the in-control level depends on covariates or on time, a chart on the raw observations alarms for the wrong reason. The regression chart fits a model on Phase I data and monitors the Phase II residuals, `y - prediction`, as a sub-Gaussian stream centred at zero whose scale is the residual standard deviation of the Phase I fit. The fit, the scale and the Phase I size are recorded in the chart. The model is part of the assumptions: a residual chart tests the model as much as the process. ## A curing oven with drift and a periodic component Two hundred readings of a curing oven show a slow linear drift with a periodic component superimposed. The first hundred minutes are Phase I. ```{r data} oven <- shewhartr::temperature_drift head(oven, 3) ``` A linear model on time captures the drift but not the periodic component, and the residuals of Phase II inherit it. The chart alarms within a few minutes of Phase II, with an estimated change point at its second minute: the alarm is against the model, not against the oven. ```{r linear} linear <- edetect_regression(temp_c ~ minute, phase1 = oven[1:100, ], phase2 = oven[101:200, ], arl = 370, direction = "both") edetect_alarm(linear) ``` A model with the periodic component (period 50 minutes, read off the Phase I residuals) has a residual scale of `r round(stats::sigma(stats::lm(temp_c ~ minute + sin(2 * pi * minute / 50) + cos(2 * pi * minute / 50), oven[1:100, ])), 3)` degrees and describes Phase I well. ```{r periodic} periodic <- edetect_regression( temp_c ~ minute + sin(2 * pi * minute / 50) + cos(2 * pi * minute / 50), phase1 = oven[1:100, ], phase2 = oven[101:200, ], arl = 370, direction = "both") edetect_alarm(periodic) autoplot(periodic) ``` This chart alarms at minute 179 with the change estimated at minute 165. The residuals of minutes 161 to 180 average about 0.25 degrees above the model, two thirds of the residual scale, which is what the detector reacted to. With a target ARL of 370 and a hundred Phase II observations, a false alarm from a stable process has a probability of roughly a quarter, so the data alone cannot tell a transient excursion of the oven from a false alarm; what the guarantee promises is that such alarms are rare in the long run. ```{r periodic-report} edetect_report(periodic) ``` ## Practical notes * The residual scale is estimated from Phase I and treated as known. If Phase II residuals are more dispersed than Phase I, the sub-Gaussian assumption fails and alarms become more frequent than the guarantee says. A longer Phase I, or a declared `sigma` with a safety margin, protects against this. * A sub-Gaussian class with a declared scale is a stronger assumption than a bounded class. When residuals have a known range, `edetect_design()` with `class = "bounded"` on the residuals gives a guarantee that does not depend on the tails. * The change-point estimate is the start of the e-process that carries most of the evidence at the alarm; for a slow drift it points to where the drift became visible, not to where it began.