Introduction

trending aims to provides a coherent interface to several modelling tools. Whilst it is useful in an interactive context, it’s main focus is to provide an intuitive interface on which other packages can be developed (e.g. trendbreaker).

Main features

*   Requires brms

Example usage

An individual model

library(outbreaks)  # for data
library(trending)   # for trend fitting
library(dplyr, warn.conflicts = FALSE)  # for data manipulation

# load data
data(covid19_england_nhscalls_2020)

# define a model
model  <- glm_nb_model(count ~ day + weekday)

# select 6 weeks of data (from a period when the prevalence was decreasing)
last_date <- as.Date("2020-05-28")
first_date <- last_date - 8*7
pathways_recent <-
  covid19_england_nhscalls_2020 %>%
  filter(date >= first_date, date <= last_date) %>%
  group_by(date, day, weekday) %>%
  summarise(count = sum(count), .groups = "drop")

# split data for fitting and prediction
dat <-
  pathways_recent %>%
  group_by(date <= first_date + 6*7) %>%
  group_split()

fitting_data <- dat[[2]]
pred_data <- select(dat[[1]], date, day, weekday)

fitted_model <- fit(model, fitting_data)

# default
fitted_model %>% 
  predict(pred_data) %>%
  glimpse()
#> Rows: 14
#> Columns: 8
#> $ date     <date> 2020-05-15, 2020-05-16, 2020-05-17, 2020-05-18, 2020-05-19,…
#> $ day      <int> 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71
#> $ weekday  <fct> rest_of_week, weekend, weekend, monday, rest_of_week, rest_o…
#> $ estimate <dbl> 12682.379, 10624.988, 10261.987, 13839.821, 11036.028, 10658…
#> $ lower_ci <dbl> 11389.734, 9298.983, 8955.560, 11749.030, 9782.389, 9416.365…
#> $ upper_ci <dbl> 14121.729, 12140.078, 11758.995, 16302.677, 12450.323, 12065…
#> $ lower_pi <dbl> 8107, 6618, 6373, 8363, 6962, 6701, 6450, 6208, 5079, 4889, …
#> $ upper_pi <dbl> 18870, 16223, 15714, 21784, 16638, 16124, 15626, 15145, 1299…

# without prediction intervals
fitted_model %>% 
  predict(pred_data, add_pi = FALSE) %>% 
  glimpse()
#> Rows: 14
#> Columns: 6
#> $ date     <date> 2020-05-15, 2020-05-16, 2020-05-17, 2020-05-18, 2020-05-19,…
#> $ day      <int> 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71
#> $ weekday  <fct> rest_of_week, weekend, weekend, monday, rest_of_week, rest_o…
#> $ estimate <dbl> 12682.379, 10624.988, 10261.987, 13839.821, 11036.028, 10658…
#> $ lower_ci <dbl> 11389.734, 9298.983, 8955.560, 11749.030, 9782.389, 9416.365…
#> $ upper_ci <dbl> 14121.729, 12140.078, 11758.995, 16302.677, 12450.323, 12065…

# without uncertainty
fitted_model %>% 
  predict(pred_data, uncertainty = FALSE) %>% 
  glimpse()
#> Rows: 14
#> Columns: 8
#> $ date     <date> 2020-05-15, 2020-05-16, 2020-05-17, 2020-05-18, 2020-05-19,…
#> $ day      <int> 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71
#> $ weekday  <fct> rest_of_week, weekend, weekend, monday, rest_of_week, rest_o…
#> $ estimate <dbl> 12682.379, 10624.988, 10261.987, 13839.821, 11036.028, 10658…
#> $ lower_ci <dbl> 11389.734, 9298.983, 8955.560, 11749.030, 9782.389, 9416.365…
#> $ upper_ci <dbl> 14121.729, 12140.078, 11758.995, 16302.677, 12450.323, 12065…
#> $ lower_pi <dbl> 8107, 6618, 6373, 8363, 6962, 6701, 6450, 6208, 5079, 4889, …
#> $ upper_pi <dbl> 18870, 16223, 15714, 21784, 16638, 16124, 15626, 15145, 1299…

Multiple models

Trending makes use of [purrr::safely()] to handle lists of models. In this situation the fitted output wil be of class [trending_model_fit_list] which is a list with components result and error. If an error occurred, error is an error object and result has a NULL value (otherwise). If the model does not error then result is the corresponding model output and the error entry will be NULL.

models  <- list(
  simple = lm_model(count ~ day),
  glm_poisson = glm_model(count ~ day, family = "poisson"),
  glm_negbin = glm_nb_model(count ~ day + weekday),
  will_error = glm_nb_model(count ~ day + nonexistant)
)

models %>%
  fit(fitting_data) %>%
  glimpse()
