Joins a table of real-world events to a detection result and reports all
three outcomes: changepoints an event explains, changepoints no event
explains, and events no changepoint found. Matching reuses the same
tolerance rule as cpt_metrics() and
cpt_consensus(), so the package has one notion of "close
enough" throughout.
Arguments
- object
A
ggcpt_eventsobject (forautoplot()).- events
A data frame of events with a location column and a label column. The location may be on the position scale or, when the result carries a time index, on the index scale (dates, say); which one is detected automatically from the column's type and reported. An event whose location cannot be placed on the series (a missing value, text that is not a number, or a date string against a
Dateindex) is left out of all three outcomes, with a warning that names it.- location
Name of the location column. Defaults to the first column whose type matches the result's index (or the first numeric column).
- label
Name of the label column. Defaults to the first character or factor column.
- tolerance
Matching window in positions. Defaults to
5.- x
A
ggcpt_eventsobject.- ...
Ignored.
- repel
Use ggrepel for the event labels? Defaults to
TRUEwhen it is installed.
Value
A ggcpt_events object: a list with
matchedone row per matched pair:
cp,event,event_value(the event's own location, on the index scale when it was given as one),event_position(that location as a position in the series) anddistance.unexplaineddetected changepoints with no event (
cp).undetectedevents with no changepoint (
event,event_value,event_position).
matched and unexplained carry cp_index, the
changepoint on the original scale, when (and only when) the result
carries a time index, so "cp_index" %in% names(x) is the
test for it.
tidy() flattens those three slots into one table with
a status column, and the row count is therefore the number of
changepoints plus the number of events, not either one alone.
The three values are
"matched" (a pair: both cp and event filled,
with distance), "unexplained_changepoint" (a
changepoint with no event: cp filled, event and
distance NA) and "undetected_event" (an event
with no changepoint: event and position filled,
cp and distance NA).
Filter on status, not on is.na(cp). An
"unexplained_changepoint" row has a non-missing cp, so
subset(tidy(x), !is.na(cp)) returns the matched pairs
and the unexplained changepoints, which is the natural
mistake to make, and it silently overstates how many changepoints an
event explains.
With print(), tidy() and autoplot().
Examples
set.seed(2026)
x <- c(rnorm(60), rnorm(60, 4))
dates <- as.Date("2020-01-01") + 0:119
fit <- cpt_detect(x, method = "pelt", index = dates)
events <- data.frame(when = as.Date(c("2020-03-01", "2020-04-15")),
what = c("policy change", "supply shock"))
cpt_annotate_events(fit, events)
#> ggcpt_events (tolerance 5 position(s))
#> Changepoints explained by an event: 1
#> Changepoints with no event: 0
#> Events with no changepoint: 1
#>
#> Matched:
#> # A tibble: 1 × 6
#> cp cp_index event event_value event_position distance
#> <int> <date> <chr> <date> <int> <int>
#> 1 60 2020-02-29 policy change 2020-03-01 61 1
#>
#> Events the detector did not find:
#> # A tibble: 1 × 3
#> event event_value event_position
#> <chr> <date> <int>
#> 1 supply shock 2020-04-15 106
