ahead (Time Series Forecasting with uncertainty quantification) gets a lot faster to install: most dependencies are now optional

R-bloggers 2026-09-09

[This article was first published on T. Moudiki's Webpage - R, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

If you’ve used ahead before, you may remember install.packages("ahead")or pip install ahead taking a while, since it dragged in two dozenmodeling packages up front regardless of which one you actually planned touse. That’s fixed now.

The problem: one function’s dependency, everyone’s install time

ahead supports a lot of forecasting methods, and each one can lean on adifferent modeling package: forecast, randomForest, e1071, glmnet,gam, quantreg, vars, fGarch, VineCopula, mboost, ranger,ForecastComb. Historically, most of these were plain Importsin DESCRIPTION, which means R installs all of them before you can evenload the package — regardless of whether you plan to use ahead::dynrmf with arandom forest or ahead::ridge2f for a multivariate model with none of the above.

The fix: (almost) everything becomes a Suggests

The new DESCRIPTION keeps only what the package’s core code actuallyneeds at load time:

Imports:    Rcpp (>= 1.0.6),    foreach,    tseriesDepends: R (>= 3.5.0)Suggests:    caret, cclust, dfoptim, doSNOW, doParallel, knitr, rmarkdown,    testthat, fpp2, glmnet, e1071, gam, quantreg, randomForest,    spatial, vars, roxygen2, ForecastComb, ranger, mboost, misc,    Mcomp, fGarch, VineCopula, forecast (>= 8.0),    ggplot2 (>= 3.0.0), randtoolbox (>= 1.17), simulatetimeseries

Everything else — randomForest, e1071, forecast, glmnet, vars,fGarch, VineCopula, and the rest — moved to Suggests. That means Rinstalls in seconds, because Rcpp, foreach, and tseries are the onlyhard requirements.

Installing the missing piece, exactly when you need it

Moving packages to Suggests only helps if the package still workssmoothly when you actually call a function that needs one of them. That’swhat a small internal helper, check_suggested(), takes care of:

check_suggested <- function(pkg, ask = interactive()) {  if (requireNamespace(pkg, quietly = TRUE)) {    return(invisible(TRUE))  }  do_install <- TRUE  if (ask) {    do_install <- utils::askYesNo(      sprintf("Package '%s' is required but not installed. Install it now?", pkg)    )    do_install <- isTRUE(do_install)  }  if (do_install) {    utils::install.packages(      pkg,      repos = c("https://techtonique.r-universe.dev", "https://cloud.r-project.org")    )  }  if (!requireNamespace(pkg, quietly = TRUE)) {    stop(      sprintf(        "Package '%s' is required. Install it with install.packages('%s', repos = c('https://techtonique.r-universe.dev', 'https://cloud.r-project.org')).",        pkg, pkg      ),      call. = FALSE    )  }  invisible(TRUE)}

Every function (hopefully all of them!) that relies on a suggested package now callscheck_suggested("that_package") first. In practice, this means:

  • If the package is already installed, nothing changes — no prompt, nodelay.
  • If it’s missing and you’re in an interactive session, you get askedbefore anything is installed on your behalf.
  • If it’s missing and the session isn’t interactive (a script, a CI job),it installs automatically.
  • If the install still fails, you get a clear error with the exact commandto run, pointing at both CRAN and Techtonique’s r-universe repository —useful since a couple of these dependencies (like ForecastComb andmisc) aren’t on CRAN at all.

So install.packages("ahead") is fast, and the first time you calldynrmf(..., fit_func = randomForest::randomForest), randomForest getsinstalled for you, once, and never again after that.

Same idea, carried into the Python wrapper

ahead’s Python package is a wrapper around the R package (via rpy2), sothe first call to any forecaster has always installed R-side dependencieson demand — that’s the “might take some time, but ONLY the 1st time it’scalled” comment you’ll see next to DynamicRegressor or Ridge2Regressorin the examples. With the R package’s own install now lean, that first-calloverhead on the Python side shrinks too: there’s less to pull in beforecheck_suggested() even gets to the package your chosen method needs.

import osimport numpy as npimport pandas as pdfrom ahead import DynamicRegressor, EATfrom time import time# Forecasting horizonh = 25# Data frame containing the time seriesdf = pd.read_csv("https://raw.githubusercontent.com/Techtonique/datasets/refs/heads/main/time_series/univariate/AirPassengers.csv").set_index('date')df.index = pd.DatetimeIndex(df.index)print(df)# univariate ts forecastingprint("Example 1 -----")d1 = DynamicRegressor(h=h, date_formatting="ms")print(d1.__module__)start = time()d1.forecast(df)print(f"Elapsed: {time()-start} \n")print("averages: \n")print(d1.averages_)print("\n")print("ranges: \n")print(d1.ranges_)print("\n")print("Example 2 -----")d2 = DynamicRegressor(h=h, type_pi="T", date_formatting="original")start = time()d2.forecast(df)print(f"Elapsed: {time()-start} \n")print("averages: \n")print(d2.averages_)print("\n")print("ranges: \n")print(d2.ranges_)print("\n")d2.plot()

The first call to DynamicRegressor.forecast() in a fresh environment isthe one that pays the (now much smaller) one-time cost of installing the Rahead package; every call after that — including d2 above, in the samesession — runs at full speed.

Why this matters in practice

  • Faster CI and Docker builds — you’re not compiling gam, fGarch,VineCopula, and a dozen others just to run a couple of unit tests thatuse two of them.
  • Less fragile installs — a slow-to-compile or awkward-to-build modelingpackage no longer stands between you and a working ahead install if youdon’t even need that method.
  • Smaller footprint for the methods you actually use — if you only evercall ridge2f, you never install the GARCH or copula-based dependenciesat all.

If you want everything available up front (for example, to prepare anoffline environment), you can still install every suggested packageyourself:

install.packages(  c("caret", "cclust", "dfoptim", "doSNOW", "doParallel", "fpp2",    "glmnet", "e1071", "gam", "quantreg", "randomForest", "spatial",    "vars", "ranger", "mboost", "Mcomp", "fGarch", "VineCopula",    "forecast", "ggplot2", "randtoolbox", "simulatetimeseries"),  repos = c("https://techtonique.r-universe.dev", "https://cloud.r-project.org"))

Get it

  • R: see the README for the Techtonique repo setup, version 0.38.1.
  • Python: pip install ahead --verbose, from ahead_python.

As always, issues and contributions are welcome on both repos.

image-title-here

To leave a comment for the author, please follow the link and comment on their blog: T. Moudiki's Webpage - R.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
Continue reading: ahead (Time Series Forecasting with uncertainty quantification) gets a lot faster to install: most dependencies are now optional