#> List of 3
#>  $ fitted_trending_model:List of 4
#>   ..$ simple     :List of 2
#>   .. ..$ fitted_model:List of 12
#>   .. .. ..- attr(*, "class")= chr "lm"
#>   .. ..$ predict     :function (newdata, alpha = 0.05, add_pi = TRUE, uncertain = TRUE)  
#>   .. ..- attr(*, "class")= chr [1:2] "trending_model_fit" "list"
#>   ..$ glm_poisson:List of 2
#>   .. ..$ fitted_model:List of 30
#>   .. .. ..- attr(*, "class")= chr [1:2] "glm" "lm"
#>   .. ..$ predict     :function (newdata, alpha = 0.05, add_pi = TRUE, uncertain = TRUE)  
#>   .. ..- attr(*, "class")= chr [1:2] "trending_model_fit" "list"
#>   ..$ glm_negbin :List of 2
#>   .. ..$ fitted_model:List of 30
#>   .. .. ..- attr(*, "class")= chr [1:3] "negbin" "glm" "lm"
#>   .. ..$ predict     :function (newdata, alpha = 0.05, add_pi = TRUE, uncertain = TRUE)  
#>   .. ..- attr(*, "class")= chr [1:2] "trending_model_fit" "list"
#>   ..$ will_error : NULL
#>  $ fitting_warning      :List of 4
#>   ..$ simple     : NULL
#>   ..$ glm_poisson: NULL
#>   ..$ glm_negbin : NULL
#>   ..$ will_error : NULL
#>  $ fitting_error        :List of 4
#>   ..$ simple     : NULL
#>   ..$ glm_poisson: NULL
#>   ..$ glm_negbin : NULL
#>   ..$ will_error : chr "object 'nonexistant' not found"
#>  - attr(*, "class")= chr [1:2] "trending_model_fit_list" "list"

Similarly [trending_model_fit_list] objects can then be used with [predict]

models %>%
  fit(fitting_data) %>% 
  predict(pred_data) %>% 
  glimpse()
#> List of 3
#>  $ output            :List of 4
#>   ..$ simple     :'data.frame':  14 obs. of  8 variables:
#>   .. ..$ date    : Date[1:14], format: "2020-05-15" "2020-05-16" ...
#>   .. ..$ day     : int [1:14] 58 59 60 61 62 63 64 65 66 67 ...
#>   .. ..$ weekday : Factor w/ 3 levels "rest_of_week",..: 1 3 3 2 1 1 1 1 3 3 ...
#>   .. ..$ estimate: num [1:14] 5813 4720 3627 2534 1441 ...
#>   .. ..$ lower_ci: num [1:14] 234 -1053 -2341 -3631 -4922 ...
#>   .. ..$ upper_ci: num [1:14] 11392 10492 9594 8699 7804 ...
#>   .. ..$ lower_pi: num [1:14] -13007 -14158 -15311 -16467 -17626 ...
#>   .. ..$ upper_pi: num [1:14] 24632 23597 22565 21535 20508 ...
#>   ..$ glm_poisson:'data.frame':  14 obs. of  8 variables:
#>   .. ..$ date    : Date[1:14], format: "2020-05-15" "2020-05-16" ...
#>   .. ..$ day     : int [1:14] 58 59 60 61 62 63 64 65 66 67 ...
#>   .. ..$ weekday : Factor w/ 3 levels "rest_of_week",..: 1 3 3 2 1 1 1 1 3 3 ...
#>   .. ..$ estimate: num [1:14] 11535 11103 10686 10286 9900 ...
#>   .. ..$ lower_ci: num [1:14] 11484 11052 10636 10236 9850 ...
#>   .. ..$ upper_ci: num [1:14] 11587 11154 10737 10336 9950 ...
#>   .. ..$ lower_pi: num [1:14] 11275 10846 10434 10038 9656 ...
#>   .. ..$ upper_pi: num [1:14] 11798 11361 10941 10536 10146 ...
#>   ..$ glm_negbin :'data.frame':  14 obs. of  8 variables:
#>   .. ..$ date    : Date[1:14], format: "2020-05-15" "2020-05-16" ...
#>   .. ..$ day     : int [1:14] 58 59 60 61 62 63 64 65 66 67 ...
#>   .. ..$ weekday : Factor w/ 3 levels "rest_of_week",..: 1 3 3 2 1 1 1 1 3 3 ...
#>   .. ..$ estimate: num [1:14] 12682 10625 10262 13840 11036 ...
#>   .. ..$ lower_ci: num [1:14] 11390 9299 8956 11749 9782 ...
#>   .. ..$ upper_ci: num [1:14] 14122 12140 11759 16303 12450 ...
#>   .. ..$ lower_pi: num [1:14] 8107 6618 6373 8363 6962 ...
#>   .. ..$ upper_pi: num [1:14] 18870 16223 15714 21784 16638 ...
#>   ..$ will_error : NULL
#>  $ prediction_warning:List of 4
#>   ..$ simple     : NULL
#>   ..$ glm_poisson: NULL
#>   ..$ glm_negbin : NULL
#>   ..$ will_error : NULL
#>  $ prediction_error  :List of 4
#>   ..$ simple     : NULL
#>   ..$ glm_poisson: NULL
#>   ..$ glm_negbin : NULL
#>   ..$ will_error : chr "no applicable method for 'predict' applied to an object of class \"NULL\""