Getting started with forecastsuite

knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 4)

forecastsuite is a local, full-featured companion to the hosted Forecast Dashboard Shiny app. This vignette covers two ways to use it: the bundled app (run_app()), for interactive, no-code forecasting, and the underlying model registry, for scripting forecasts directly in R.

The bundled app

library(forecastsuite)
run_app()

run_app() launches a local Shiny app with four tabs, worked in order:

  1. Import Data -- upload a file, pull a data frame from your global environment, a Google Sheet, a CSV URL, or pasted text; or pick the bundled demo dataset (five years of simulated daily clinic visits) to explore the app with no data of your own. An optional grouping column splits the finalized dataset into one series per distinct value (e.g. one per district), and a merge/relabel panel cleans up messy raw values ("female"/"Female"/"FEMALE") before they become separate groups.
  2. Holidays -- Sundays, a fixed-date catalog, movable holidays from a file, and manual entries feed one compiled holiday list, with a consistency check against your data.
  3. Model -- pick a model (Prophet, ARIMA, SARIMA, ETS, TBATS, NNETAR, Holt-Winters, or LSTM if torch is installed), Fit & Forecast, and browse the plot, metrics, rolling-origin cross-validation, and an Analysis panel: seasonal decomposition, anomaly detection, residual diagnostics, and (with a grouping column active) cross-group correlation. A "Show Code" panel prints the R equivalent of whatever you just ran, with a copy-to-clipboard button and a download.
  4. Model Guide -- what each model is, its parameters, the evaluation metrics, and how the recommendation heuristic works.

Saving your setup (dataset, holidays, every Model-tab setting) to one file and reloading it later is available from any tab, via "Save Project (.rds)" / "Load Project (.rds)".

The registry API, without the app

Every model -- built-in or your own -- implements the same three-function contract:

fit(train_df, date_agg, ...)         # -> model_obj
forecast(model_obj, h, ...)          # -> raw_forecast_obj
to_tibble(raw_forecast_obj, test_df) # -> tibble(ds, yhat[, yhat_lower, yhat_upper])

train_df/test_df are ds/y tibbles (date, value). list_models() lists what's registered; get_model(key) returns one entry.

library(forecastsuite)

vapply(list_models(available_only = TRUE), function(m) m$key, character(1))

End to end on a synthetic series

set.seed(1)
n <- 120
ds <- as.Date("2023-01-01") + 0:(n - 1)
y <- 100 + 15 * sin(2 * pi * seq_len(n) / 7) + seq_len(n) * 0.2 + rnorm(n, sd = 3)

train_df <- tibble::tibble(ds = ds[1:100], y = y[1:100])
test_df  <- tibble::tibble(ds = ds[101:120], y = y[101:120])

model <- get_model("arima")
fit_obj <- model$fit(train_df, date_agg = "day", auto = TRUE)
fc_raw  <- model$forecast(fit_obj, h = 20)
fc_tib  <- model$to_tibble(fc_raw, test_df)

head(fc_tib)
safe_compute_metrics(fc_tib, test_df, label = "Test")
plot_forecast_generic(fc_tib, train_df = train_df, subtitle = model$annotate(fit_obj))

Analysis functions

The same functions behind the app's Analysis panel work standalone on any ds/y tibble -- no fitted model needed for decomposition or anomaly detection, and no Shiny session either.

decomp <- decompose_series(train_df, date_agg = "day")
head(decomp)
anomalies <- detect_anomalies(train_df, date_agg = "day", method = "iqr", threshold = 1.5)
anomalies[anomalies$is_anomaly, ]
resid_diag <- compute_residual_diagnostics(fc_tib, test_df)
resid_diag[c("ljung_box_p", "shapiro_p", "n")]

group_correlation_matrix() takes a named list of ds/y tibbles (one per group, e.g. grouped_series() from the app) and returns pairwise correlations:

group_correlation_matrix(list(
  A = tibble::tibble(ds = ds, y = y),
  B = tibble::tibble(ds = ds, y = y * 0.5 + rnorm(n, sd = 1))
))

Registering your own model

Adding a model is one register_model() call away from working everywhere the built-in ones do (the app's Model tab, Compare Models, Cross-Validation, the Show Code panel):

register_model(
  key = "naive_last",
  label = "Naive (last value)",
  fit = function(train_df, date_agg = "day", ...) utils::tail(train_df$y, 1),
  forecast = function(model_obj, h, ...) list(mean = rep(model_obj, h)),
  to_tibble = function(fc_obj, test_df) {
    tibble::tibble(ds = test_df$ds[seq_along(fc_obj$mean)], yhat = fc_obj$mean)
  }
)


Try the forecastsuite package in your browser

Any scripts or data that you put into this service are public.

forecastsuite documentation built on Oct. 18, 2026, 5:08 p.m.