tidyverts / feasts

Feature Extraction And Statistics for Time Series
https://feasts.tidyverts.org/
291 stars 23 forks source link

forecast x13 special #151

Open zac-garland opened 2 years ago

zac-garland commented 2 years ago

Is the intended behavior of the forecast special within the x13 model to match that of the seasonal package?

By way of example, with the forecast.save and forecast.maxlead arguments the return behavior is different than the seasonal example (could be user error).

Is it intended that this would be performed within a downstream model and forecast or is there a method for extracting these from the feasts special?

I appreciate the help and guidance in advance.

library(fpp3)
#> ── Attaching packages ──────────────────────────────────────────── fpp3 0.4.0 ──
#> ✓ tibble      3.1.6          ✓ tsibble     1.1.1     
#> ✓ dplyr       1.0.7          ✓ tsibbledata 0.4.0     
#> ✓ tidyr       1.2.0          ✓ feasts      0.2.2.9000
#> ✓ lubridate   1.8.0          ✓ fable       0.3.1     
#> ✓ ggplot2     3.3.5
#> ── Conflicts ───────────────────────────────────────────────── fpp3_conflicts ──
#> x lubridate::date()    masks base::date()
#> x dplyr::filter()      masks stats::filter()
#> x tsibble::intersect() masks base::intersect()
#> x tsibble::interval()  masks lubridate::interval()
#> x dplyr::lag()         masks stats::lag()
#> x tsibble::setdiff()   masks base::setdiff()
#> x tsibble::union()     masks base::union()
library(seasonal)
#> 
#> Attaching package: 'seasonal'
#> The following object is masked from 'package:tibble':
#> 
#>     view
seas_fcast <- seas(
  x = AirPassengers,
  transform.function = "log",
  forecast.maxlead = 36,
  forecast.save = "fct"
)
str(seas_fcast)
#> List of 13
#>  $ series     :List of 8
#>   ..$ fct: Time-Series [1:36, 1:3] from 1961 to 1964: 444 414 466 498 498 ...
#>   .. ..- attr(*, "dimnames")=List of 2
#>   .. .. ..$ : NULL
#>   .. .. ..$ : chr [1:3] "forecast" "lowerci" "upperci"
#>   ..$ rsd: Time-Series [1:144] from 1949 to 1961: -0.000686 0.006451 -0.005287 0.008333 -0.009011 ...
#>   ..$ s10: Time-Series [1:144] from 1949 to 1961: 0.902 0.954 1.07 1.002 0.949 ...
#>   ..$ s11: Time-Series [1:144] from 1949 to 1961: 123 125 125 128 127 ...
#>   ..$ s12: Time-Series [1:144] from 1949 to 1961: 123 124 126 127 127 ...
#>   ..$ s13: Time-Series [1:144] from 1949 to 1961: 0.998 1.004 0.996 1.006 1.003 ...
#>   ..$ s16: Time-Series [1:144] from 1949 to 1961: 0.913 0.946 1.055 1.012 0.95 ...
#>   ..$ s18: Time-Series [1:144] from 1949 to 1961: 0.913 0.946 1.055 1.012 0.95 ...
#>  $ udg        : Named chr [1:344] "Aug  6, 2022" "09.41.27" "1.1" "57" ...
#>   ..- attr(*, "names")= chr [1:344] "date" "time" "version" "build" ...
#>  $ data       : Time-Series [1:144, 1:6] from 1949 to 1961: 123 125 125 128 127 ...
#>   ..- attr(*, "dimnames")=List of 2
#>   .. ..$ : NULL
#>   .. ..$ : chr [1:6] "final" "seasonal" "seasonaladj" "trend" ...
#>  $ err        :List of 3
#>   ..$ error  : list()
#>   ..$ warning: list()
#>   ..$ note   : list()
#>   ..- attr(*, "class")= chr "x13messages"
#>  $ est        :List of 5
#>   ..$ reg         :'data.frame': 3 obs. of  5 variables:
#>   .. ..$ group         : chr [1:3] "1-Coefficient Trading Day" "Easter[1]" "Automatically Identified Outliers"
#>   .. ..$ variable      : chr [1:3] "Weekday" "Easter[1]" "AO1951.May"
#>   .. ..$ estimate      : chr [1:3] "-0.294969914081430E-02" "+0.177673735674792E-01" "+0.100155824411322E+00"
#>   .. ..$ standard.error: chr [1:3] "+0.523191799014611E-03" "+0.715802978553251E-02" "+0.204386646810968E-01"
#>   .. ..$ fixed         : chr [1:3] "       " "       " "       "
#>   ..$ arima       :'data.frame': 2 obs. of  7 variables:
#>   .. ..$ operator      : chr [1:2] "MA" "MA"
#>   .. ..$ factor        : chr [1:2] "Nonseasonal" "Seasonal"
#>   .. ..$ period        : chr [1:2] "01" "12"
#>   .. ..$ lag           : chr [1:2] "01" "12"
#>   .. ..$ estimate      : chr [1:2] "+0.11562041392576E+00" "+0.49736001930226E+00"
#>   .. ..$ standard.error: chr [1:2] "+0.85858806039688E-01" "+0.77467729778807E-01"
#>   .. ..$ fixed         : chr [1:2] "       " "       "
#>   ..$ variance    :'data.frame': 2 obs. of  2 variables:
#>   .. ..$ V1: chr [1:2] "mle" "se"
#>   .. ..$ V2: num [1:2] 0.000954 0.000118
#>   ..$ coefficients: Named num [1:5] -0.00295 0.01777 0.10016 0.11562 0.49736
#>   .. ..- attr(*, "names")= chr [1:5] "Weekday" "Easter[1]" "AO1951.May" "MA-Nonseasonal-01" ...
#>   ..$ se          : Named num [1:5] 0.000523 0.007158 0.020439 0.085859 0.077468
#>   .. ..- attr(*, "names")= chr [1:5] "Weekday" "Easter[1]" "AO1951.May" "MA-Nonseasonal-01" ...
#>  $ model      :List of 2
#>   ..$ regression:List of 2
#>   .. ..$ variables: chr [1:3] "td1coef" "easter[1]" "ao1951.May"
#>   .. ..$ b        : num [1:3] -0.00295 0.01777 0.10016
#>   ..$ arima     :List of 2
#>   .. ..$ model: chr "(0 1 1)(0 1 1)"
#>   .. ..$ ma   : num [1:2] 0.116 0.497
#>   ..- attr(*, "class")= chr [1:2] "spclist" "list"
#>  $ fivebestmdl: chr [1:11] "<p>Best Five ARIMA Models</p>" "" "<p class=\"indent\">" "     Model #  1 : (0 1 0)(0 1 1) (<abbr title=\"Bayesian information criterion 2\">BIC2</abbr> =     -4.007) <br> " ...
#>  $ wdir       : chr "/var/folders/lk/w50zlm7n5sl0nbp83vn3wgb00000gn/T//RtmpRuE4xv/x13114ad13fa2378"
#>  $ iofile     : chr "/var/folders/lk/w50zlm7n5sl0nbp83vn3wgb00000gn/T//RtmpRuE4xv/x13114ad13fa2378/iofile"
#>  $ call       : language seas(x = AirPassengers, transform.function = "log", forecast.maxlead = 36,      forecast.save = "fct")
#>  $ list       :List of 4
#>   ..$ x                 : Time-Series [1:144] from 1949 to 1961: 112 118 132 129 121 135 148 148 136 119 ...
#>   ..$ transform.function: chr "log"
#>   ..$ forecast.maxlead  : num 36
#>   ..$ forecast.save     : chr "fct"
#>  $ x          : Time-Series [1:144] from 1949 to 1961: 112 118 132 129 121 135 148 148 136 119 ...
#>  $ spc        :List of 9
#>   ..$ series    :List of 4
#>   .. ..$ title : chr "\"iofile\""
#>   .. ..$ file  : chr "\"/var/folders/lk/w50zlm7n5sl0nbp83vn3wgb00000gn/T//RtmpRuE4xv/x13114ad13fa2378/iofile.dta\""
#>   .. ..$ format: chr "\"datevalue\""
#>   .. ..$ period: num 12
#>   ..$ transform :List of 2
#>   .. ..$ function: chr "log"
#>   .. ..$ print   : chr "aictransform"
#>   ..$ seats     :List of 2
#>   .. ..$ noadmiss: chr "yes"
#>   .. ..$ save    : chr [1:6] "s10" "s11" "s12" "s13" ...
#>   ..$ regression:List of 1
#>   .. ..$ aictest: chr [1:2] "td" "easter"
#>   ..$ outlier   : list()
#>   ..$ automdl   :List of 1
#>   .. ..$ print: chr "bestfivemdl"
#>   ..$ forecast  :List of 2
#>   .. ..$ maxlead: num 36
#>   .. ..$ save   : chr "fct"
#>   ..$ estimate  :List of 1
#>   .. ..$ save: chr [1:3] "model" "estimates" "residuals"
#>   ..$ spectrum  :List of 1
#>   .. ..$ print: chr "qs"
#>   ..- attr(*, "class")= chr [1:2] "spclist" "list"
#>  - attr(*, "class")= chr "seas"
tsibble::as_tsibble(seas_fcast$series$fct) %>% autoplot()
#> Plot variable not specified, automatically selected `.vars = value`

