Build parsnip model from nnetar function in forecast package

Hello,
I would like to build a parsnip model for a simple neural net as implemented in the nnetar() function from the forecast package.
I followed the offictial tutorial How to build a parsnip model as far as I could get. But after trying it out, it gives an error which does not surprise me. The code for building the model you find below. I guess that there are some errors in set_fit() and set_pred() and how the data is fed to the arguments (y and xreg) of the nnetar() function. I would be happy about any hints. This is my first time using tidymodels/parsnip and also building a parsnip model.

library(tidymodels)

set_new_model("narx_neuralnet")
set_model_mode(model = "narx_neuralnet", mode = "regression")
set_model_engine(
  "narx_neuralnet",
  mode = "regression",
  eng = "nnetar"
)
set_dependency("narx_neuralnet", eng = "nnetar", pkg = "forecast")

show_model_info("narx_neuralnet")


set_model_arg(
  "narx_neuralnet",
  eng = "nnetar",
  parsnip = "hidden_units",
  original = "size",
  func = list(fun = "hidden_units", pkg = "dials"),
  has_submodel = FALSE
)

set_model_arg(
  "narx_neuralnet",
  eng = "nnetar",
  parsnip = "epochs",
  original = "repeats",
  func = list(fun = "epochs", pkg = "dials"),
  has_submodel = FALSE
)

set_model_arg(
  "narx_neuralnet",
  eng = "nnetar",
  parsnip = "nonseas_lags",
  original = "p",
  func = list(fun = "sample_size", pkg = "dials"),
  has_submodel = FALSE
)

set_model_arg(
  "narx_neuralnet",
  eng = "nnetar",
  parsnip = "seas_lags",
  original = "P",
  func = list(fun = "sample_size", pkg = "dials"),
  has_submodel = FALSE
)

set_model_arg(
  "narx_neuralnet",
  eng = "nnetar",
  parsnip = "penalty",
  original = "decay",
  func = list(fun = "penalty", pkg = "dials"),
  has_submodel = FALSE
)


narx_neuralnet <-
  function(mode = "regression",  
           hidden_units = NULL, 
           epochs = NULL,
           nonseas_lags = NULL,
           seas_lags = NULL,
           penalty = NULL) {
    # Check for correct mode
    if (mode  != "regression") {
      stop("`mode` should be 'regression'", call. = FALSE)
    }
    
    # Capture the arguments in quosures
    args <- list(
      hidden_units = rlang::enquo(hidden_units),
      epochs = rlang::enquo(epochs),
      nonseas_lags = rlang::enquo(nonseas_lags),
      seas_lags = rlang::enquo(seas_lags),
      penalty = rlang::enquo(penalty)
      )
    
    # Save some empty slots for future parts of the specification
    out <- list(args = args, 
                eng_args = NULL,
                mode = mode,
                method = NULL, 
                engine = NULL)
    
    # set classes in the correct order
    class(out) <- make_classes("narx_neuralnet")
    out
  }

set_fit(
  model = "narx_neuralnet",
  eng = "nnetar",
  mode = "regression",
  value = list(
    interface = "matrix",
    protect = c("y", "xreg"),
    func = c(pkg = "forecast", fun = "nnetar"),
    defaults = list()
  )
)


show_model_info("narx_neuralnet")



set_pred(
  model = "narx_neuralnet",
  eng = "nnetar",
  mode = "regression",
  type = "numeric",
  value = list(
    pre = NULL,
    post = function(results, object) {
      results$mean %>% 
        tibble::tibble(.pred = .)
    },
    func = c(pkg = "forecast", fun = "forecast"),
    args =
      list(
        object = expr(object$fit),
        xreg = expr(xreg),
        type = "response"
      )
  )
)


show_model_info("narx_neuralnet")


narx_neuralnet(hidden_units = 5) %>%
  translate(engine = "nnetar")

Everything that you have here looks great.

The issue that is going to come up is related to forecast taking a one-dimensional object as input. tidymodels assumes that you will have independent and dependent variables in different data structures. You could write a wrapper function that takes x (the index or date object) and y (the outcome) and convert it to a ts object before passing it to forecast::netar(). You can call the wrapper in set_fit() or use it as the pre argument. You would also have to do the same for predictions I think.

If you are interested in that, maybe start an issue in parsnip; it would be easier to prototype something there.

It looks like @mdancho has some of this worked out in his modeltime vignette.

Yes, modeltime is what I'd point you to. I haven't integrated NNETAR yet, but I have every other model from the forecast and prophet package integrated. Use the development version as I'm still making changes that will go into Modeltime 0.1.0 - set to release to CRAN next week.

Blog Article - Introducing Modeltime

Documentation

Thanks a lot for your replies. I will have a look at it today and try to get as far as possible on my own before reaching out again. Thanks again!

@Max, @mdancho
I created an issue here:
https://github.com/business-science/modeltime/issues/30

Thanks again for your hints,
Max

This topic was automatically closed 21 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.