2

I am currently in a scenario where I need to fit multiple models where I just change one of the parameters.

library(fpp3)

gas <- us_gasoline %>% filter(year(Week) <= 2004)
gas %>% autoplot(Barrels)
fit <- gas %>%
  model(
    fourier1 = TSLM(Barrels ~ trend() + fourier(K = 1)),
    fourier2 = TSLM(Barrels ~ trend() + fourier(K = 2)),
    fourier3 = TSLM(Barrels ~ trend() + fourier(K = 3)),
    fourier4 = TSLM(Barrels ~ trend() + fourier(K = 4)),
    fourier5 = TSLM(Barrels ~ trend() + fourier(K = 5)),
    fourier6 = TSLM(Barrels ~ trend() + fourier(K = 6)),
    fourier7 = TSLM(Barrels ~ trend() + fourier(K = 7)),
    fourier8 = TSLM(Barrels ~ trend() + fourier(K = 8)),
    fourier9 = TSLM(Barrels ~ trend() + fourier(K = 9))
  )

I would like to have a loop from 1 to n and create a model definition for each of the values of K that I would then feed to the function model. I would like to store the model definitions in a dictionary (list) where I would have separately the model name and the model itself.

Thus far I have been able to create single model formuas and feed them to the function model in a successful manner:

form <- as.formula("Barrels ~ trend() + fourier(K = 1)")
mod <- TSLM(form)

gas %>% 
  model(
    mod
  )

My goal would be to have a set of model definitions created in a loop and stored in a list, which I would then feed to the function model as arguments

# Create model definitions
mods <- list()
for (i in seq(1, 9)) {            
  form <- paste0("Barrels ~ trend() + fourier(K = ", as.character(i), ")")
  mods[[paste0("model_name_", as.character(i))]] <- TSLM(form)
}

In python I would use list unpacking or dict unpacking to achieve this, but I fail to attain this in R. People suggest using do.call but I fail to do this...

Could anybody please help?

Anoushiravan R
  • 21,622
  • 3
  • 18
  • 41
jj_coder
  • 33
  • 4

2 Answers2

1

EDIT After discussion with the OP I decided to propose a solution using injection operator !!:

library(glue)

vec <- c(1:9)
form <- glue("Barrels ~ trend() + fourier(K = {vec})")
model_nm <- glue("model_name_{vec}")


map2_dfc(model_nm, form, ~ gas %>% model(!!{.x} := TSLM(formula(.y))))

# A mable: 1 x 9
  model_name_1 model_name_2 model_name_3 model_name_4 model_name_5 model_name_6 model_name_7
       <model>      <model>      <model>      <model>      <model>      <model>      <model>
1       <TSLM>       <TSLM>       <TSLM>       <TSLM>       <TSLM>       <TSLM>       <TSLM>
# … with 2 more variables: model_name_8 <model>, model_name_9 <model>

First Answer I made a slight modifications to your own code adding formula function to extract formula from your character definitions. As soon as we have a list of mable objects we can apply any function such as fabletools::forecast on each element with purrr::map:

library(purrr)
library(fable)
library(fpp3)

# Create model definitions
mods <- list()

for (i in seq(1, 9)) {            
  form <- paste0("Barrels ~ trend() + fourier(K = ", as.character(i), ")")
  mods[[paste0("model_name_", as.character(i))]] <- gas %>%
    model(fourier1 = TSLM(formula(form)))
}


map(mods, forecasts)
Anoushiravan R
  • 21,622
  • 3
  • 18
  • 41
  • 1
    I see, thank you for your answer and the time you took to examine the issue. If I understand correctly you are storing many different mables of a single model inside the list object. I would however like to create a single mable containing all the objects. Another approach I can think of (perhaps this is better) is to create many 1 model mables and then join them all using bind_rows. – jj_coder Oct 02 '22 at 17:52
  • Check my updates. – Anoushiravan R Oct 02 '22 at 19:34
0

What about something that looks like this. Here we map the numbers you are looking for (k values), then we pivot_wider to get the desired output structure.

library(tidyverse)
library(fpp3)


gas <- us_gasoline %>% filter(year(Week) <= 2004)


tibble(K = 1:9) |>
  mutate(mod = map(K, \(k){
           form <- as.formula(paste0("Barrels ~ trend() + fourier(K =", k, ")"))
           mod <- TSLM(form)
           gas |>
             model(mod)|>
             pull(mod)
         })) |>
  pivot_wider(names_from = K, values_from = mod, names_prefix = "fourier")
#> # A tibble: 1 x 9
#>   fourier1      fourier2      fourier3  fourier4  fourier5  fourier6  fourier7 
#>   <list>        <list>        <list>    <list>    <list>    <list>    <list>   
#> 1 <lst_mdl [1]> <lst_mdl [1]> <lst_mdl> <lst_mdl> <lst_mdl> <lst_mdl> <lst_mdl>
#> # ... with 2 more variables: fourier8 <list>, fourier9 <list>
AndS.
  • 7,748
  • 2
  • 12
  • 17
  • Thank you for your answer! This almost did the trick, but the resulting object is a "tibble" and I need it to be a "mable" (model table). I am trying to convert it with the function "as_mable", but I fail miserable – jj_coder Oct 03 '22 at 05:38
  • Also... each element in your resulting tibble is a list and I want it to be a lst_model... I am sorry. This is also getting on my nerves – jj_coder Oct 03 '22 at 05:52