--- title: "Naming Conventions and the Analysis/Tidying/Plotting Pipeline" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Naming Conventions and the Analysis/Tidying/Plotting Pipeline} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, warning = FALSE, message = FALSE, comment = "#>" ) ``` ```{r load} library(exuber) ``` ## What this page covers The package covers more than the original test. Alongside `radf()`, the recursive ADF/SADF/GSADF/BSADF statistic of Phillips, Shi & Yu (2015), it has alternative tests, dating procedures, monitoring schemes and root inference, about 25 functions in all. This page explains how the function names group them. It also says which functions plug into the `summary()`, `datestamp()`, `tidy()` and `autoplot()` pipeline built for `radf()`, and which have differently shaped output of their own. ## The naming scheme | Pattern | Means | Examples | |---|---|---| | `radf_` prefix | Built on the recursive ADF core: calls `radf()` directly or reuses its `badf`/`bsadf` recursion | `radf()`, `radf_tt()`, `radf_sign()`, `radf_common()`, `radf_kp()`, `radf_recovery()`, and the `_cv`/`_mc`/`_sb`/`_wb` critical-value engines | | `_test` suffix | A standalone hypothesis test with its own null distribution, not built on the recursive ADF core | `lbi_test()`, `ssu_test()`, `quantile_test()`, `cobubble_test()` | | `dating_` prefix | Dating by point estimation and model selection, with no formal hypothesis test | `dating_hls()`, `dating_hlw()`, `dating_knp()`, `dating_pdc()` | | `monitor`/`monitor_` prefix | Real-time (sequential) detection, grouped by what the function does and not by its internal mechanism. `monitor()` is the flagship of this family, as `radf()` is for the `radf_` family. It reuses `badf` and `bsadf` directly but carries no `radf` or `sadf` token, so that it reads as the real-time monitor and not as a `radf_` variant (see below) | `monitor()`, `monitor_cusum()`, `monitor_lbi()`, `monitor_quantile()` | | `root` family | Confidence-interval inference on the magnitude of the explosive root, not a test for its presence (`exuber_functions(family = "root")`; there is no shared prefix) | `rootstamp()`, which has two S3 methods: the default for a single sub-sample and the `radf_obj` method, which runs every `datestamp()` episode at once | | stands alone | A point-estimation tool, not a test | `contagion_reg()` | The prefixes are a convention and not a contract. They are easy to misremember and they sometimes pull against each other. `monitor()` is grouped with the other monitors under a name that deliberately does not advertise its ADF-family internals, so that nobody mistakes it for a `radf_*()` variant. For programmatic use, do not parse function names. Call `exuber_functions()`, which returns the same categorization as queryable data. ```{r registry} exuber_functions(family = "monitor") ``` Two names look related but are not. The `dating_*()` functions above are standalone SSR/BIC procedures that run directly on raw data and take no critical value. `datestamp()` (see below) is a different thing: it is the generic that applies the threshold-crossing rule of Phillips, Shi and Yu to any `radf_obj` and `radf_cv` pair. `datestamp()` normally needs a `radf_cv`, with one exception. `datestamp(object, option = "svadf")` runs the asymmetric-threshold dating of Sarkar & Wells (2026) directly on `object$badf` and needs no critical value (see `vignette("experimental-methods")`). ## What actually plugs into `summary()`/`datestamp()`/`tidy()`/`autoplot()` These four generics are built around one shape: a `radf_obj` (from `radf()`) paired with a `radf_cv` that carries a time-varying boundary (`badf_cv` and `bsadf_cv`, one critical value per recursion point) as well as the three scalar sup-statistic critical values (`adf_cv`, `sadf_cv` and `gsadf_cv`). Only functions whose result has the `radf_obj` class, and whose paired `_cv()` function computes that time-varying boundary, get the full pipeline. In practice there are two tiers. ### Full support: `radf_common()`, `radf_kp()`, `radf_tt()`, `radf_sign()`, `radf_sign_dm()`, `radf_sbz()` `radf_kp()` and `radf_common()` return the output of `radf()` itself, computed on a series purged of volatility or on a PCA factor respectively, so every generic works exactly as it does for plain `radf()`. The running example for this section is the series these tests were designed for: the `sim_psy1()` bubble with a permanent volatility break (`sim_vol_break()`, where the innovation standard deviation triples half-way through the sample). See `vignette("volatility-robust-radf")`. ```{r vol-dgp} y <- sim_psy1(n = 200, seed = 1, e = sim_vol_break(199)) ``` ```{r kp-full} res <- radf_kp(y, minw = 20) cv <- radf_mc_cv(n = attr(res, "n"), minw = 20) summary(res, cv = cv) datestamp(res, cv = cv) tidy(res, cv = cv) autoplot(res, cv = cv) ``` The other three are different. They carry the `radf_obj` class but build their statistic on `gls_dfstat_grid()` and do not call `radf()` directly. This function is a no-intercept, GLS-demeaned recursive Dickey-Fuller grid. It is fed the raw series for `radf_tt()`, the cumulated sign of the series for `radf_sign()`, and a recursively demeaned cumulated sign for `radf_sign_dm()`. The `_cv()` functions of all three return the time-varying boundaries `badf_cv` and `bsadf_cv` as well as the three scalar critical values, so `datestamp()` and `autoplot()` work on them. The `bsadf` that `gls_dfstat_grid()` returns is already the sup over all window starts at each point. This differs from `radf_mc_cv()`, which applies a `cummax()` across replicates because of the output shape of the base C++ engine. Here the boundary is the per-time-point quantile across replicates, the same construction `radf_mc_cv()` uses for its own `bsadf_cv`. ```{r tt-full} res <- radf_tt(y, minw = 20) cv <- radf_tt_cv(n = 200, minw = 20) summary(res, cv = cv) datestamp(res, cv = cv) tidy(res, cv = cv) autoplot(res, cv = cv) ``` ```{r sign-full} res <- radf_sign(y, minw = 20) cv <- radf_sign_cv(n = 200, minw = 20) summary(res, cv = cv) datestamp(res, cv = cv) tidy(res, cv = cv) autoplot(res, cv = cv) ``` `radf_sbz()` is a fourth, separate case. It builds its statistic (`supBZ`) on `wls_dfstat_grid()`, a no-intercept recursive Dickey-Fuller grid weighted by WLS and kernel volatility, and not on `gls_dfstat_grid()`. The same fix applies for the same reason, since `wls_dfstat_grid()` already returns the full `badf` and `bsadf` path for each replicate. The wild bootstrap in `radf_sbz_cv()` is therefore built as the Monte Carlo simulation in `radf_tt_cv()` and `radf_sign_cv()` is, with a per-time-point quantile across replicates and no `cummax()` shortcut. The test rejects on a sufficiently strong deterministic explosive path. Its kernel-volatility weighting costs enough power, however, that it does not reject the series above at nboot = 100 to 200, where the bubble is the milder default of `sim_psy1()`. The same trade between power and robustness applies to the `supBZ` leg of `radf_sbz_union()`, described on the volatility-robustness page. ```{r sbz-full} res <- radf_sbz(y, minw = 20) cv <- radf_sbz_cv(y, minw = 20, nboot = 200, seed = 1) summary(res, cv = cv) tidy(res, cv = cv) ``` `datestamp()` and `autoplot()` need at least one rejection to have anything to show, and they raise an error otherwise, as for any other `radf_obj` and `radf_cv` pair. The series above does not clear the `supBZ` threshold, so we repeat the volatility break with a stronger explosive regime that does not collapse (`rho = 1.03` from `t = 120` to the end of the sample): ```{r sbz-full-reject} y_strong <- sim_psy1(n = 200, te = 120, tf = 200, c = 0.03, alpha = 0, seed = 1, e = sim_vol_break(199)) res2 <- radf_sbz(y_strong, minw = 20) cv2 <- radf_sbz_cv(y_strong, minw = 20, nboot = 100, seed = 1) datestamp(res2, cv = cv2) autoplot(res2, cv = cv2) ``` ### Own `print()` and `autoplot()`: everything else The remaining 15 or so functions return their own class, with their own `print()` and `autoplot()` methods. They are `lbi_test()`, `ssu_test()`, `quantile_test()`, `cobubble_test()`, the `dating_*()` family, the `monitor()` and `monitor_*()` family (including `monitor()` itself, despite its ADF-family internals), `contagion_reg()`, `radf_recovery()`, `rootstamp()` and `radf_sbz_union()`. Their output does not fit the `radf_obj` shape: a dating table is not a per-series sup-statistic, and a monitoring alarm is not a critical value grid. Forcing them through `summary()`, `datestamp()` and `tidy()` would not close a documentation gap, so each is presented by its own methods, shown below. `rootstamp()` needs one remark. The function reference lists it under Analysis, right after `datestamp()`, because that is its position in the sequence of steps (detect, date, measure the growth rate). That is a position in the workflow and not an S3-support tier. It has its own class with its own `print()` and `autoplot()` methods, like everything else in this section. See `vignette("root-inference")`. ```{r standalone} dating_hls(sim_data$psy1, trim = 0.05) ssu_test(sim_data$psy1, sig_lvl = 95) autoplot(dating_hls(sim_data$psy1, trim = 0.05)) ``` ## Summary | Tier | Functions | `summary()` | `datestamp()` | `tidy()` | `autoplot()` | |---|---|---|---|---|---| | Full | `radf()`, `radf_common()`, `radf_kp()`, `radf_tt()`, `radf_sign()`, `radf_sign_dm()`, `radf_sbz()` | yes | yes | yes | yes | | Standalone | everything else | own `print()` | -- | -- | own method |