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?
CodePudding user response:
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)
CodePudding user response:
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>