The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
ggchangepoint provides a unified, tidy,
ggplot2-native interface to changepoint detection in R: one
dispatcher (cpt_detect()) covering 31 methods across six
methodological families, one result class (ggcpt) with a
stable tidy contract, and one visualisation entry point
(autoplot()) that draws everything a method reports —
including confidence intervals and posterior probabilities (Wickham 2016; Robinson 2017). This vignette is
the feature tour: it visits every exported
function in the package at the point where it belongs in the
workflow, so a reader can map the full surface in one sitting. The
companion vignettes develop the methodology in depth
(vignette("introduction")) and treat method comparison and
evaluation (vignette("comparison")).
Every detector returns a ggcpt object: a list carrying
the tidy changepoints tibble (cp = last index
of the left segment, cp_value = series value there, plus
any method-specific columns), a segments table, the
data, the raw engine fit, and metadata
(method, change_in, penalty, convention, runtime).
new_ggcpt() is the low-level constructor and
is_ggcpt() the class test; most users never call either
directly.
res <- cpt_detect(x, method = "pelt", change_in = "mean")
is_ggcpt(res)
#> [1] TRUE
print(res)
#> 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.369manual <- new_ggcpt(
changepoints = tibble::tibble(cp = 100L, cp_value = x[100]),
data = tibble::tibble(index = seq_along(x), value = x),
method = "manual"
)
is_ggcpt(manual)
#> [1] TRUEThe broom verbs give one row per changepoint
(tidy()), a one-row model summary (glance()),
and the data augmented with segment ids, fitted levels, residuals, and a
changepoint flag (augment()):
tidy(res)
#> # A tibble: 1 × 2
#> cp cp_value
#> <int> <dbl>
#> 1 100 0.369
glance(res)
#> # A tibble: 1 × 9
#> n n_changepoints method change_in penalty_type penalty_value cp_convention
#> <int> <int> <chr> <chr> <chr> <dbl> <chr>
#> 1 200 1 pelt mean MBIC NA left
#> # ℹ 2 more variables: total_cost <dbl>, runtime <dbl>
head(augment(res))
#> # A tibble: 6 × 6
#> index value seg_id .fitted .resid is_changepoint
#> <int> <dbl> <int> <dbl> <dbl> <lgl>
#> 1 1 0.521 1 -0.0980 0.619 FALSE
#> 2 2 -1.08 1 -0.0980 -0.982 FALSE
#> 3 3 0.139 1 -0.0980 0.237 FALSE
#> 4 4 -0.0847 1 -0.0980 0.0133 FALSE
#> 5 5 -0.667 1 -0.0980 -0.569 FALSE
#> 6 6 -2.52 1 -0.0980 -2.42 FALSEThe remaining S3 surface: a human-readable digest, tibble/data-frame
coercion, a one-line format, and a base-plot() fallback
that delegates to autoplot().
summary(res)
#> ggcpt Summary
#> Method: pelt
#> Change in: mean
#> Changepoints found: 1
#> CP convention: left
#> Series length: 200
#> Penalty: MBIC
#> Runtime (seconds): 0.008
#>
#> Segments:
#> # A tibble: 2 × 5
#> seg_id start end n param_estimate
#> <int> <int> <int> <int> <dbl>
#> 1 1 1 100 100 -0.0980
#> 2 2 101 200 100 6.12
#>
#> Changepoints:
#> # A tibble: 1 × 2
#> cp cp_value
#> <int> <dbl>
#> 1 100 0.369
as_tibble(res)
#> # A tibble: 1 × 2
#> cp cp_value
#> <int> <dbl>
#> 1 100 0.369
head(as.data.frame(res))
#> cp cp_value
#> 1 100 0.3694259
format(res)
#> [1] "ggcpt [pelt] 1 changepoint(s) on 200 observations"cpt_detect() is the recommended entry point: pick a
method, say what the change is in (change_in =
"mean", "var", "meanvar",
"slope", or "distribution"), and optionally
set a penalty. Incompatible
method/change_in combinations error — they are
never silently substituted.
cpt_detect(x, method = "binseg", change_in = "mean")
#> ggcpt (changepoint detection result)
#> Method: binseg
#> 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.369Anything else in ... reaches the underlying wrapper, and
takes precedence over the value the dispatcher would otherwise derive
from change_in — here the NOT contrast is set directly
rather than inherited:
tidy(cpt_detect(x, method = "not", contrast = "pcwsLinMean"))
#> # A tibble: 1 × 2
#> cp cp_value
#> <int> <dbl>
#> 1 100 0.369cpt_methods() is the live capability table: every method
the package knows, its engine package, what it can detect, and whether
the engine is installed. Four rows carry status "planned"
rather than "available": their engines (gfpop,
robseg, FOCuS, hdbinseg) are not
on CRAN, so they are documented as future work and are not
wired to cpt_detect().
print(cpt_methods(), n = Inf)
#> # A tibble: 35 × 6
#> method change_in engine status target_release installed
#> <chr> <chr> <chr> <chr> <chr> <lgl>
#> 1 pelt mean, var, meanvar chang… avail… <NA> TRUE
#> 2 binseg mean, var, meanvar chang… avail… <NA> TRUE
#> 3 segneigh mean, var, meanvar chang… avail… <NA> TRUE
#> 4 amoc mean, var, meanvar chang… avail… <NA> TRUE
#> 5 np distribution chang… avail… <NA> TRUE
#> 6 ecp distribution (multivariat… ecp avail… <NA> TRUE
#> 7 fpop mean fpop avail… <NA> TRUE
#> 8 wbs mean wbs avail… <NA> TRUE
#> 9 wbs2 mean break… avail… <NA> TRUE
#> 10 not mean, var, slope not avail… <NA> TRUE
#> 11 mosum mean mosum avail… <NA> TRUE
#> 12 idetect mean IDete… avail… <NA> TRUE
#> 13 tguh mean break… avail… <NA> TRUE
#> 14 smuce mean (with CIs) stepR avail… <NA> FALSE
#> 15 hsmuce mean (heteroskedastic, wi… stepR avail… <NA> FALSE
#> 16 cpop slope cpop avail… <NA> FALSE
#> 17 bcp mean (Bayesian) bcp avail… <NA> FALSE
#> 18 bocpd mean (Bayesian online) ocp avail… <NA> FALSE
#> 19 beast mean/trend (Bayesian) Rbeast avail… <NA> FALSE
#> 20 cpm distribution (sequential) cpm avail… <NA> FALSE
#> 21 kcp running statistics (kerne… kcpRS avail… <NA> FALSE
#> 22 npmojo distribution (multivariat… CptNo… avail… <NA> FALSE
#> 23 decafs mean (drift + AR noise) DeCAFS avail… <NA> FALSE
#> 24 sn mean, var, acf, correlati… SNSeg avail… <NA> FALSE
#> 25 inspect mean (high-dimensional) Inspe… avail… <NA> FALSE
#> 26 ocd mean (high-dimensional, o… ocd avail… <NA> FALSE
#> 27 geomcp distribution (multivariat… chang… avail… <NA> FALSE
#> 28 strucchange mean, regression (with CI… struc… avail… <NA> FALSE
#> 29 segmented slope (with CIs) segme… avail… <NA> FALSE
#> 30 envcpt mean/trend vs autocorrela… EnvCpt avail… <NA> FALSE
#> 31 fastcpd mean, var, meanvar, AR/AR… fastc… avail… <NA> FALSE
#> 32 gfpop mean (graph-constrained) gfpop plann… when on CRAN NA
#> 33 robust mean (robust loss) robseg plann… when on CRAN NA
#> 34 focus mean (online) FOCuS plann… when on CRAN NA
#> 35 sbs mean (high-dimensional) hdbin… plann… next release NAcpt_penalty() constructs standard penalty values for the
engines that take numeric penalties; see its help for the per-engine
penalty semantics.
Each engine also has a direct wrapper exposing its native arguments.
Every wrapper from 0.2.0 onwards returns a ggcpt object;
only the two original wrappers below predate the class and still return
a bare tibble.
cpt_wrapper() and ecp_wrapper() are the
original interface to the changepoint/changepoint.np
(Killick, Fearnhead, and Eckley 2012; Killick and
Eckley 2014; Haynes, Fearnhead, and Eckley 2017) and ecp
(Matteson and James 2014; James and Matteson
2014) engines; they return bare tibbles for backward
compatibility.
Seven multiscale, randomised, and functional-pruning engines (Fryzlewicz 2014, 2020, 2018; Baranowski, Chen, and Fryzlewicz 2019; Eichinger and Kirch 2018; Anastasiou and Fryzlewicz 2022; Maidstone et al. 2017), one call each:
fpop_wrapper(x, penalty = 2 * log(length(x)))
#> ggcpt (changepoint detection result)
#> Method: fpop
#> Change in: mean
#> Changepoints found: 1
#> CP convention: left
#> Penalty: Manual = 10.597
#> Series length: 200
#>
#> Changepoints:
#> # A tibble: 1 × 2
#> cp cp_value
#> <int> <dbl>
#> 1 100 0.369wbs_wrapper(x, n_intervals = 2000, seed = 1)
#> ggcpt (changepoint detection result)
#> Method: wbs
#> Change in: mean
#> Changepoints found: 1
#> CP convention: left
#> Penalty: sSIC
#> Series length: 200
#>
#> Changepoints:
#> # A tibble: 1 × 2
#> cp cp_value
#> <int> <dbl>
#> 1 100 0.369wbs2_wrapper(x)
#> ggcpt (changepoint detection result)
#> Method: wbs2
#> Change in: mean
#> Changepoints found: 1
#> CP convention: left
#> Penalty: SDLL
#> Series length: 200
#>
#> Changepoints:
#> # A tibble: 1 × 2
#> cp cp_value
#> <int> <dbl>
#> 1 100 0.369not_wrapper(x, contrast = "pcwsConstMean", seed = 1)
#> ggcpt (changepoint detection result)
#> Method: not
#> Change in: mean
#> Changepoints found: 1
#> CP convention: left
#> Penalty: sSIC
#> Series length: 200
#>
#> Changepoints:
#> # A tibble: 1 × 2
#> cp cp_value
#> <int> <dbl>
#> 1 100 0.369mosum_wrapper(x)
#> ggcpt (changepoint detection result)
#> Method: mosum
#> Change in: mean
#> Changepoints found: 1
#> CP convention: left
#> Penalty: threshold = 3.6342
#> Series length: 200
#>
#> Changepoints:
#> # A tibble: 1 × 2
#> cp cp_value
#> <int> <dbl>
#> 1 100 0.369
mosum_wrapper(x3, multiscale = TRUE)
#> ggcpt (changepoint detection result)
#> Method: mosum
#> Change in: mean
#> Changepoints found: 2
#> CP convention: left
#> Penalty: threshold
#> Series length: 300
#>
#> Changepoints:
#> # A tibble: 2 × 2
#> cp cp_value
#> <int> <dbl>
#> 1 100 -0.100
#> 2 200 2.90Multiscale inference with confidence.
smuce_wrapper() implements SMUCE (Frick, Munk, and Sieling 2014), which bounds
the probability of over-estimating the number of changes at the level
alpha and returns a confidence interval for every
changepoint location (ci_lower/ci_upper);
family = "hsmuce" switches to the heteroskedastic extension
HSMUCE (Pein, Sieling, and Munk 2017),
which estimates a variance per segment:
Change in slope. cpop_wrapper()
performs exact penalised estimation of a continuous piecewise-linear
mean (Fearnhead, Maidstone, and Letchford 2019;
Fearnhead and Grose 2024):
y_slope <- cumsum(c(rep(0.4, 100), rep(-0.3, 100))) + rnorm(200)
res_cpop <- cpop_wrapper(y_slope)
tidy(res_cpop)Bayesian detection. bcp_wrapper()
implements the Barry–Hartigan product-partition model (Barry and Hartigan 1993; Erdman and Emerson
2007); bocpd_wrapper() runs Bayesian online
changepoint detection over the run-length posterior (Adams and MacKay 2007);
beast_wrapper() wraps the BEAST Bayesian model-averaging
ensemble (K. Zhao et al. 2019). The first
and third keep the locations whose posterior probability clears
prob_threshold and record it in a
posterior_prob column; BOCPD returns the maximum a
posteriori changepoint set.
Sequential and kernel nonparametrics.
cpm_wrapper() runs distribution-free sequential tests and
reports, alongside each estimated location, the
detection_time at which a stream monitor would have flagged
it (Ross 2015); kcp_wrapper()
applies kernel change point analysis to running statistics (mean,
variance, autocorrelation, correlation) (Arlot,
Celisse, and Harchaoui 2019; Cabrieto et al. 2018);
npmojo_wrapper() detects distributional changes under
serial dependence (McGonigle and Cho
2025):
Robustness to drift and dependence.
decafs_wrapper() detects abrupt changes when the signal
also drifts and the noise is autocorrelated (Romano et al. 2022); sn_wrapper()
segments a chosen parameter (mean, variance,
autocorrelation, or bivariate correlation) by self-normalisation,
avoiding long-run variance estimation entirely (Z. Zhao, Jiang, and Shao 2022);
envcpt_wrapper() reports changepoints only when a
changepoint model beats the trend and autoregressive alternatives (Beaulieu and Killick 2018):
res_env <- envcpt_wrapper(x, models = c("mean", "meancpt", "trendcpt"))
tidy(res_env)
res_env$penalty$type # criterion and winning modelHigh-dimensional and multivariate. These wrappers
take a matrix or data frame with one row per time point.
inspect_wrapper() finds sparse mean changes by projection
(Wang and Samworth 2018);
ocd_wrapper() monitors a high-dimensional stream online
(Chen, Wang, and Samworth 2022);
geomcp_wrapper() maps each observation to a distance and an
angle and segments both (Grundy, Killick, and
Mihaylov 2020); multivariate ecp input flows through
cpt_detect() unchanged. The univariate wrappers, by
contrast, reject a multi-column argument rather than silently flattening
it.
set.seed(1)
X <- cbind(a = c(rnorm(80), rnorm(80, 4)),
b = c(rnorm(80), rnorm(80, -3)),
c = rnorm(160))# Online detection: the reported locations are declaration times (the change
# plus the detection delay), in a `declared_at` column. Monte Carlo threshold
# calibration makes this the slowest wrapper, so it is shown but not run
# here. It needs at least two coordinates and rejects a univariate series.
res_ocd <- ocd_wrapper(X, mc_reps = 100)
tidy(res_ocd)tidy(cpt_detect(X, method = "ecp", seed = 1))
#> # A tibble: 1 × 2
#> cp cp_value
#> <int> <dbl>
#> 1 80 -0.590Regression structure.
strucchange_wrapper() dates Bai–Perron breaks, either in a
bare series or in the coefficients of a formula (Bai and Perron 1998, 2003; Zeileis et al.
2002); segmented_wrapper() fits a continuous
broken-line regression, so the change it reports is a kink in the trend
rather than a jump in the level (Muggeo 2003,
2008). Both carry a confidence interval for every break:
The modern PELT family.
fastcpd_wrapper() exposes the fastcpd engine,
whose family argument covers changes in the mean, the
variance, or both, as well as changes in a fitted AR/ARMA/GARCH model
(Li and Zhang 2024):
Rather than guessing one penalty, cpt_crops() computes
every optimal segmentation over a penalty interval (the CROPS
algorithm, via changepoint (Killick and
Eckley 2014)) and returns a ggcpt_path object with
its own print(), tidy(), and three
autoplot() types:
path <- cpt_crops(x3)
path
#> ggcpt_path (CROPS penalty path)
#> Change in: mean
#> Penalty range: [5.704, 57.04]
#> Series length: 300
#> Distinct segmentations: 5
#>
#> # A tibble: 5 × 3
#> penalty n_cpts cost
#> <dbl> <int> <dbl>
#> 1 6.69 2 327.
#> 2 6.27 5 307.
#> 3 6.19 8 288.
#> 4 5.76 9 282.
#> 5 5.70 10 276.
tidy(path)
#> # A tibble: 5 × 4
#> penalty n_cpts cost cpts
#> <dbl> <int> <dbl> <list>
#> 1 6.69 2 327. <int [2]>
#> 2 6.27 5 307. <int [5]>
#> 3 6.19 8 288. <int [8]>
#> 4 5.76 9 282. <int [9]>
#> 5 5.70 10 276. <int [10]>autoplot() renders any ggcpt. Options:
show_segments (fitted segment means), show_fit
(the engine’s own fitted signal, where provided — SMUCE, DeCAFS, CPOP,
segmented, bcp, BEAST), show_ci (changepoint-location
confidence intervals, where provided — SMUCE/HSMUCE, strucchange,
segmented), show_points/show_line, an
index for a date axis, and the cptline_*
styling arguments (cptline_color,
cptline_alpha, cptline_type,
cptline_linewidth). Asking for an overlay the result cannot
supply warns rather than failing silently.
autoplot(res, show_segments = TRUE, cptline_color = "firebrick",
cptline_type = "dashed", cptline_linewidth = 0.8)Multivariate results facet automatically:
The composable layers work inside any ggplot pipeline:
geom_changepoint() draws vertical rules,
geom_cpt_segment() draws segment levels,
geom_cpt_ci() draws horizontal interval whiskers, and
stat_changepoint() runs detection inside the plot.
theme_ggcpt() is a publication theme and
annotate_segments() shades alternating segments.
cp_tbl <- tidy(res)
df <- data.frame(index = seq_along(x), value = x)
ggplot(df, aes(index, value)) +
annotate_segments(cp = cp_tbl$cp, n = length(x)) +
geom_line() +
geom_changepoint(data = cp_tbl, aes(xintercept = cp), color = "red") +
geom_cpt_segment(
data = res$segments,
aes(x = start, xend = end, y = param_estimate, yend = param_estimate),
inherit.aes = FALSE, color = "darkred", linewidth = 1
) +
theme_ggcpt()ci_tbl <- tidy(res_smuce)
ci_tbl$y_pos <- min(x) - 1
ggplot(df, aes(index, value)) +
geom_line(color = "grey60") +
geom_cpt_ci(
data = ci_tbl,
aes(y = y_pos, xmin = ci_lower, xmax = ci_upper),
width = 0.6, color = "blue", inherit.aes = FALSE
) +
geom_changepoint(data = ci_tbl, aes(xintercept = cp), color = "blue")ggcptplot() and ggecpplot() are the
original one-call plots for the two classical engines:
The Bayesian engines get the field’s signature displays:
ggcpt_posterior() shows the posterior mean over the series
and the per-location changepoint probability (for bcp and
BEAST results); ggcpt_runlength() shows the BOCPD
run-length posterior as a heatmap.
Finally, ggcpt_interactive() turns any result (or any
ggplot built from one) into a plotly widget with values on
hover. The widget itself is not embedded here, only its class:
ggcpt_compare() runs several detectors on the same
series and renders them faceted (default) or overlaid, honouring
future::plan() for parallel execution;
ggcpt_compare_table() returns the tidy union of the same
runs. A method that finds nothing keeps its panel and contributes an
NA row — “no changepoints” is a result, not a missing
one.
cpt_batch() runs one detector over many series (matrix,
data frame, or list of vectors), also under future::plan(),
and returns a tidy ggcpt_batch tibble with list-columns for
the per-series changepoints and the ggcpt objects
themselves:
XB <- cbind(shifted = x, pure_noise = rnorm(200))
batch <- cpt_batch(XB, method = "pelt")
batch
#> ggcpt_batch (2 series, method: pelt)
#>
#> # A tibble: 2 × 2
#> series n_changepoints
#> <chr> <int>
#> 1 shifted 1
#> 2 pure_noise 0
tidy(batch)
#> # A tibble: 1 × 3
#> series cp cp_value
#> <chr> <int> <dbl>
#> 1 shifted 100 0.369
autoplot(batch)cpt_stability() quantifies how fragile a segmentation
is: it resamples residuals within the fitted segments, re-runs the
detector, and reports the re-detection frequency at every location — a
model-agnostic confidence signal for engines with no native
intervals:
st <- cpt_stability(x, method = "pelt", B = 30, seed = 1)
st
#> ggcpt_stability (30 bootstrap replicates, method: pelt)
#>
#> Original changepoints and their re-detection frequency:
#> # A tibble: 1 × 2
#> cp stability
#> <int> <dbl>
#> 1 100 1
autoplot(st)cpt_metrics() scores predictions against a known truth:
precision, recall and F1 under one-to-one matching, the covering metric
and adjusted Rand index in the conventions of van
den Burg and Williams (2020), Hausdorff distance, annotation
error, and matched MAE/RMSE. cpt_metrics_annotated()
averages over multiple annotators, and ggcpt_eval() draws
the agreement (true positives, false positives, and misses):
truth <- 100
pred <- tidy(res)$cp
cpt_metrics(pred, truth, n = length(x), margin = 5)
#> # A tibble: 1 × 12
#> n n_pred n_truth precision recall f1 covering hausdorff rand_index
#> <int> <int> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 200 1 1 1 1 1 1 0 1
#> # ℹ 3 more variables: annotation_error <int>, mae_matched <dbl>,
#> # rmse_matched <dbl>
cpt_metrics_annotated(pred, list(100, 101, 99), n = length(x))
#> # A tibble: 1 × 7
#> n n_annotators n_pred precision recall f1 covering
#> <int> <int> <int> <dbl> <dbl> <dbl> <dbl>
#> 1 200 3 1 1 1 1 0.993cpt_simulate() (alias rcpt()) generates
series with known changepoints in mean, variance, both, or slope, under
Gaussian, Student-t, AR(1), or random-walk noise; the truth travels with
the data in the true_changepoints and
true_segments attributes. Five canonical test signals from
the literature ship ready-made: signal_blocks() (the
Donoho–Johnstone blocks signal (Donoho and
Johnstone 1994)), signal_fms(),
signal_mix(), signal_teeth(), and
signal_stairs().
sim <- cpt_simulate(300, changepoints = c(100, 200), change_in = "mean",
params = c(0, 5, 1), seed = 1)
attr(sim, "true_changepoints")
#> [1] 100 200
sim2 <- rcpt(300, changepoints = 150, params = c(0, 3), seed = 2) # the alias
attr(sim2, "true_changepoints")
#> [1] 150
signals <- list(blocks = signal_blocks(1024, seed = 1),
fms = signal_fms(500, seed = 1),
mix = signal_mix(500, seed = 1),
teeth = signal_teeth(400, seed = 1),
stairs = signal_stairs(500, seed = 1))
vapply(signals, function(s) length(attr(s, "true_changepoints")), integer(1))
#> blocks fms mix teeth stairs
#> 11 7 5 3 9blocks <- signals$blocks
ggplot(blocks, aes(index, value)) +
geom_line(color = "grey50") +
geom_vline(xintercept = attr(blocks, "true_changepoints"),
color = "blue", linewidth = 0.3) +
labs(title = "The blocks test signal with its true changepoints")cpt_cite() returns the verified reference(s) behind a
result or a method name, so a write-up can cite the right paper without
leaving R; called with no argument it returns the whole
method-to-reference table.
cpt_cite("pelt")
#> [pelt] Killick, R., Fearnhead, P. and Eckley, I. A. (2012). Optimal detection of changepoints with a linear computational cost. Journal of the American Statistical Association, 107(500), 1590-1598.
cpt_cite(res)
#> [pelt] Killick, R., Fearnhead, P. and Eckley, I. A. (2012). Optimal detection of changepoints with a linear computational cost. Journal of the American Statistical Association, 107(500), 1590-1598.This tour visited every exported function in the package. For the
framework’s design, the mathematics of the wrapped methods, and worked
analyses, see
vignette("introduction", package = "ggchangepoint"); for
method comparison and accuracy evaluation in depth, see
vignette("comparison", package = "ggchangepoint").
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.