AirPassengers %>% 
  tsibble::as_tsibble() %>% 
  model(
    seats = X_13ARIMA_SEATS(value ~ transform(`function` = "log") + forecast(maxlead = 36,save = "fct"))
  ) %>% 
  components() -> out_mod 

out_mod %>% 
  str()
#> dcmp_ts [144 × 7] (S3: dcmp_ts/tbl_ts/tbl_df/tbl/data.frame)
#>  $ .model       : chr [1:144] "seats" "seats" "seats" "seats" ...
#>  $ index        : mth [1:144] 1949 Jan, 1949 Feb, 1949 Mar, 1949 Apr, 1949 May, 1949 Jun...
#>  $ value        : num [1:144] 112 118 132 129 121 135 148 148 136 119 ...
#>  $ trend        : num [1:144] 123 124 126 127 127 ...
#>  $ seasonal     : num [1:144] 0.913 0.946 1.055 1.012 0.95 ...
#>  $ irregular    : num [1:144] 0.998 1.004 0.996 1.006 1.003 ...
#>  $ season_adjust: num [1:144] 123 125 125 128 127 ...
#>  - attr(*, "key")= tibble [1 × 2] (S3: tbl_df/tbl/data.frame)
#>   ..$ .model: chr "seats"
#>   ..$ .rows : list<int> [1:1] 
#>   .. ..$ : int [1:144] 1 2 3 4 5 6 7 8 9 10 ...
#>   .. ..@ ptype: int(0) 
#>   ..- attr(*, ".drop")= logi TRUE
#>  - attr(*, "index")= chr "index"
#>   ..- attr(*, "ordered")= logi TRUE
#>  - attr(*, "index2")= chr "index"
#>  - attr(*, "interval")= interval [1:1] 1M
#>   ..@ .regular: logi TRUE
#>  - attr(*, "method")= chr "X-13ARIMA-SEATS"
#>  - attr(*, "response")= chr "value"
#>  - attr(*, "seasons")=List of 1
#>   ..$ seasonal:List of 2
#>   .. ..$ period: int 12
#>   .. ..$ base  : num 0
#>  - attr(*, "aliases")=List of 2
#>   ..$ season_adjust: language f(trend, irregular)
#>   ..$ value        : language f(trend, seasonal, irregular)

