Skip to contents

Runs a grid of X-13ARIMA-SEATS specifications, computes diagnostics (QS tests, residual checks, revision metrics, distance to a baseline model, etc.), and ranks candidates using a composite score. Returns the best model together with a diagnostic table and seasonality call. The function may fit several X-13 models; use max_specs for fast exploratory runs and larger grids for review runs.

Usage

auto_seasonal_analysis(
  y,
  specs = NULL,
  use_fivebest = TRUE,
  max_specs = NULL,
  seasonal_only = TRUE,
  auto_outliers = TRUE,
  transform_fun = c("auto", "log", "none"),
  td_candidates = NULL,
  td_usertype = "td",
  include_easter = c("auto", "always", "off"),
  easter_len = 15L,
  include_history_top_n = 10,
  early_period_end = NULL,
  current_model = NULL,
  current_sa = NULL,
  current_seasonal = NULL,
  w_qs = 1,
  w_stability = 1,
  w_aicc = 1,
  w_rev = 0.5,
  w_dist_sa = 1,
  w_dist_seas = 0.5,
  w_engine = 1,
  engine = c("seats", "x11", "auto"),
  outlier_types = c("AO", "LS", "TC"),
  outlier_method = "AddOne",
  outlier_critical = 4,
  outlier_alpha = NULL,
  comparison_mode = c("full_search", "incumbent_fixed")
)

Arguments

y

A time series (ts) or an object that can be converted to ts via tsbox::ts_ts().

specs

Optional character vector of ARIMA specifications in the form "(p d q)(P D Q)". If NULL, default specs are chosen based on the series frequency.

use_fivebest

Logical. If TRUE, a small set of data-driven "five best" specs (as in seasonal::seas(x, x11 = "")) is added to the candidate grid.

max_specs

Optional positive integer limiting the number of candidate ARIMA specifications fitted after de-duplication and seasonal filtering.

seasonal_only

Logical. If TRUE (default), candidate ARIMA specifications must have a non-zero seasonal order (P + D + Q > 0).

auto_outliers

Logical. If TRUE, enables automatic outlier detection in the candidate models.

transform_fun

Transformation applied to the input series before seasonal adjustment. "auto" selects a safe log transform if the series is strictly positive, "log" forces a log transform, and "none" keeps the series in levels.

td_candidates

Optional named list of alternative trading-day regressors, e.g. list(wd = wd.m, wd1 = wd1.m). Each element should be a time series aligned with y.

td_usertype

Character string passed as regression.usertype when xreg is used (default "td").

include_easter

Controls inclusion of Easter regressors: "auto" (default) asks X-13 to select among its supported Easter windows or reject Easter, "always" fixes easter[easter_len], and "off" neither includes nor tests Easter. A logical value is also accepted and mapped to "auto"/"off".

easter_len

Integer, length (in days) of the Easter effect imposed by include_easter = "always". It is ignored in "auto" and "off" modes.

include_history_top_n

Integer, number of top-ranked models for which revision metrics over the history are computed before the final ranking step.

early_period_end

Reserved for future use. Optional index or date marking the end of an early sample period used for stability checks.

current_model

Optional incumbent seasonal::seas object used as a baseline for distance measures and comparison.

current_sa

Optional baseline seasonally adjusted series. If not supplied, it is extracted from current_model when possible.

current_seasonal

Optional baseline seasonal component. If not supplied, it is extracted from current_model (preferring SEATS, falling back to X-11 if needed).

w_qs

Weight of the QS (seasonality) diagnostics in the composite ranking score.

w_stability

Weight of residual / stability diagnostics (e.g. Ljung-Box) in the composite score.

w_aicc

Weight of the information criterion (AICc) in the composite score.

w_rev

Weight of the revision metric (historical revision MAE) in the composite score.

w_dist_sa

Weight of the distance between seasonally adjusted series (candidate vs baseline) in the composite score.

w_dist_seas

Weight of the distance between seasonal components (candidate vs baseline) in the composite score.

w_engine

Weight / penalty term related to the decomposition engine (SEATS vs X-11) in the composite score.

engine

Engine preference for candidate models. One of "seats", "x11" or "auto" (tries both and lets the ranking decide, with a small penalty for deviations from the preferred engine).

outlier_types

Character vector of outlier types to detect, typically a subset of c("AO", "LS", "TC").

outlier_method

Character scalar giving the outlier detection method (passed to seasonal::seas(), e.g. "AddOne").

outlier_critical

Numeric critical value for outlier detection (e.g. t- or z-threshold).

outlier_alpha

Optional numeric significance level for outlier detection. If supplied, it is converted to a two-sided normal cutoff for outlier_critical.

comparison_mode

Comparison design. "full_search" (default) lets every candidate select its own regressors and outliers. "incumbent_fixed" requires current_model, retains its selected transformation and effective regression variables, and varies only the requested ARIMA specification and decomposition engine. Stored user regressors must be available in the incumbent model.

Value

