How to use a character value when using the 'model' function to call the model/variable to forecast?

123 Views Asked by At

My aim is to make a function where you input the variable you want forecasted, and then use cross validation on multiple types of models (ie. Naive, ETS, Mean), then using the 'pull' function I will pull out the model with the lowest RMSE, then I will forecast 1 step ahead with the best model.

However, I am having trouble with the second last line, using the character value 'best.model' as the input for the model when forecasting. (Error is below the code, which you an run yourself).

Here is the code to make more sense:

library(fpp3)
tsibble <- prices

function.fc <- function (variable) {

## cross validation models
cv.fit <- tsibble %>%
  select(year, !!sym(variable)) %>%
  stretch_tsibble(.init = 180, .step = 1) %>%
  filter(.id != max(.id)) %>%
  model(
    MEAN(!!sym(variable)),
    NAIVE(!!sym(variable))
  ) 

## cv forecasts
cv.fc <- cv.fit %>%
  forecast(h = 1)

## cv accuracy
cv.accuracy <- cv.fc %>%
  accuracy(tsibble)

## pulls out the name of the best model
best.model <- cv.accuracy %>%
  select(.model, .type, RMSE) %>%
  arrange(RMSE) %>%
  filter(row_number(RMSE) == 1) %>%
  pull(.model)

## pulls out 1 step forecast 
fc <- model(.data = tsibble, noquote(best.model)) %>%
  forecast(h = 1)


return(fc)

}


function.fc("copper")

Error: Model definition(s) incorrectly created: noquote(best.model) Check that specified model(s) are model definitions. Run `rlang::last_error()` to see where the error occurred

As you can see I have tried using the 'unquote' function but this still does not work. Does anyone have any suggestions on what to use? I have struggled to find other posts with my problem.

2

There are 2 best solutions below

1
On BEST ANSWER

At the end of your function, best.model is a string. For example, if variable = "copper", then your function produces best.model = "NAIVE(copper)". The model() function requires a model definition to be provided, not strings. You can parse the string as code using rlang::parse_expr() then evaluate it to produce a model definition with rlang::eval_tidy().

library(rlang)
library(fpp3)
#> ── Attaching packages ──────────────────────────────────────────── fpp3 0.4.0 ──
#> ✓ tibble      3.1.0          ✓ tsibble     1.0.0     
#> ✓ dplyr       1.0.5          ✓ tsibbledata 0.3.0     
#> ✓ tidyr       1.1.3          ✓ feasts      0.2.1.9000
#> ✓ lubridate   1.7.10         ✓ fable       0.3.0.9000
#> ✓ ggplot2     3.3.3
best.model <- "NAIVE(copper)"
best.model <- eval_tidy(parse_expr(best.model))
best.model
#> <RW model definition>
model(prices, best.model)
#> # A mable: 1 x 1
#>   best.model
#>      <model>
#> 1    <NAIVE>

Created on 2021-05-11 by the reprex package (v1.0.0)

0
On

Here is a solution that takes a slightly different approach, first defining a named list of models to choose from and then using best.model to choose from the list. This is generally preferable to getting bogged down by non-standard-evaluation. Note also that I've used {{ to 'pipe' unquoted arguments. You'll notice I've also changed some object names. This is because you should generally avoid . in object names to avoid confusion with the S3 system of object-oriented programming in R.

library(fpp3)

my_forecast <- function(data, variable) {
  
  # Define a list of models with sensible names
  models <- list(
    mean = fable::MEAN,
    naive = fable::NAIVE
  )
  
  ## cross validation models
  cv_fit <- data %>%
    select(year, {{ variable }}) %>%
    stretch_tsibble(.init = 180, .step = 1) %>%
    filter(.id != max(.id)) %>%
    model(
      mean = models$mean({{ variable }}),
      naive = models$naive({{ variable }})
    ) 
  
  ## cv forecasts
  cv_fc <- cv_fit %>%
    forecast(h = 1)
  
  ## cv accuracy
  cv_accuracy <- cv_fc %>%
    accuracy(data)
  
  ## pulls out the name of the best model
  best_model <- cv_accuracy %>%
    select(.model, .type, RMSE) %>%
    arrange(RMSE) %>%
    filter(row_number() == 1) %>%
    pull(.model)
  
  ## pulls out 1 step forecast 
  fc <- data %>% 
    model("{best_model}" := models[[best_model]]({{ variable }})) %>%
    forecast(h = 1)
  
  fc
  
}

my_forecast(prices, copper)
#> # A fable: 1 x 4 [1Y]
#> # Key:     .model [1]
#>   .model  year       copper .mean
#>   <chr>  <dbl>       <dist> <dbl>
#> 1 naive   1998 N(2.6, 0.69)  2.59

Created on 2021-05-11 by the reprex package (v2.0.0)