Created on 2022-08-06 by the reprex package (v2.0.1)

mitchelloharawild commented 1 year ago

Hi, the forecasts do work internally since {feasts} directly uses {seasonal} to produce the results. However they are not exposed by the components() method since the forecasts are not part of the decomposition of the model.

library(feasts)
#> Loading required package: fabletools
fit_36 <- AirPassengers %>% 
  tsibble::as_tsibble() %>% 
  model(
    seats = X_13ARIMA_SEATS(value ~ transform(`function` = "log") + forecast(maxlead = 36,save = "fct"))
  )

Forecasts exist in model object:

fit_36$seats[[1]]$fit$fit$series$fct
#>          forecast  lowerci   upperci
#> Jan 1961 444.2964 418.1670  472.0585
#> Feb 1961 413.5093 381.4051  448.3158
#> Mar 1961 465.5498 422.4688  513.0240
#> Apr 1961 497.5508 445.3449  555.8766
#> May 1961 498.3558 440.6152  563.6630
#> Jun 1961 575.2956 503.0633  657.8994
#> Jul 1961 670.7166 580.5151  774.9337
#> Aug 1961 654.9312 561.3962  764.0501
#> Sep 1961 556.7305 472.9611  655.3369
#> Oct 1961 490.9876 413.5452  582.9322
#> Nov 1961 422.5169 352.9779  505.7555
#> Dec 1961 478.1585 396.3657  576.8297
#> Jan 1962 481.0627 391.6394  590.9040
#> Feb 1962 452.3742 362.7822  564.0917
#> Mar 1962 514.5911 406.8467  650.8694
#> Apr 1962 538.7240 420.2625  690.5767
#> May 1962 545.1952 419.9158  707.8509
#> Jun 1962 635.8976 483.9242  835.5973
#> Jul 1962 726.2196 546.2771  965.4347
#> Aug 1962 716.4868 532.9609  963.2101
#> Sep 1962 615.3769 452.8749  836.1884
#> Oct 1962 531.6177 387.1698  729.9571
#> Nov 1962 462.2284 333.2868  641.0547
#> Dec 1962 523.0996 373.5303  732.5597
#> Jan 1963 526.2768 369.1401  750.3038
#> Feb 1963 494.8919 341.6832  716.7984
#> Mar 1963 568.7985 386.7674  836.5021
#> Apr 1963 583.3043 390.8428  870.5389
#> May 1963 596.4370 394.0242  902.8305
#> Jun 1963 702.8835 458.0716 1078.5327
#> Jul 1963 786.3155 505.6679 1222.7235
#> Aug 1963 791.9620 502.8166 1247.3808
#> Sep 1963 666.3004 417.7790 1062.6581
#> Oct 1963 581.5833 360.2171  938.9870
#> Nov 1963 510.9198 312.7342  834.6995
#> Dec 1963 566.3870 342.6866  936.1157

