Skip to contents

Overview

The fiphde (forecasting influenza in support of public health decision making) package provides utilities for forecasting influenza hospitalizations in the United States. fiphde includes functions for retrieving hospitalization time series data from the HHS Protect system at HealthData.gov, preparing raw data for forecasting, fitting time series and count regression models to create probabilistic forecasts for influenza hospitalizations at state and national levels, visualizing and evaluating forecasts, and formatting forecasts for submission to FluSight.

fiphde rhymes with “fifty,” as in the 50 states in the US.

Usage

The fiphde package retrieves current data from HHS and CDC APIs and fits models and forecasts using this data. This vignette uses data current to May 28, 2022 (MMWR epidemiological week 21 of 2022). Running the code here as written will produce different results depending on when you run the code, as new data is constantly being added and historical data is constantly being revised.

To get started, load the packages that are used in this vignette.

Data retrieval

Prior to fitting any forecasts we need to first retrieve data from the HealthData.gov COVID-19 Reported Patient Impact and Hospital Capacity by State Timeseries API. Running get_hdgov_hosp(limitcols=TRUE) will initiate an API call with an argument to return only a selection of fields relevant to flu hospitalization reporting.

hosp <- get_hdgov_hosp(limitcols = TRUE)
hosp
#> # A tibble: 43,967 × 14
#>    state date       flu.admits flu.admits.cov flu.deaths flu.deaths.cov flu.icu
#>    <chr> <date>          <dbl>          <dbl>      <dbl>          <dbl>   <dbl>
#>  1 AL    2019-12-31         NA              0         NA              0      NA
#>  2 HI    2019-12-31         NA              0         NA              0      NA
#>  3 IN    2019-12-31         NA              0         NA              0      NA
#>  4 LA    2019-12-31         NA              0         NA              0      NA
#>  5 MN    2019-12-31         NA              0         NA              0      NA
#>  6 MT    2019-12-31         NA              0         NA              0      NA
#>  7 NC    2019-12-31         NA              0         NA              0      NA
#>  8 PR    2019-12-31         NA              0         NA              0      NA
#>  9 TX    2019-12-31         NA              0         NA              0      NA
#> 10 AL    2020-01-01         NA              0         NA              0      NA
#> # ℹ 43,957 more rows
#> # ℹ 7 more variables: flu.icu.cov <dbl>, flu.tot <dbl>, flu.tot.cov <dbl>,
#> #   cov.admits <dbl>, cov.admits.cov <dbl>, cov.deaths <dbl>,
#> #   cov.deaths.cov <dbl>

Time series forecasting

