tidyverts / fable

Tidy time series forecasting
https://fable.tidyverts.org
GNU General Public License v3.0
559 stars 65 forks source link

ARIMA will not produce a weekly forecast when week_start != 1 #397

Open jrauser opened 1 year ago

jrauser commented 1 year ago

See reprex below. If week_start is set to 1 (the default) everything works as expected.

library(fable)
#> Loading required package: fabletools
library(tsibble)
#> 
#> Attaching package: 'tsibble'
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, union
dat<-tsibble(wk=make_yearweek(2023, 21:50, week_start=7), 
             x=rnorm(30,100,10),
             index=wk)
mable <- model(dat, ARIMA(x ~ pdq()))
fable <- mable %>% forecast(h=3)
#> Error in `mutate()`:
#> ℹ In argument: `ARIMA(x ~ pdq()) = (function (object, ...) ...`.
#> Caused by error in `vec_ptype2.yearweek.yearweek()`:
#> ! Can't combine <yearweek> with different `week_start`.
#> Backtrace:
#>      ▆
#>   1. ├─mable %>% forecast(h = 3)
#>   2. ├─generics::forecast(., h = 3)
#>   3. ├─fabletools:::forecast.mdl_df(., h = 3)
#>   4. │ └─dplyr::mutate_at(...)
#>   5. │   ├─dplyr::mutate(.tbl, !!!funs)
#>   6. │   └─dplyr:::mutate.data.frame(.tbl, !!!funs)
#>   7. │     └─dplyr:::mutate_cols(.data, dplyr_quosures(...), by)
#>   8. │       ├─base::withCallingHandlers(...)
#>   9. │       └─dplyr:::mutate_col(dots[[i]], data, mask, new_columns)
#>  10. │         └─mask$eval_all_mutate(quo)
#>  11. │           └─dplyr (local) eval()
#>  12. ├─generics (local) `<fn>`(...)
#>  13. └─fabletools:::forecast.lst_mdl(...)
#>  14.   └─fabletools:::mapply_maybe_parallel(...)
#>  15.     └─base::mapply(FUN = .f, ..., MoreArgs = MoreArgs, SIMPLIFY = SIMPLIFY)
#>  16.       ├─generics (local) `<fn>`(dots[[1L]][[1L]], dots[[2L]][[1L]], h = 3, point_forecast = `<named list>`)
#>  17.       └─fabletools:::forecast.mdl_ts(...)
#>  18.         ├─generics::forecast(...)
#>  19.         └─fable:::forecast.ARIMA(...)
#>  20.           └─vctrs:::`!=.vctrs_vctr`(...)
#>  21.             └─vctrs::vec_equal(e1, e2)
#>  22.               └─vctrs:::vec_cast_common_params(!!!args, .to = .ptype)
#>  23.                 └─vctrs:::vec_cast_common_opts(...)
#>  24.                   └─vctrs (local) `<fn>`()
#>  25.                     └─tsibble:::vec_ptype2.yearweek.yearweek(x = x, y = y, x_arg = x_arg, y_arg = y_arg, call = call)
#>  26.                       └─rlang::abort("Can't combine <yearweek> with different `week_start`.")
new_data <- tsibble(wk=make_yearweek(2023, 51:52, week_start=7), index=wk)
fable <- mable %>% forecast(new_data = new_data)
#> Error in `mutate()`:
#> ℹ In argument: `ARIMA(x ~ pdq()) = (function (object, ...) ...`.
#> Caused by error in `vec_ptype2.yearweek.yearweek()`:
#> ! Can't combine <yearweek> with different `week_start`.
#> Backtrace:
#>      ▆
#>   1. ├─mable %>% forecast(new_data = new_data)
#>   2. ├─generics::forecast(., new_data = new_data)
#>   3. ├─fabletools:::forecast.mdl_df(., new_data = new_data)
#>   4. │ └─dplyr::mutate_at(...)
#>   5. │   ├─dplyr::mutate(.tbl, !!!funs)
#>   6. │   └─dplyr:::mutate.data.frame(.tbl, !!!funs)
#>   7. │     └─dplyr:::mutate_cols(.data, dplyr_quosures(...), by)
#>   8. │       ├─base::withCallingHandlers(...)
#>   9. │       └─dplyr:::mutate_col(dots[[i]], data, mask, new_columns)
#>  10. │         └─mask$eval_all_mutate(quo)
#>  11. │           └─dplyr (local) eval()
#>  12. ├─generics (local) `<fn>`(...)
#>  13. └─fabletools:::forecast.lst_mdl(...)
#>  14.   └─fabletools:::mapply_maybe_parallel(...)
#>  15.     └─base::mapply(FUN = .f, ..., MoreArgs = MoreArgs, SIMPLIFY = SIMPLIFY)
#>  16.       ├─generics (local) `<fn>`(dots[[1L]][[1L]], dots[[2L]][[1L]], h = NULL, point_forecast = `<named list>`)
#>  17.       └─fabletools:::forecast.mdl_ts(...)
#>  18.         ├─generics::forecast(...)
#>  19.         └─fable:::forecast.ARIMA(...)
#>  20.           └─vctrs:::`!=.vctrs_vctr`(...)
#>  21.             └─vctrs::vec_equal(e1, e2)
#>  22.               └─vctrs:::vec_cast_common_params(!!!args, .to = .ptype)
#>  23.                 └─vctrs:::vec_cast_common_opts(...)
#>  24.                   └─vctrs (local) `<fn>`()
#>  25.                     └─tsibble:::vec_ptype2.yearweek.yearweek(x = x, y = y, x_arg = x_arg, y_arg = y_arg, call = call)
#>  26.                       └─rlang::abort("Can't combine <yearweek> with different `week_start`.")
jrauser commented 1 year ago

Possibly related? https://github.com/tidyverts/tsibble/issues/299

jrauser commented 1 year ago

I looked into this a little bit, and the problem comes when the arima model object is created. It has a $tsp which has a $range, where the week_start hasn't been propagated. So the problem appears to be in whatever happens inside range() when a vector of yearweeks are given.

library(fable)
#> Loading required package: fabletools
library(tsibble)
#> 
#> Attaching package: 'tsibble'
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, union
dat<-tsibble(wk=make_yearweek(2023, 21:50, week_start=7), 
             x=rnorm(30,100,10),
             index=wk)
dat$wk[1]
#> <yearweek[1]>
#> [1] "2023 W21"
#> # Week starts on: Sunday
range(dat$wk)
#> <yearweek[2]>
#> [1] "2023 W20" "2023 W49"
#> # Week starts on: Monday
jrauser commented 1 year ago

I think range just does something like c(min(dat$wk), max(dat$wk)), without propagating the attributes of the object.

The code that makes the range at L402 of arima.R needs to be aware of yearweek objects. The right thing might be to make a range() that knows how to operate properly on yearweeks.

jrauser commented 1 year ago

Putting this in my code appears to paper over the problem:

range.yearweek <- function(x, ...) {
  yearweek(c(min(x), max(x)), week_start=attr(x, "week_start"))
}

I don't know how to write that code properly or I'd submit a PR.

mitchelloharawild commented 1 year ago

Thanks for your careful investigation, resolving https://github.com/tidyverts/tsibble/issues/300 should fix this issue.