As far as I’m aware, the forecasts from X-13ARIMA-SEATS are from the regARIMA model it uses, so be mindful that the forecasts won’t use all options specified for the decomposition. Note that even though x11() is specified for seasonal adjustment, the forecasts do not change.

fit_12 <- AirPassengers %>% 
  tsibble::as_tsibble() %>% 
  model(
    seats = X_13ARIMA_SEATS(value ~ transform(`function` = "log")  + x11() + forecast(maxlead = 12,save = "fct"))
  )
waldo::compare(
  fit_12$seats[[1]]$fit$fit$series$fct[1:12,],
  fit_36$seats[[1]]$fit$fit$series$fct[1:12,]
)
#> ✔ No differences

Created on 2022-08-25 by the reprex package (v2.0.1)

I would like to add a forecast() method for X_13ARIMA_SEATS() which will allow you to extract or produce forecasts, but I think the interface is a bit unusual since the forecast horizon needs to be specified upfront when modelling.

mcaselli commented 1 year ago

I would like to add a forecast() method for X_13ARIMA_SEATS() which will allow you to extract or produce forecasts, but I think the interface is a bit unusual since the forecast horizon needs to be specified upfront when modelling.

I've been struggling to figure out how to incorporate forecasts from X_13ARIMA_SEATS into a tsibble/feasts workflow, e.g. to use the feasts graphics methods on a forecast from X_13ARIMA_SEATS.

