Author: Youzhi Yu University of Chicago

# The ggchangepoint Feature Tour: A Complete Map of the Package Surface

#### Youzhi Yu
University of Chicago

# Abstract

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")`).

    set.seed(2026)
    x <- c(rnorm(100, 0, 1), rnorm(100, 6, 1))    # one mean shift at t = 100
    x3 <- c(rnorm(100), rnorm(100, 4), rnorm(100, 1))  # shifts at 100, 200

# The result object and its methods

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.369

    manual <- 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] TRUE

The `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   FALSE

The 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"

    plot(res)

# Unified detection

`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.369

Anything 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.369

`cpt_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   NA

`cpt_penalty()` constructs standard penalty values for the engines that take numeric penalties; see its help for the per-engine penalty semantics.

    cpt_penalty("BIC", n = 200)
    #> [1] 5.298317
    cpt_penalty("MBIC", n = 200)
    #> [1] 10.59663
    cpt_penalty("Manual", value = 10)
    #> [1] 10

# Engine wrappers

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.

## The classical core (0.1.0)

`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.

    cpt_wrapper(x, change_in = "mean", cp_method = "PELT")
    #> # A tibble: 1 × 2
    #>      cp cp_value
    #>   <int>    <dbl>
    #> 1   100    0.369
    ecp_wrapper(x, algorithm = "divisive", seed = 1)
    #> # A tibble: 1 × 2
    #>      cp cp_value
    #>   <dbl>    <dbl>
    #> 1   101     7.22

## The search and pruning wave (0.2.0)

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.369

    wbs_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.369

    wbs2_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.369

    not_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.369

    mosum_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.90

    idetect_wrapper(x, seed = 1)
    #> ggcpt (changepoint detection result)
    #>   Method:          idetect
    #>   Change in:       mean
    #>   Changepoints found: 1
    #>   CP convention:   left
    #>   Penalty:         threshold
    #>   Series length:   200
    #>
    #> Changepoints:
    #> # A tibble: 1 × 2
    #>      cp cp_value
    #>   <int>    <dbl>
    #> 1   100    0.369

    tguh_wrapper(x)
    #> ggcpt (changepoint detection result)
    #>   Method:          tguh
    #>   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.369

## The engine wave (0.4.0)

Multiscale 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:

    res_smuce <- smuce_wrapper(x, alpha = 0.5)
    tidy(res_smuce)

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.

    res_bcp <- bcp_wrapper(x, seed = 1)
    tidy(res_bcp)

    res_bocpd <- bocpd_wrapper(x)
    tidy(res_bocpd)

    res_beast <- beast_wrapper(x, seed = 1)
    tidy(res_beast)

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):

    tidy(cpm_wrapper(x, cpm_type = "Mann-Whitney"))

    tidy(kcp_wrapper(x, running_stat = "mean", nperm = 100, seed = 1))

    tidy(npmojo_wrapper(x))

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):

    tidy(decafs_wrapper(x))

    tidy(sn_wrapper(x3, parameter = "mean"))

    res_env <- envcpt_wrapper(x, models = c("mean", "meancpt", "trendcpt"))
    tidy(res_env)
    res_env$penalty$type   # criterion and winning model

High-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))

    res_hd <- inspect_wrapper(X)
    tidy(res_hd)

    # 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(geomcp_wrapper(X))

    tidy(cpt_detect(X, method = "ecp", seed = 1))
    #> # A tibble: 1 × 2
    #>      cp cp_value
    #>   <int>    <dbl>
    #> 1    80   -0.590

Regression 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:

    tidy(strucchange_wrapper(x))

    tidy(segmented_wrapper(y_slope, npsi = 1, seed = 1))

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):

    tidy(fastcpd_wrapper(x, family = "mean"))

# The penalty path

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(path)                          # the cost elbow

    autoplot(path, type = "path")           # changepoints vs penalty

    autoplot(path, type = "segmentations")  # the candidate models themselves

# The visualisation layer

`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)

    autoplot(res_smuce, show_ci = TRUE, show_fit = TRUE)

Multivariate results facet automatically:

    autoplot(res_hd)

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()

    ggplot(df, aes(index, value)) +
      geom_line() +
      stat_changepoint(method = "pelt", color = "blue")

    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:

    ggcptplot(x, change_in = "mean", cp_method = "PELT")

    ggecpplot(x, algorithm = "divisive", seed = 1)

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.

    ggcpt_posterior(res_bcp)

    ggcpt_runlength(res_bocpd)

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:

    class(ggcpt_interactive(res))   # requires plotly
    #> [1] "plotly"     "htmlwidget"

# Comparing methods

`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.

    ggcpt_compare(x, methods = c("pelt", "binseg", "amoc"))

    ggcpt_compare(x, methods = c("pelt", "binseg"), layout = "overlay")

    ggcpt_compare_table(x, methods = c("pelt", "binseg", "amoc"))
    #> # A tibble: 3 × 3
    #>   method    cp cp_value
    #>   <chr>  <int>    <dbl>
    #> 1 pelt     100    0.369
    #> 2 binseg   100    0.369
    #> 3 amoc     100    0.369