We will first fit a time series model, creating an ensemble model from ARIMA and exponential smoothing models. Time series modeling is based on the tidyverts (https://tidyverts.org/) collection of packages for tidy time series forecasting in R.

Data preparation

We need to initially prepare the data for a time series forecast. The prep_hdgov_hosp function call below will limit to states only (no territories), remove any data from an incomplete epidemiological week (Sunday-Saturday), remove locations with little to no reported hospitalizations over the last month, and further exclude Washington DC from downstream analysis. The function aggregates total number of cases at each epidemiological week at each location. This function also adds in location FIPS codes, and joins in historical influenza-like illness (ILI) and hospitalization mean values and ranks by week. Historical indicators for ILI and hospitalizations are summarized from CDC ILINet and CDC FluSurv-Net respectively.

# Prep data
prepped_hosp <-
  hosp %>%
  prep_hdgov_hosp(statesonly=TRUE, min_per_week = 0, remove_incomplete = TRUE) %>%
  dplyr::filter(abbreviation != "DC")
prepped_hosp
#> # A tibble: 4,284 × 13
#>    abbreviation location week_start monday     week_end   epiyear epiweek
#>    <chr>        <chr>    <date>     <date>     <date>       <dbl>   <dbl>
#>  1 US           US       2020-10-18 2020-10-19 2020-10-24    2020      43
#>  2 US           US       2020-10-25 2020-10-26 2020-10-31    2020      44
#>  3 US           US       2020-11-01 2020-11-02 2020-11-07    2020      45
#>  4 US           US       2020-11-08 2020-11-09 2020-11-14    2020      46
#>  5 US           US       2020-11-15 2020-11-16 2020-11-21    2020      47
#>  6 US           US       2020-11-22 2020-11-23 2020-11-28    2020      48
#>  7 US           US       2020-11-29 2020-11-30 2020-12-05    2020      49
#>  8 US           US       2020-12-06 2020-12-07 2020-12-12    2020      50
#>  9 US           US       2020-12-13 2020-12-14 2020-12-19    2020      51
#> 10 US           US       2020-12-20 2020-12-21 2020-12-26    2020      52
#> # ℹ 4,274 more rows
#> # ℹ 6 more variables: flu.admits <dbl>, flu.admits.cov <dbl>, ili_mean <dbl>,
#> #   ili_rank <int>, hosp_mean <dbl>, hosp_rank <int>

Now let’s explore the data.

prepped_hosp %>% 
  filter(abbreviation %in% c("US", "CA", "TX", "NY")) %>% 
  ggplot(aes(week_end, flu.admits)) + geom_line() + 
  facet_wrap(~abbreviation, scale="free_y")

What states had the highest admissions over the 2021-2022 flu season?

prepped_hosp %>% 
  filter(abbreviation!="US") %>% 
  filter(week_start>="2021-07-01" & week_end<"2022-06-30") %>% 
  group_by(abbreviation) %>% 
  summarize(total.flu.admits=sum(flu.admits)) %>% 
  arrange(desc(total.flu.admits)) %>% 
  head(10) %>% 
  knitr::kable(caption="Top 10 states with highest flu hospitalizations in 2021-2022.")
Top 10 states with highest flu hospitalizations in 2021-2022.
abbreviation total.flu.admits
TX 7164
FL 4550
PA 3286
CA 3233
NY 3073
AZ 2819
MI 2511
OK 2358
OH 2169
MA 2141

Next let’s turn this into a tsibble. tsibble objects are tibbles with an index variable describing the inherent ordering from past to present, and a key that defines observational units over time. The make_tsibble function provides a convenience wrapper around tsibble::as_tsibble using the epidemiological week’s Monday as the weekly index and the location as the key. Note that the specification of arguments for epidemiological week / year and location key are passed as “bare” (unquoted) names of columns storing this information in the original tibble.

prepped_hosp_tsibble <- make_tsibble(prepped_hosp,
                                     epiyear=epiyear,
                                     epiweek=epiweek,
                                     key=location)
prepped_hosp_tsibble
#> # A tsibble: 4,284 x 14 [1W]
#> # Key:       location [51]
#>    abbreviation location week_start monday        yweek week_end   epiyear
#>    <chr>        <chr>    <date>     <date>       <week> <date>       <dbl>
#>  1 AL           01       2020-10-18 2020-10-19 2020 W43 2020-10-24    2020
#>  2 AL           01       2020-10-25 2020-10-26 2020 W44 2020-10-31    2020
#>  3 AL           01       2020-11-01 2020-11-02 2020 W45 2020-11-07    2020
#>  4 AL           01       2020-11-08 2020-11-09 2020 W46 2020-11-14    2020
#>  5 AL           01       2020-11-15 2020-11-16 2020 W47 2020-11-21    2020
#>  6 AL           01       2020-11-22 2020-11-23 2020 W48 2020-11-28    2020
#>  7 AL           01       2020-11-29 2020-11-30 2020 W49 2020-12-05    2020
#>  8 AL           01       2020-12-06 2020-12-07 2020 W50 2020-12-12    2020
#>  9 AL           01       2020-12-13 2020-12-14 2020 W51 2020-12-19    2020
#> 10 AL           01       2020-12-20 2020-12-21 2020 W52 2020-12-26    2020
#> # ℹ 4,274 more rows
#> # ℹ 7 more variables: epiweek <dbl>, flu.admits <dbl>, flu.admits.cov <dbl>,
#> #   ili_mean <dbl>, ili_rank <int>, hosp_mean <dbl>, hosp_rank <int>

Fit a model and forecast

Next, let’s fit a time series model and create forecasts using the ts_fit_forecast function. This function takes a tsibble created as above, a forecast horizon in weeks, the name of the outcome variable to forecast, and optional covariates to use in the ARIMA model.

Here we fit a non-seasonal ARIMA model with the autoregressive term (p) restricted to 1:2, order of integration for differencing (d) restricted to 0:2, and the moving average (q) restricted to 0 (see ?fable::ARIMA for more information). The model also fits a non-seasonal exponential smoothing model (see ?fable::ETS for details). In this example we do not fit an autoregressive neural network model, but we could change nnetar=NULL to nnetar="AR(P=1)" to do so (see ?fable::nnetar for details). We can also trim the data used in modeling, and here we restrict to only include hospitalization data reported after January 1, 2021. By setting ensemble=TRUE we create an ensemble model that averages the ARIMA and exponential smoothing models.

hosp_fitfor <- ts_fit_forecast(prepped_hosp_tsibble,
                               horizon=4L,
                               outcome="flu.admits",
                               trim_date = "2021-01-01",
                               covariates=TRUE, 
                               models=list(arima='PDQ(0, 0, 0) + pdq(1:2, 0:2, 0)',
                                           ets='season(method="N")',
                                           nnetar=NULL), 
                               ensemble=TRUE)

The function will output messages describing the ARIMA and ETS model formulas passed to fable::ARIMA and fable::ETS.

Trimming to 2021-01-01
ARIMA  formula: flu.admits ~ PDQ(0, 0, 0) + pdq(1:2, 0:2, 0) + hosp_rank + ili_rank
ETS    formula: flu.admits ~ season(method = "N")

After fitting the models, the function then forecasts the outcome to the specified number of weeks. Let’s take a look at the object returned from this model fit + forecast. We see $tsfit, which gives us the ARIMA, ETS, and ensemble model fits as list columns, one row per location; $tsfor gives us the forecast for the next four weeks, one row per model per location (key in the tsibble); $formulas gives us the model formulas passed to fable modeling functions; and if any models failed to converge we would see those locations in $nullmodels.

hosp_fitfor
#> $tsfit
#> # A mable: 51 x 4
#> # Key:     location [51]
#>    location                       arima          ets      ensemble
#>    <chr>                        <model>      <model>       <model>
#>  1 01       <LM w/ ARIMA(2,0,0) errors> <ETS(A,N,N)> <COMBINATION>
#>  2 02       <LM w/ ARIMA(1,0,0) errors> <ETS(A,N,N)> <COMBINATION>
#>  3 04       <LM w/ ARIMA(2,1,0) errors> <ETS(M,N,N)> <COMBINATION>
#>  4 05       <LM w/ ARIMA(2,1,0) errors> <ETS(A,N,N)> <COMBINATION>
#>  5 06       <LM w/ ARIMA(2,2,0) errors> <ETS(M,A,N)> <COMBINATION>
#>  6 08       <LM w/ ARIMA(1,1,0) errors> <ETS(A,N,N)> <COMBINATION>
#>  7 09       <LM w/ ARIMA(1,1,0) errors> <ETS(A,N,N)> <COMBINATION>
#>  8 10       <LM w/ ARIMA(2,1,0) errors> <ETS(A,N,N)> <COMBINATION>
#>  9 12       <LM w/ ARIMA(2,2,0) errors> <ETS(M,N,N)> <COMBINATION>
#> 10 13       <LM w/ ARIMA(2,1,0) errors> <ETS(A,N,N)> <COMBINATION>
#> # ℹ 41 more rows
#> 
#> $tsfor
#> # A fable: 612 x 10 [1W]
#> # Key:     location, .model [153]
#>    location .model    yweek flu.admits .mean epiweek ili_mean ili_rank hosp_mean
#>    <chr>    <chr>    <week>     <dist> <dbl>   <dbl>    <dbl>    <int>     <dbl>
#>  1 01       arima  2022 W22  N(16, 56)  16.2      22    1.23        17         0
#>  2 01       arima  2022 W23  N(20, 56)  20.4      23    1.09        14         0
#>  3 01       arima  2022 W24  N(19, 65)  19.2      24    1.02        12         0
#>  4 01       arima  2022 W25  N(21, 65)  21.0      25    0.984       10         0
#>  5 01       ets    2022 W22  N(19, 78)  18.9      22    1.23        17         0
#>  6 01       ets    2022 W23  N(19, 88)  18.9      23    1.09        14         0
#>  7 01       ets    2022 W24  N(19, 99)  18.9      24    1.02        12         0
#>  8 01       ets    2022 W25 N(19, 110)  18.9      25    0.984       10         0
#>  9 01       ensem… 2022 W22  N(18, 63)  17.5      22    1.23        17         0
#> 10 01       ensem… 2022 W23  N(20, 67)  19.7      23    1.09        14         0
#> # ℹ 602 more rows
#> # ℹ 1 more variable: hosp_rank <int>
#> 
#> $formulas
#> $formulas$arima
#> flu.admits ~ PDQ(0, 0, 0) + pdq(1:2, 0:2, 0) + hosp_rank + ili_rank
#> <environment: 0x55b086e3bce8>
#> 
#> $formulas$ets
#> flu.admits ~ season(method = "N")
#> <environment: 0x55b086e3bce8>
#> 
#> 
#> $nullmodels
#> # A tibble: 0 × 2
#> # ℹ 2 variables: location <chr>, model <chr>

Next, we can format the forecasts for submission to FluSight using the format_for_submission function.

formatted_list <- format_for_submission(hosp_fitfor$tsfor, method = "ts", format = "legacy")

The list contains separate submission-ready tibbles, one element for each type of model fitted.

formatted_list
#> $arima
#> # A tibble: 4,896 × 7
#>    forecast_date target            target_end_date location type  quantile value
#>    <date>        <chr>             <date>          <chr>    <chr> <chr>    <chr>
#>  1 2022-12-20    1 wk ahead inc f… 2022-06-04      01       point NA       17   
#>  2 2022-12-20    2 wk ahead inc f… 2022-06-11      01       point NA       21   
#>  3 2022-12-20    3 wk ahead inc f… 2022-06-18      01       point NA       20   
#>  4 2022-12-20    4 wk ahead inc f… 2022-06-25      01       point NA       22   
#>  5 2022-12-20    1 wk ahead inc f… 2022-06-04      01       quan… 0.010    0    
#>  6 2022-12-20    2 wk ahead inc f… 2022-06-11      01       quan… 0.010    3    
#>  7 2022-12-20    3 wk ahead inc f… 2022-06-18      01       quan… 0.010    1    
#>  8 2022-12-20    4 wk ahead inc f… 2022-06-25      01       quan… 0.010    3    
#>  9 2022-12-20    1 wk ahead inc f… 2022-06-04      01       quan… 0.025    2    
#> 10 2022-12-20    2 wk ahead inc f… 2022-06-11      01       quan… 0.025    6    
#> # ℹ 4,886 more rows
#> 
#> $ensemble
#> # A tibble: 4,896 × 7
#>    forecast_date target            target_end_date location type  quantile value
#>    <date>        <chr>             <date>          <chr>    <chr> <chr>    <chr>
#>  1 2022-12-20    1 wk ahead inc f… 2022-06-04      01       point NA       18   
#>  2 2022-12-20    2 wk ahead inc f… 2022-06-11      01       point NA       20   
#>  3 2022-12-20    3 wk ahead inc f… 2022-06-18      01       point NA       20   
#>  4 2022-12-20    4 wk ahead inc f… 2022-06-25      01       point NA       20   
#>  5 2022-12-20    1 wk ahead inc f… 2022-06-04      01       quan… 0.010    0    
#>  6 2022-12-20    2 wk ahead inc f… 2022-06-11      01       quan… 0.010    1    
#>  7 2022-12-20    3 wk ahead inc f… 2022-06-18      01       quan… 0.010    0    
#>  8 2022-12-20    4 wk ahead inc f… 2022-06-25      01       quan… 0.010    0    
#>  9 2022-12-20    1 wk ahead inc f… 2022-06-04      01       quan… 0.025    3    
#> 10 2022-12-20    2 wk ahead inc f… 2022-06-11      01       quan… 0.025    4    
#> # ℹ 4,886 more rows
#> 
#> $ets
#> # A tibble: 4,896 × 7
#>    forecast_date target            target_end_date location type  quantile value
#>    <date>        <chr>             <date>          <chr>    <chr> <chr>    <chr>
#>  1 2022-12-20    1 wk ahead inc f… 2022-06-04      01       point NA       19   
#>  2 2022-12-20    2 wk ahead inc f… 2022-06-11      01       point NA       19   
#>  3 2022-12-20    3 wk ahead inc f… 2022-06-18      01       point NA       19   
#>  4 2022-12-20    4 wk ahead inc f… 2022-06-25      01       point NA       19   
#>  5 2022-12-20    1 wk ahead inc f… 2022-06-04      01       quan… 0.010    0    
#>  6 2022-12-20    2 wk ahead inc f… 2022-06-11      01       quan… 0.010    0    
#>  7 2022-12-20    3 wk ahead inc f… 2022-06-18      01       quan… 0.010    0    
#>  8 2022-12-20    4 wk ahead inc f… 2022-06-25      01       quan… 0.010    0    
#>  9 2022-12-20    1 wk ahead inc f… 2022-06-04      01       quan… 0.025    2    
#> 10 2022-12-20    2 wk ahead inc f… 2022-06-11      01       quan… 0.025    1    
#> # ℹ 4,886 more rows

We can check to see if the submission is valid. Note that this will fail if the expected date of the target dates and current dates don’t line up. You’ll see a message noting “The submission target end dates do not line up with expected Saturdays by horizon. Note if submission forecast date is not Sunday or Monday, then forecasts are assumed to to start the following week.” If validation succeeds, the $valid element of the returned list will be TRUE, and FALSE if any validation checks fail.

validate_forecast(formatted_list$ensemble)

Let’s plot the forecast with the observed data using the plot_forecast function. Here we plot forecasts with the 50% prediction interval for US, New York (FIPS 36) and Florida (FIPS 12).

plot_forecast(prepped_hosp, formatted_list$ensemble, loc="US", pi = .5)

plot_forecast(prepped_hosp, formatted_list$ensemble, loc="36", pi = .5)

plot_forecast(prepped_hosp, formatted_list$ensemble, loc="12", pi = .5)

Finally, we can pull out the ARIMA model parameters used for each location to save for posterity or retrospective analysis.

hosp_fitfor$tsfit$arima %>% 
  map("fit") %>% 
  map_df("spec") %>% 
  mutate(location = hosp_fitfor$tsfit$location, .before = "p")
#> # A tibble: 51 × 9
#>    location     p     d     q     P     D     Q constant period
#>    <chr>    <int> <int> <dbl> <dbl> <dbl> <dbl> <lgl>     <dbl>
#>  1 01           2     0     0     0     0     0 TRUE         52
#>  2 02           1     0     0     0     0     0 TRUE         52
#>  3 04           2     1     0     0     0     0 FALSE        52
#>  4 05           2     1     0     0     0     0 FALSE        52
#>  5 06           2     2     0     0     0     0 FALSE        52
#>  6 08           1     1     0     0     0     0 FALSE        52
#>  7 09           1     1     0     0     0     0 FALSE        52
#>  8 10           2     1     0     0     0     0 FALSE        52
#>  9 12           2     2     0     0     0     0 FALSE        52
#> 10 13           2     1     0     0     0     0 FALSE        52
#> # ℹ 41 more rows

Count regression forecasting

The time series methods above assume that the outcome has a continuous distribution. When forecasting counts and especially small counts (e.g., “how many influenza hospitalizations will occur next week in Hawaii?”) alternative methods may have more desirable properties. fiphde provides functionality to leverage count regression models for forecasting influenza hospitalizations. Here we will demonstrate how to implement a count regression modeling approach. The example will step through the process of forecasting hospitalizations in Hawaii by using influenza-like illness (ILI) and indicators of historical hospitalization and ILI severity for the given epidemiological weeks. The usage illustrates an automated tuning procedure that finds the “best” models from possible covariates and model families (e.g., Poisson, Quasipoisson, Negative binomial, etc.).

ILI retrieval and prep

Given that ILI will be a covariate in the count regression model, we must first retrieve ILI data using the get_cdc_ili() function, which is a wrapper around ilinet(). Here, we’re only using recent ILI data (i.e., since 2019) and we further filter the signal to only include Hawaii. ILI is subject to revision and may be less reliable when initially reported, so we replace the current week and one week previous with Nowcast data. Finally, we fit a time series model to forecast ILI for the next four weeks.

ilidat <- 
  get_cdc_ili(region=c("state"), years=2019:2022) %>%
  filter(region == "Hawaii") %>%
  replace_ili_nowcast(., weeks_to_replace=1)

ilifor <- forecast_ili(ilidat, horizon=4L, trim_date="2020-03-01")

In the next step we are going to log transform weighted ILI and flu admissions, so in this step we need to remove all the zeros. The fiphde package provides the mnz_replace function which replaces zeros in a variable with the smallest non-zero value of that variable.

ilidat <- ilidat %>% mutate(weighted_ili=mnz_replace(weighted_ili))

ilifor$ilidat <- ilifor$ilidat %>% mutate(ili=mnz_replace(ili))
ilifor$ili_future <- ilifor$ili_future %>% mutate(ili=mnz_replace(ili))
ilifor$ili_bound <- ilifor$ili_bound  %>% mutate(ili=mnz_replace(ili))

Finally, we create our modeling dataset by combining the flu admission data prepared above with the forecasted ILI data in future weeks together with the historical severity data by epidemiological week.

dat_hi <-
  prepped_hosp %>%
  filter(abbreviation=="HI") %>%
  dplyr::mutate(date = MMWRweek::MMWRweek2Date(epiyear, epiweek)) %>%
  left_join(ilifor$ilidat, by = c("epiyear", "location", "epiweek")) %>%
  mutate(ili = log(ili))
dat_hi
#> # A tibble: 84 × 15
#>    abbreviation location week_start monday     week_end   epiyear epiweek
#>    <chr>        <chr>    <date>     <date>     <date>       <dbl>   <dbl>
#>  1 HI           15       2020-10-18 2020-10-19 2020-10-24    2020      43
#>  2 HI           15       2020-10-25 2020-10-26 2020-10-31    2020      44
#>  3 HI           15       2020-11-01 2020-11-02 2020-11-07    2020      45
#>  4 HI           15       2020-11-08 2020-11-09 2020-11-14    2020      46
#>  5 HI           15       2020-11-15 2020-11-16 2020-11-21    2020      47
#>  6 HI           15       2020-11-22 2020-11-23 2020-11-28    2020      48
#>  7 HI           15       2020-11-29 2020-11-30 2020-12-05    2020      49
#>  8 HI           15       2020-12-06 2020-12-07 2020-12-12    2020      50
#>  9 HI           15       2020-12-13 2020-12-14 2020-12-19    2020      51
#> 10 HI           15       2020-12-20 2020-12-21 2020-12-26    2020      52
#> # ℹ 74 more rows
#> # ℹ 8 more variables: flu.admits <dbl>, flu.admits.cov <dbl>, ili_mean <dbl>,
#> #   ili_rank <int>, hosp_mean <dbl>, hosp_rank <int>, date <date>, ili <dbl>

Fit a model and forecast

First we create a list of different count regression models to fit. We can inspect the formulations of each model. Note that these models are specified using the trending package and internally the trendeval package is used to determine the best fitting approach.

models <-
  list(
    poisson = trending::glm_model(flu.admits ~ ili + hosp_rank + ili_rank, family = "poisson"),
    quasipoisson = trending::glm_model(flu.admits ~ ili + hosp_rank + ili_rank, family = "quasipoisson"),
    negbin = trending::glm_nb_model(flu.admits ~ ili + hosp_rank + ili_rank)
  )
models$poisson
#> Untrained trending model:
#>     glm(formula = flu.admits ~ ili + hosp_rank + ili_rank, family = "poisson")
models$quasipoisson
#> Untrained trending model:
#>     glm(formula = flu.admits ~ ili + hosp_rank + ili_rank, family = "quasipoisson")
models$negbin
#> Untrained trending model:
#>     glm.nb(formula = flu.admits ~ ili + hosp_rank + ili_rank)

We must next project covariates four weeks into the future, which we can accomplish by pulling from the forecasted ILI and historical severity data.

new_cov <-
  ilifor$ili_future %>%
  left_join(fiphde:::historical_severity, by="epiweek") %>%
  select(-epiweek,-epiyear) %>%
  mutate(ili = log(ili))
new_cov
#> # A tibble: 4 × 6
#>   location   ili ili_mean ili_rank hosp_mean hosp_rank
#>   <chr>    <dbl>    <dbl>    <int>     <dbl>     <int>
#> 1 15       0.655    1.02        12         0         0
#> 2 15       0.634    0.984       10         0         0
#> 3 15       0.642    0.946        9         0         0
#> 4 15       0.644    0.921        8         0         0

Next we use fiphde’s glm_wrap function which attempts to find the best-fitting model out of all the models supplied, and specifies the prediction interval quantiles used in FluSight.

res <- glm_wrap(dat_hi,
                new_covariates = new_cov,
                .models = models,
                alpha = c(0.01, 0.025, seq(0.05, 0.5, by = 0.05)) * 2)
res$forecasts$location <- "15"

We can take a quick look at the forecast output as well as different aspects of the final fitted model.

head(res$forecasts)
#>   epiweek epiyear quantile value location
#> 1      22    2022    0.010     0       15
#> 2      22    2022    0.025     0       15
#> 3      22    2022    0.050     0       15
#> 4      22    2022    0.100     0       15
#> 5      22    2022    0.150     1       15
#> 6      22    2022    0.200     1       15
res$model
#> # A tibble: 2 × 4
#>   model_class fit        location data$fit_data     
#>   <chr>       <trndng__> <chr>    <list>            
#> 1 glm         <glm>      15       <tibble [84 × 15]>
#> 2 glm         <fn>       15       <tibble [84 × 15]>
res$model$fit
#> $fitted_model
#> 
#> Call:  glm(formula = flu.admits ~ ili + hosp_rank + ili_rank, family = "poisson", 
#>     data = data)
#> 
#> Coefficients:
#> (Intercept)          ili    hosp_rank     ili_rank  
#>   -0.110528     1.463418    -0.051589     0.003076  
#> 
#> Degrees of Freedom: 83 Total (i.e. Null);  80 Residual
#> Null Deviance:       229.6 
#> Residual Deviance: 175.6     AIC: 234
#> 
#> $predict
#> function (newdata, alpha = 0.05, add_pi = TRUE, simulate_pi = TRUE, 
#>     uncertain = TRUE) 
#> {
#>     if (missing(newdata)) {
#>         newdata <- data[all.vars(formula(model))]
#>     }
#>     result <- add_confidence_interval(model, newdata, alpha)
#>     if (add_pi) {
#>         if (simulate_pi) {
#>             result <- add_prediction_interval(model, result, 
#>                 alpha, simulate_pi, uncertain)
#>         }
#>         else {
#>             result <- add_prediction_interval(model, result, 
#>                 alpha, simulate_pi, uncertain)
#>         }
#>     }
#>     result
#> }
#> <bytecode: 0x55b093ac6130>
#> <environment: 0x55b089669cb8>
#> 
#> attr(,"class")
#> [1] "trending_model_fit" "list"
res$model$fit$fitted_model$family
#> 
#> Family: poisson 
#> Link function: log
res$model$fit$fitted_model$coefficients
#>  (Intercept)          ili    hosp_rank     ili_rank 
#> -0.110527528  1.463417923 -0.051589427  0.003075992

Next we prepare the data in the quantile format used by FluSight.

hi_glm_prepped <- format_for_submission(res$forecasts, method="CREG", format = "legacy")
hi_glm_prepped
#> $CREG
#> # A tibble: 96 × 7
#>    forecast_date target            target_end_date location type  quantile value
#>    <date>        <chr>             <date>          <chr>    <chr> <chr>    <chr>
#>  1 2024-08-01    1 wk ahead inc f… 2022-06-04      15       quan… 0.010    0    
#>  2 2024-08-01    1 wk ahead inc f… 2022-06-04      15       quan… 0.025    0    
#>  3 2024-08-01    1 wk ahead inc f… 2022-06-04      15       quan… 0.050    0    
#>  4 2024-08-01    1 wk ahead inc f… 2022-06-04      15       quan… 0.100    0    
#>  5 2024-08-01    1 wk ahead inc f… 2022-06-04      15       quan… 0.150    1    
#>  6 2024-08-01    1 wk ahead inc f… 2022-06-04      15       quan… 0.200    1    
#>  7 2024-08-01    1 wk ahead inc f… 2022-06-04      15       quan… 0.250    1    
#>  8 2024-08-01    1 wk ahead inc f… 2022-06-04      15       quan… 0.300    1    
#>  9 2024-08-01    1 wk ahead inc f… 2022-06-04      15       quan… 0.350    2    
#> 10 2024-08-01    1 wk ahead inc f… 2022-06-04      15       quan… 0.400    2    
#> # ℹ 86 more rows

Finally, we can visualize the forecasted point estimates with the 90% prediction interval alongside the observed data.

plot_forecast(dat_hi, hi_glm_prepped$CREG, location="15", pi=.9)