In leiu of a more integrated approach (i'd love the forecast() method you describe! but I too see the issues with the interface mismatch) are you aware of any good workarounds to get back into the tsibble/feasts world after forecasting with X_13_ARIMA_SEATS?

I'm thinking some pivoting and rbinding of the original data and the forecast data back into a new tsibble might do the trick, but is there something more elegant than that?

Thanks!!

mitchelloharawild commented 1 year ago

I'm working on writing a forecast() method that I described above, however I'm having some trouble with determining how they compute the intervals. When I produce what I expected to be the correct distribution from the forecasts I get slightly different intervals.

As for using X_13ARIMA_SEATS forecasts in tsibble/feasts, I guess it depends what you're trying to do. If you're trying to plot the forecasts, you'll want to convert the mts object shown above (forecast, lowerci, upperci) into a fable with a distribution column for the forecasted values. Then you can plot these forecasts with autoplot(<fable>), or make a custom graphic using the {ggdist} package. If you just wanted to plot the forecasts without uncertainty, you can directly use the forecast column and convert the mts into a tsibble with as_tsibble().

mitchelloharawild commented 1 year ago

I've pushed my work in progress code to the forecastx13 branch of this repo.

Here's how it works, and how the intervals currently slightly differ for h=2:

library(feasts)
#> Loading required package: fabletools
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
fit_36 <- AirPassengers %>% 
  tsibble::as_tsibble() %>% 
  model(
    seats = X_13ARIMA_SEATS(value ~ forecast(maxlead = 36,lognormal = "no", save = c("ftr", "fct", "fvr")))
  )

fit_36 %>% 
  forecast() %>% 
  mutate(hilo(value, 95), median(value))
#> # A fable: 24 x 6 [1M]
#> # Key:     .model [1]
#>    .model    index            value .mean      `hilo(value, 95)` `median(value)`
#>    <chr>     <mth>           <dist> <dbl>                 <hilo>           <dbl>
#>  1 seats  1961 Jan lN(6.1, 0.00096)  445. [418.1670, 472.0585]95            444.
#>  2 seats  1961 Feb    lN(6, 0.0017)  418. [384.8105, 452.3187]95            417.
#>  3 seats  1961 Mar  lN(6.1, 0.0025)  466. [422.4688, 513.0240]95            466.
#>  4 seats  1961 Apr  lN(6.2, 0.0032)  498. [445.3449, 555.8766]95            498.
#>  5 seats  1961 May  lN(6.2, 0.0039)  499. [440.6152, 563.6630]95            498.
#>  6 seats  1961 Jun  lN(6.4, 0.0047)  577. [503.0633, 657.8994]95            575.
#>  7 seats  1961 Jul  lN(6.5, 0.0054)  673. [580.5151, 774.9337]95            671.
#>  8 seats  1961 Aug  lN(6.5, 0.0062)  657. [561.3962, 764.0501]95            655.
#>  9 seats  1961 Sep  lN(6.3, 0.0069)  559. [472.9611, 655.3369]95            557.
#> 10 seats  1961 Oct  lN(6.2, 0.0077)  493. [413.5452, 582.9322]95            491.
#> # … with 14 more rows

fit_36$seats[[1]]$fit$fit$series$fct[1:12,]
#>       forecast  lowerci  upperci
#>  [1,] 444.2964 418.1670 472.0585
#>  [2,] 413.5093 381.4051 448.3158
#>  [3,] 465.5498 422.4688 513.0240
#>  [4,] 497.5508 445.3449 555.8766
#>  [5,] 498.3558 440.6152 563.6630
#>  [6,] 575.2956 503.0633 657.8994
#>  [7,] 670.7166 580.5151 774.9337
#>  [8,] 654.9312 561.3962 764.0501
#>  [9,] 556.7305 472.9611 655.3369
#> [10,] 490.9876 413.5452 582.9322
#> [11,] 422.5169 352.9779 505.7555
#> [12,] 478.1585 396.3657 576.8297

Created on 2022-08-27 by the reprex package (v2.0.1)

zac-garland commented 1 year ago

1st & foremost 🤓: hooray </code>

this is on my radar and appreciate the response. plan to go through the examples in more depth and provide back more code.

I think the end goal - in my mind is how do we swap between nsa/sa models & forecasts e.g.

components() %>% forecast() %>% reCompose()

I think the seasonality forecasts get us a portion of the way there

thoughts?

mitchelloharawild commented 1 year ago

I'm not quite sure what you mean here, are you trying to produce forecasts of seasonal and seasonally adjusted data?

If so, you would typically do: data %>% model(<decomposition>) %>% components() %>% model(<decomposed series, seasonal or not>) %>% forecast().

Also do you have more familiarity with X13-ARIMA-SEATS, and can explain why the forecasts differ for h=2 only? It seems really weird to me and is the only thing preventing me from including this in the next CRAN release.

zac-garland commented 1 year ago

I can take a look on the 2nd comment!

on the first, I think we want to treat it like a hierarchical forecast, where we forecast the seasonal component and the SA series separately and then recombine into the NSA forecast and historical contribution.

does that help?

mitchelloharawild commented 1 year ago

For a hierarchical reconciliation of decomposed data, you can set this up by manipulating the components data:

library(fable)
#> Loading required package: fabletools
library(feasts)
library(tidyr)
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union

# Prepare/tidy dataset
as_tsibble(USAccDeaths) %>% 
  # Specify and estimate model for decomposition
  model(STL(value)) %>% 
  # Extract decomposed data
  components() %>% 
  # Convert components of interest into long form
  select(index, season_year, season_adjust) %>% 
  pivot_longer(c(season_year, season_adjust), names_to = "component") %>% 
  # Compute aggregate (the original data) from decomposition
  aggregate_key(component, value = sum(value)) %>% 
  # Model each series
  model(ets = ETS(value)) %>%
  # Impose reconciliation constraint
  reconcile(ets_dcmp_rec = min_trace(ets)) %>% 
  # Produce forecasts
  forecast(h = "2 years") %>% 
  # Optionally, focus only on original series
  filter(is_aggregated(component)) %>%
  # Plot and compare forecasts 
  # (seems reconciled forecasts have smaller intervals but similar means)
  autoplot()

Created on 2022-08-31 by the reprex package (v2.0.1)

I'd be very interested to hear about what you think of the h=2 differences if you have the chance to look into it.

zac-garland commented 1 year ago

@mitchelloharawild I connected with one of my colleagues and we believe this is due to leap-year adjustments in the x13 model. This would line up with the forecasts differing in February / h=2 let me know if you see something similar.

AQLT commented 1 year ago

If I can intervene in the conversation, this certainly come from the pre-leap-year adjustment. Indeed, by default, when a log-transformation is used the series is pre-adjusted from leap-year instead of adding an external regressor in the regarima model. In that case the month of February is renormalise to corresponds to a month of length 28.5: the values are multiplied by 28.25/28 in leap years and 28.25/29 otherwise. This correction is not performed with additive models (and as you see no difference in that case for h=2)

library(feasts)
library(dplyr)
fit_36 <- AirPassengers %>% 
  tsibble::as_tsibble() %>% 
  model(
    seats = X_13ARIMA_SEATS(value ~ forecast(maxlead = 36,lognormal = "no", save = c("ftr", "fct", "fvr")))
  )
fit_36$seats[[1]]$fit$fit # no leap year regressor
#> 
#> Call:
#> NULL
#> 
#> Coefficients:
#>           Weekday          Easter[1]         AO1951.May  MA-Nonseasonal-01  
#>          -0.00295            0.01777            0.10016            0.11562  
#>    MA-Seasonal-12  
#>           0.49736

fit_36 %>% 
  forecast() %>% 
  mutate(hilo(value, 95), median(value))
#> # A fable: 24 x 6 [1M]
#> # Key:     .model [1]
#>    .model      index            value .mean      `hilo(value, 95)` median(valu…¹
#>    <chr>       <mth>           <dist> <dbl>                 <hilo>         <dbl>
#>  1 seats  1961 janv. lN(6.1, 0.00096)  445. [418.1670, 472.0585]95          444.
#>  2 seats  1961 févr.    lN(6, 0.0017)  418. [384.8105, 452.3187]95          417.
#>  3 seats   1961 mars  lN(6.1, 0.0025)  466. [422.4688, 513.0240]95          466.
#>  4 seats   1961 avr.  lN(6.2, 0.0032)  498. [445.3449, 555.8766]95          498.
#>  5 seats    1961 mai  lN(6.2, 0.0039)  499. [440.6152, 563.6630]95          498.
#>  6 seats   1961 juin  lN(6.4, 0.0047)  577. [503.0633, 657.8994]95          575.
#>  7 seats  1961 juil.  lN(6.5, 0.0054)  673. [580.5151, 774.9337]95          671.
#>  8 seats   1961 août  lN(6.5, 0.0062)  657. [561.3962, 764.0501]95          655.
#>  9 seats  1961 sept.  lN(6.3, 0.0069)  559. [472.9611, 655.3369]95          557.
#> 10 seats   1961 oct.  lN(6.2, 0.0077)  493. [413.5452, 582.9322]95          491.
#> # … with 14 more rows, and abbreviated variable name ¹​`median(value)`
# 1961 is not a leap year
diag(c(1, 28.25/28, rep(1,10))) %*% 
  fit_36$seats[[1]]$fit$fit$series$fct[1:12,] # same value
#>       forecast  lowerci  upperci
#>  [1,] 444.2964 418.1670 472.0585
#>  [2,] 417.2013 384.8105 452.3187
#>  [3,] 465.5498 422.4688 513.0240
#>  [4,] 497.5508 445.3449 555.8766
#>  [5,] 498.3558 440.6152 563.6630
#>  [6,] 575.2956 503.0633 657.8994
#>  [7,] 670.7166 580.5151 774.9337
#>  [8,] 654.9312 561.3962 764.0501
#>  [9,] 556.7305 472.9611 655.3369
#> [10,] 490.9876 413.5452 582.9322
#> [11,] 422.5169 352.9779 505.7555
#> [12,] 478.1585 396.3657 576.8297

fit_36_add <- AirPassengers %>% 
  tsibble::as_tsibble() %>% 
  model(
    seats = X_13ARIMA_SEATS(value ~ forecast(maxlead = 36,lognormal = "no", save = c("ftr", "fct", "fvr")) + transform(`function` = "none"))
  )
fit_36_add$seats[[1]]$fit$fit # leap year regressor
#> 
#> Call:
#> NULL
#> 
#> Coefficients:
#>          Constant          Leap Year            Weekday          Easter[1]  
#>           30.6208            11.3210            -0.9036             6.8937  
#> AR-Nonseasonal-01  
#>            0.8193

fit_36_add %>% 
  forecast() %>% 
  mutate(hilo(value, 95), median(value))
#> # A fable: 24 x 6 [1M]
#> # Key:     .model [1]
#>    .model      index       value .mean      `hilo(value, 95)` `median(value)`
#>    <chr>       <mth>      <dist> <dbl>                 <hilo>           <dbl>
#>  1 seats  1961 janv. N(439, 100)  439. [419.3149, 458.4862]95            439.
#>  2 seats  1961 févr. N(407, 178)  407. [380.5100, 432.7901]95            407.
#>  3 seats   1961 mars N(446, 212)  446. [417.3459, 474.4348]95            446.
#>  4 seats   1961 avr. N(492, 244)  492. [461.1230, 522.3311]95            492.
#>  5 seats    1961 mai N(497, 266)  497. [464.9678, 528.9404]95            497.
#>  6 seats   1961 juin N(564, 281)  564. [530.7221, 596.4164]95            564.
#>  7 seats  1961 juil. N(651, 291)  651. [617.4773, 684.4027]95            651.
#>  8 seats   1961 août N(635, 299)  635. [601.3547, 669.1328]95            635.
#>  9 seats  1961 sept. N(541, 305)  541. [506.4526, 574.8578]95            541.
#> 10 seats   1961 oct. N(488, 309)  488. [453.1060, 521.9616]95            488.
#> # … with 14 more rows
fit_36_add$seats[[1]]$fit$fit$series$fct[1:12,]
#>       forecast  lowerci  upperci
#>  [1,] 438.9006 419.3149 458.4862
#>  [2,] 406.6501 380.5100 432.7901
#>  [3,] 445.8903 417.3459 474.4348
#>  [4,] 491.7271 461.1230 522.3311
#>  [5,] 496.9541 464.9678 528.9404
#>  [6,] 563.5693 530.7221 596.4164
#>  [7,] 650.9400 617.4773 684.4027
#>  [8,] 635.2437 601.3547 669.1328
#>  [9,] 540.6552 506.4526 574.8578
#> [10,] 487.5338 453.1060 521.9616
#> [11,] 419.8635 385.3110 454.4159
#> [12,] 465.1630 430.4826 499.8433

Hi, the forecasts do work internally since {feasts} directly uses {seasonal} to produce the results. However they are not exposed by the components() method since the forecasts are not part of the decomposition of the model.

Note that the forecasts have an impact in the decomposition, especially with the X-11 decomposition: the decomposes series is a series extended with forecasts.

Note that even though x11() is specified for seasonal adjustment, the forecasts do not change.

This is normal because the forecasts only depends on the pre-adjustment process and SEATS/X-11 is the decomposition algorithm.