# Batch detection and stability

`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)

# Evaluation against ground truth

`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.993

    ggcpt_eval(pred, truth, x, margin = 5)

# Simulation and canonical signals

`cpt_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      9

    blocks <- 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")

# Citing the methodology

`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.

# Closing note

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")`.

# References

Adams, Ryan Prescott, and David JC MacKay. 2007. “Bayesian Online Changepoint Detection.” arXiv Preprint arXiv:0710.3742.
Anastasiou, Andreas, and Piotr Fryzlewicz. 2022. “Detecting Multiple Generalized Change-Points by Isolating Single Ones.” Metrika 85: 141–74. https://doi.org/10.1007/s00184-021-00821-6.
Arlot, Sylvain, Alain Celisse, and Zaid Harchaoui. 2019. “A Kernel Multiple Change-Point Algorithm via Model Selection.” Journal of Machine Learning Research 20 (162): 1–56.
Bai, Jushan, and Pierre Perron. 1998. “Estimating and Testing Linear Models with Multiple Structural Changes.” Econometrica 66 (1): 47–78.
———. 2003. “Computation and Analysis of Multiple Structural Change Models.” Journal of Applied Econometrics 18 (1): 1–22.
Baranowski, Rafał, Yining Chen, and Piotr Fryzlewicz. 2019. “Narrowest-over-Threshold Detection of Multiple Change Points and Change-Point-Like Features.” Journal of the Royal Statistical Society Series B 81 (3): 649–72. https://doi.org/10.1111/rssb.12322.
Barry, Daniel, and John A Hartigan. 1993. “A Bayesian Analysis for Change Point Problems.” Journal of the American Statistical Association 88 (421): 309–19.
Beaulieu, Claudie, and Rebecca Killick. 2018. “Distinguishing Trends and Shifts from Memory in Climate Data.” Journal of Climate 31 (23): 9519–43.
Cabrieto, Jedelyn, Janne Adolf, Francis Tuerlinckx, Peter Kuppens, and Eva Ceulemans. 2018. “Detecting Long-Lived Autodependency Changes in a Multivariate System via Change Point Detection and Regime Switching Models.” Scientific Reports 8: 15637.
Chen, Yudong, Tengyao Wang, and Richard J Samworth. 2022. “High-Dimensional, Multiscale Online Changepoint Detection.” Journal of the Royal Statistical Society: Series B 84 (1): 234–66.
Donoho, David L, and John M Johnstone. 1994. “Ideal Spatial Adaptation by Wavelet Shrinkage.” Biometrika 81 (3): 425–55. https://doi.org/10.1093/biomet/81.3.425.
Eichinger, Birte, and Claudia Kirch. 2018. “A MOSUM Procedure for the Estimation of Multiple Random Change Points.” Bernoulli 24 (1): 526–64. https://doi.org/10.3150/16-BEJ887.
Erdman, Chandra, and John W Emerson. 2007. “Bcp: An r Package for Performing a Bayesian Analysis of Change Point Problems.” Journal of Statistical Software 23 (3): 1–13.
Fearnhead, Paul, and Daniel Grose. 2024. “Cpop: Detecting Changes in Piecewise-Linear Signals.” Journal of Statistical Software 109 (7): 1–30.
Fearnhead, Paul, Robert Maidstone, and Adam Letchford. 2019. “Detecting Changes in Slope with an L0 Penalty.” Journal of Computational and Graphical Statistics 28 (2): 265–75.
Frick, Klaus, Axel Munk, and Hannes Sieling. 2014. “Multiscale Change Point Inference.” Journal of the Royal Statistical Society: Series B 76 (3): 495–580.
Fryzlewicz, Piotr. 2014. “Wild Binary Segmentation for Multiple Change-Point Detection.” The Annals of Statistics 42 (6): 2243–81. https://doi.org/10.1214/14-AOS1245.
———. 2018. “Tail-Greedy Bottom-up Data Decompositions and Fast Multiple Change-Point Detection.” The Annals of Statistics 46 (6B): 3390–3421.
———. 2020. “Detecting Multiple Change-Point Features via Narrowest-over-Threshold.” Journal of the Royal Statistical Society Series B 82 (5): 1377–1418.
Grundy, Thomas, Rebecca Killick, and Gueorgui Mihaylov. 2020. “High-Dimensional Changepoint Detection via a Geometrically Inspired Mapping.” Statistics and Computing 30: 1155–66.
Haynes, Kaylea, Paul Fearnhead, and Idris A Eckley. 2017. “A Computationally Efficient Nonparametric Approach for Changepoint Detection.” Statistics and Computing 27 (5): 1313–29. https://doi.org/10.1007/s11222-016-9687-5.
James, Nicholas A, and David S Matteson. 2014. “Ecp: An R Package for Nonparametric Multiple Change Point Analysis of Multivariate Data.” Journal of Statistical Software 62 (7): 1–25. https://doi.org/10.18637/jss.v062.i07.
Killick, Rebecca, and Idris Eckley. 2014. “Changepoint: An R Package for Changepoint Analysis.” Journal of Statistical Software 58 (3): 1–19. https://doi.org/10.18637/jss.v058.i03.
Killick, Rebecca, Paul Fearnhead, and Idris A Eckley. 2012. “Optimal Detection of Changepoints with a Linear Computational Cost.” Journal of the American Statistical Association 107 (500): 1590–98. https://doi.org/10.1080/01621459.2012.737745.
Li, Xingchi, and Xianyang Zhang. 2024. “Fastcpd: Fast Change Point Detection in r.” arXiv Preprint arXiv:2404.05933.
Maidstone, Robert, Toby Hocking, Guillem Rigaill, and Paul Fearnhead. 2017. “On Optimal Multiple Changepoint Algorithms for Large Data.” Statistics and Computing 27 (2): 519–33. https://doi.org/10.1007/s11222-016-9636-3.
Matteson, David S, and Nicholas A James. 2014. “A Nonparametric Approach for Multiple Change Point Analysis of Multivariate Data.” Journal of the American Statistical Association 109 (505): 334–45.
McGonigle, Euan T, and Haeran Cho. 2025. “Nonparametric Data Segmentation in Multivariate Time Series via Joint Characteristic Functions.” Biometrika 112 (2): asaf024.
Muggeo, Vito MR. 2003. “Estimating Regression Models with Unknown Break-Points.” Statistics in Medicine 22 (19): 3055–71.
———. 2008. “Segmented: An r Package to Fit Regression Models with Broken-Line Relationships.” R News 8 (1): 20–25.
Pein, Florian, Hannes Sieling, and Axel Munk. 2017. “Heterogeneous Change Point Inference.” Journal of the Royal Statistical Society: Series B 79 (4): 1207–27.
Robinson, David. 2017. “Broom: An R Package for Converting Statistical Analysis Objects into Tidy Data Frames.” Journal of Open Source Software 2 (19): 401. https://doi.org/10.21105/joss.00401.
Romano, Gaetano, Guillem Rigaill, Vincent Runge, and Paul Fearnhead. 2022. “Detecting Abrupt Changes in the Presence of Local Fluctuations and Autocorrelated Noise.” Journal of the American Statistical Association 117 (540): 2147–62.
Ross, Gordon J. 2015. “Parametric and Nonparametric Sequential Change Detection in r: The Cpm Package.” Journal of Statistical Software 66 (3): 1–20.
van den Burg, Gerrit J J, and Christopher K I Williams. 2020. “An Evaluation of Change Point Detection Algorithms.” arXiv Preprint arXiv:2003.06222. https://doi.org/10.48550/arXiv.2003.06222.
Wang, Tengyao, and Richard J Samworth. 2018. “High Dimensional Change Point Estimation via Sparse Projection.” Journal of the Royal Statistical Society: Series B 80 (1): 57–83.
Wickham, Hadley. 2016. Ggplot2: Elegant Graphics for Data Analysis. Springer. https://doi.org/10.1007/978-3-319-24277-4.
Zeileis, Achim, Friedrich Leisch, Kurt Hornik, and Christian Kleiber. 2002. “Strucchange: An r Package for Testing for Structural Change in Linear Regression Models.” Journal of Statistical Software 7 (2): 1–38.
Zhao, Kaiguang, Michael A Wulder, Tongxi Hu, et al. 2019. “Detecting Change-Point, Trend, and Seasonality in Satellite Time Series Data to Track Abrupt Changes and Nonlinear Dynamics: A Bayesian Ensemble Algorithm.” Remote Sensing of Environment 232: 111181.
Zhao, Zifeng, Feiyu Jiang, and Xiaofeng Shao. 2022. “Segmenting Time Series via Self-Normalisation.” Journal of the Royal Statistical Society: Series B 84 (5): 1699–1725.