An object of class "auto_seasonal_analysis" with components such as best (best seas model), table (diagnostic and ranking table, including actual Easter inclusion in with_easter and its selected window in easter_window, SEATS substitution metadata in SEATS_model_switch and SEATS_operative_model, and peak-to-trough seasonal amplitude in seasonal_amp_pct. For log models this is the seasonal-factor range in percentage points; for level models it is the additive range relative to the median absolute series level), specs_tried, frequency, transform, baseline, and seasonality. When current_model is supplied, baseline includes its diagnostics and an indicator of whether its AICc is comparable with the selected candidate. comparison_mode and regressor_comparison describe whether candidate AICc differences hold the incumbent regressors fixed.

Details

Ranking combines residual seasonality on the adjusted series (QS_p), Ljung-Box residual diagnostics, AICc, revision metrics for the top candidates, distance from an incumbent model when supplied, and a small engine-preference penalty.

Examples

# \donttest{
if (requireNamespace("seasonal", quietly = TRUE)) {
  res <- auto_seasonal_analysis(
    AirPassengers,
    max_specs = 3,
    include_history_top_n = 2
  )
  res$seasonality$overall
}
#> Model used in SEATS is different: (1 1 2)(1 0 0)
#> Model used in SEATS is different: (1 1 2)(1 0 0)
#> Model used in SEATS is different: (1 1 1)(1 0 0)
#> seas(
#>   x = structure(c(112, 118, 132, 129, 121, 135, 148, 148, 136, 119, 104, 118, 115, 126, 141, 135, 125, 149, 170, 170, 158, 133, 114, 140, 145, 150, 178, 163, 172, 178, 199, 199, 184, 162, 146, 166, 171, 180, 193, 181, 183, 218, 230, 242, 209, 191, 172, 194, 196, 196, 236, 235, 229, 243, 264, 272, 237, 211, 180, 201, 204, 188, 235, 227, 234, 264, 302, 293, 259, 229, 203, 229, 242, 233, 267, 269, 270, 315, 364, 347, 312, 274, 237, 278, 284, 277, 317, 313, 318, 374, 413, 405, 355, 306, 271, 306, 315, 301,  356, 348, 355, 422, 465, 467, 404, 347, 305, 336, 340, 318, 362, 348, 363, 435, 491, 505, 404, 359, 310, 337, 360, 342, 406, 396, 420, 472, 548, 559, 463, 407, 362, 405, 417, 391, 419, 461, 472, 535, 622, 606, 508, 461, 390, 432), tsp = c(1949, 1960.91666666667, 12), class = "ts"),
#>   seats.noadmiss = "no",
#>   transform.function = "log",
#>   arima.model = "(0 1 0)(0 1 1)",
#>   seats = "",
#>   regression.variables = "easter[8]",
#>   regression.aictest = NULL,
#>   outlier = NULL
#> )
#> seas(
#>   x = structure(c(112, 118, 132, 129, 121, 135, 148, 148, 136, 119, 104, 118, 115, 126, 141, 135, 125, 149, 170, 170, 158, 133, 114, 140, 145, 150, 178, 163, 172, 178, 199, 199, 184, 162, 146, 166, 171, 180, 193, 181, 183, 218, 230, 242, 209, 191, 172, 194, 196, 196, 236, 235, 229, 243, 264, 272, 237, 211, 180, 201, 204, 188, 235, 227, 234, 264, 302, 293, 259, 229, 203, 229, 242, 233, 267, 269, 270, 315, 364, 347, 312, 274, 237, 278, 284, 277, 317, 313, 318, 374, 413, 405, 355, 306, 271, 306, 315, 301,  356, 348, 355, 422, 465, 467, 404, 347, 305, 336, 340, 318, 362, 348, 363, 435, 491, 505, 404, 359, 310, 337, 360, 342, 406, 396, 420, 472, 548, 559, 463, 407, 362, 405, 417, 391, 419, 461, 472, 535, 622, 606, 508, 461, 390, 432), tsp = c(1949, 1960.91666666667, 12), class = "ts"),
#>   seats.noadmiss = "no",
#>   transform.function = "log",
#>   arima.model = "(0 1 0)(0 1 1)",
#>   seats = "",
#>   regression.variables = "easter[8]",
#>   regression.aictest = NULL,
#>   outlier = NULL
#> )
#> seas(
#>   x = structure(c(112, 118, 132, 129, 121, 135, 148, 148, 136, 119, 104, 118, 115, 126, 141, 135, 125, 149, 170, 170, 158, 133, 114, 140, 145, 150, 178, 163, 172, 178, 199, 199, 184, 162, 146, 166, 171, 180, 193, 181, 183, 218, 230, 242, 209, 191, 172, 194, 196, 196, 236, 235, 229, 243, 264, 272, 237, 211, 180, 201, 204, 188, 235, 227, 234, 264, 302, 293, 259, 229, 203, 229, 242, 233, 267, 269, 270, 315, 364, 347, 312, 274, 237, 278, 284, 277, 317, 313, 318, 374, 413, 405, 355, 306, 271, 306, 315, 301,  356, 348, 355, 422, 465, 467, 404, 347, 305, 336, 340, 318, 362, 348, 363, 435, 491, 505, 404, 359, 310, 337, 360, 342, 406, 396, 420, 472, 548, 559, 463, 407, 362, 405, 417, 391, 419, 461, 472, 535, 622, 606, 508, 461, 390, 432), tsp = c(1949, 1960.91666666667, 12), class = "ts"),
#>   seats.noadmiss = "no",
#>   transform.function = "log",
#>   arima.model = "(1 1 1)(0 1 1)",
#>   seats = "",
#>   regression.variables = "easter[8]",
#>   regression.aictest = NULL,
#>   outlier = NULL
#> )
#> seas(
#>   x = structure(c(112, 118, 132, 129, 121, 135, 148, 148, 136, 119, 104, 118, 115, 126, 141, 135, 125, 149, 170, 170, 158, 133, 114, 140, 145, 150, 178, 163, 172, 178, 199, 199, 184, 162, 146, 166, 171, 180, 193, 181, 183, 218, 230, 242, 209, 191, 172, 194, 196, 196, 236, 235, 229, 243, 264, 272, 237, 211, 180, 201, 204, 188, 235, 227, 234, 264, 302, 293, 259, 229, 203, 229, 242, 233, 267, 269, 270, 315, 364, 347, 312, 274, 237, 278, 284, 277, 317, 313, 318, 374, 413, 405, 355, 306, 271, 306, 315, 301,  356, 348, 355, 422, 465, 467, 404, 347, 305, 336, 340, 318, 362, 348, 363, 435, 491, 505, 404, 359, 310, 337, 360, 342, 406, 396, 420, 472, 548, 559, 463, 407, 362, 405, 417, 391, 419, 461, 472, 535, 622, 606, 508, 461, 390, 432), tsp = c(1949, 1960.91666666667, 12), class = "ts"),
#>   seats.noadmiss = "no",
#>   transform.function = "log",
#>   arima.model = "(1 1 1)(0 1 1)",
#>   seats = "",
#>   regression.variables = "easter[8]",
#>   regression.aictest = NULL,
#>   outlier = NULL
#> )
#> seas(
#>   x = structure(c(112, 118, 132, 129, 121, 135, 148, 148, 136, 119, 104, 118, 115, 126, 141, 135, 125, 149, 170, 170, 158, 133, 114, 140, 145, 150, 178, 163, 172, 178, 199, 199, 184, 162, 146, 166, 171, 180, 193, 181, 183, 218, 230, 242, 209, 191, 172, 194, 196, 196, 236, 235, 229, 243, 264, 272, 237, 211, 180, 201, 204, 188, 235, 227, 234, 264, 302, 293, 259, 229, 203, 229, 242, 233, 267, 269, 270, 315, 364, 347, 312, 274, 237, 278, 284, 277, 317, 313, 318, 374, 413, 405, 355, 306, 271, 306, 315, 301,  356, 348, 355, 422, 465, 467, 404, 347, 305, 336, 340, 318, 362, 348, 363, 435, 491, 505, 404, 359, 310, 337, 360, 342, 406, 396, 420, 472, 548, 559, 463, 407, 362, 405, 417, 391, 419, 461, 472, 535, 622, 606, 508, 461, 390, 432), tsp = c(1949, 1960.91666666667, 12), class = "ts"),
#>   seats.noadmiss = "no",
#>   transform.function = "log",
#>   arima.model = "(0 1 1)(0 1 1)",
#>   seats = "",
#>   regression.variables = "easter[1]",
#>   regression.aictest = NULL,
#>   outlier = NULL
#> )
#> seas(
#>   x = structure(c(112, 118, 132, 129, 121, 135, 148, 148, 136, 119, 104, 118, 115, 126, 141, 135, 125, 149, 170, 170, 158, 133, 114, 140, 145, 150, 178, 163, 172, 178, 199, 199, 184, 162, 146, 166, 171, 180, 193, 181, 183, 218, 230, 242, 209, 191, 172, 194, 196, 196, 236, 235, 229, 243, 264, 272, 237, 211, 180, 201, 204, 188, 235, 227, 234, 264, 302, 293, 259, 229, 203, 229, 242, 233, 267, 269, 270, 315, 364, 347, 312, 274, 237, 278, 284, 277, 317, 313, 318, 374, 413, 405, 355, 306, 271, 306, 315, 301,  356, 348, 355, 422, 465, 467, 404, 347, 305, 336, 340, 318, 362, 348, 363, 435, 491, 505, 404, 359, 310, 337, 360, 342, 406, 396, 420, 472, 548, 559, 463, 407, 362, 405, 417, 391, 419, 461, 472, 535, 622, 606, 508, 461, 390, 432), tsp = c(1949, 1960.91666666667, 12), class = "ts"),
#>   seats.noadmiss = "no",
#>   transform.function = "log",
#>   arima.model = "(0 1 1)(0 1 1)",
#>   seats = "",
#>   regression.variables = "easter[1]",
#>   regression.aictest = NULL,
#>   outlier = NULL
#> )
#> # A tibble: 1 × 5
#>   call_overall share_ids share_qsori share_m7_strong share_m7_weak
#>   <chr>            <dbl>       <dbl>           <dbl>         <dbl>
#> 1 ADJUST               1           1               1             1
# }