Runs a changepoint detection method on a sequence and returns a tidy
ggcpt result object. This is the recommended entry point for most
users. See cpt_methods() for the full method table with
engines and capabilities.
Usage
cpt_detect(
x,
method = "pelt",
change_in = "mean",
penalty = "MBIC",
index = NULL,
y = NULL,
...
)Arguments
- x
The series. A numeric vector for univariate methods, or a numeric matrix/data frame (rows are time points) for the multivariate methods (run
subset(cpt_methods(), multivariate)$methodfor the list). Ats,xts,zooor (unkeyed)tsibbleis accepted directly and its time index is carried through totidy()andautoplot(); so is a data frame together withy(and optionallyindex).- method
Detection method: any
methodincpt_methods()whosestatusis"available"or"registered". Methods whose engines live inSuggestsreport what to install when missing;cpt_register_method()adds detectors this package does not wrap.- change_in
What to detect change in. One of
"mean","var","meanvar","slope","distribution","covariance","network","regression"or"seasonality". Defaults to"mean". The requested value is validated against the method's capabilities (seecpt_methods()); incompatible combinations error rather than silently running something else.A compatible request may still be routed to the method's own native change type, because several engines have no separate estimator for the thing being asked about. That is never silent: the result's
change_inrecords what was actually detected, so compare it with what you asked for. Measured across every method and every value itssupportsentry lists, six pairs are routed:not's"var"becomes"meanvar"(its variance contrast is piecewise-constant in mean and variance),cpm's"mean"and"var"both become"distribution",kcp's become"running mean"and"running var", andwbsts's"mean"becomes"var"(it detects change in the wavelet spectrum). Every other listed combination returns the change type it was asked for.- penalty
Penalty type or value. Either a character string (
"MBIC","BIC","SIC","AIC","Hannan-Quinn","None") or a numeric penalty value. Defaults to"MBIC". See the penalty-semantics section ofcpt_penaltyfor how each engine interprets it; methods that use thresholds, significance levels, or posteriors instead of penalties ignore this argument, and"segneigh"falls back to"SIC"because changepoint does not implement MBIC for Segment Neighbourhood. Note also that the default"MBIC"is resolved to a numeric value for the numeric-penalty engines ("fpop","cpop","decafs"), and that value is stronger than those wrappers' own2 * log(n)default (19.9 against 11.8 at \(n = 360\)), socpt_detect(x, method = "decafs")can report fewer changepoints thandecafs_wrapper(x)on the same series. Passpenaltyexplicitly to make the two entry points agree.- index
Optional time index, one value per observation (dates, say). Detection still runs on observation positions (every wrapped engine assumes an equally spaced sequence), but the index is stored on the result and threaded through
tidy()(ascp_index),augment(),autoplot()andcpt_report(), so the output speaks in the user's own units. An index that is not equally spaced warns. Whenxis a data frame andyis given,indexselects a column of that data frame instead of being a vector.- y
Column selection for the data-frame interface:
cpt_detect(df, y = value, index = date, method = "pelt"). A bare column name, a string, or a column position. Only meaningful whenxis a data frame; a data frame passed withoutykeeps its 0.4.0 meaning (one column per coordinate).- ...
Additional arguments passed to the specific wrapper (see the wrapper's help page for engine-specific options). Where an argument is also derived from
change_in(not'scontrast,cpm'scpm_type,kcp'srunning_stat,sn'sparameter,fastcpd'sfamily), a value supplied here takes precedence. Check the spelling against the wrapper's help page: several engines end their own signature in...(wbs, not, Rbeast, strucchange, segmented, fastcpd, fChange, bfast), so for those a misspelt argument name is silently discarded upstream and the engine quietly uses its default rather than reporting the typo. Every other wired method rejects an unknown argument by name.
Value
A ggcpt object: a list with changepoints
(cp, cp_value), segments (seg_id,
start, end, n, param_estimate),
data (index, value), the method,
change_in, penalty, cp_convention and
runtime that produced it, the matched call, and
fit, the raw upstream object. Optional slots
(data_wide, regions, diagnostics, ...) appear
only when an engine supplies them; new_ggcpt()
documents all of them, and tidy.ggcpt(),
glance.ggcpt() and augment.ggcpt()
are the supported way to read one.
Scale sensitivity of the penalised change-in-mean engines
"pelt", "binseg", "segneigh" and "fpop"
compare a penalty against a raw segment cost when
change_in = "mean": changepoint's Normal cost assumes a
noise standard deviation of 1, and fpop's lambda is an
absolute penalty on the residual sum of squares. Neither rescales the
data, so on a series whose noise is much wider than 1 the penalty is
effectively negligible and the segmentation shatters. On 200 observations
with one true changepoint in the middle and a jump of five standard
deviations, "pelt" returns 1 changepoint at \(\sigma = 1\), 39
at \(\sigma = 3\) and 141 at \(\sigma = 10\). These are means over 20
draws, because a single draw is not stable here: the same three settings
gave 21/75 at \(n = 100\) and 57/266 at \(n = 400\), so the effect
grows with the series as well as with the noise. Three ways to avoid it, in order of
convenience:
standardise the series first (
cpt_detect(scale(x)[, 1], method = "pelt"));pass a penalty on the data's own scale, for example
penalty = 2 * log(length(x)) * stats::var(diff(x)) / 2;use
change_in = "meanvar", which estimates a variance per segment and is unaffected.
Most other engines are unaffected: SMUCE, WBS, WBS2, NOT, MOSUM,
Isolate-Detect, TGUH, CPOP, "bcp", "beast" and the
nonparametric and multivariate methods estimate or cancel the noise scale
internally, and returned the same segmentation at a thousandth, one and
a thousand times the units. Three did not, on the same series:
"geomcp" runs PELT on its mapped distance and angle series and so
inherits the sensitivity above; "decafs" floors its noise
estimate at about 0.03, so it under-segments a series whose noise is
smaller than that; and "bocpd"'s default prior is on the data's
own scale. At a thousandth of the units the last two found nothing.
Standardising first avoids all three.
See also
cpt_methods() for what is available and what each
method can do. To get the result out: tidy.ggcpt(),
glance.ggcpt(), augment.ggcpt(),
summary.ggcpt() and print.ggcpt(). To
draw it: autoplot.ggcpt(). For the penalty:
cpt_penalty().
Examples
set.seed(2022)
x <- c(rnorm(100, 0, 1), rnorm(100, 10, 1))
result <- cpt_detect(x, method = "pelt", change_in = "mean")
result
#> ggcpt (changepoint detection result)
#> Method: pelt
#> Change in: mean
#> Changepoints found: 1
#> CP convention: left
#> Penalty: MBIC
#> Series length: 200
#>
#> Changepoints:
#> # A tibble: 1 × 2
#> cp cp_value
#> <int> <dbl>
#> 1 100 0.467
ggplot2::autoplot(result)
# A date index: detection is unchanged, but the report speaks in dates.
dates <- as.Date("2000-01-01") + 0:199
dated <- cpt_detect(x, method = "pelt", index = dates)
tidy(dated)
#> # A tibble: 1 × 3
#> cp cp_index cp_value
#> <int> <date> <dbl>
#> 1 100 2000-04-09 0.467
# The data-frame interface.
df <- data.frame(day = dates, value = x)
cpt_detect(df, y = value, index = day, method = "pelt")
#> ggcpt (changepoint detection result)
#> Method: pelt
#> Change in: mean
#> Changepoints found: 1
#> CP convention: left
#> Penalty: MBIC
#> Series length: 200
#> Index: 2000-01-01 to 2000-07-18
#>
#> Changepoints:
#> # A tibble: 1 × 3
#> cp cp_index cp_value
#> <int> <date> <dbl>
#> 1 100 2000-04-09 0.467
