| Title: | Infer temporal trends of multiple pathogens |
|---|---|
| Description: | 'EpiStrainDynamics' is a statistical framework developed for inferring temporal trends of multiple pathogens from routinely collected surveillance data. |
| Authors: | Saras Windecker [aut, cre] (ORCID: <https://orcid.org/0000-0002-4870-8353>), Oliver Eales [aut] (ORCID: <https://orcid.org/0000-0002-8086-4495>), James McCaw [aut] (ORCID: <https://orcid.org/0000-0002-2452-3098>), Freya Shearer [aut] (ORCID: <https://orcid.org/0000-0001-9600-3473>), Milad Kharratzadeh [ctb], Sangeeta Bhatia [rev], Joshua Lambert [rev] |
| Maintainer: | Saras Windecker <[email protected]> |
| License: | Apache License 2.0 |
| Version: | 0.1.0 |
| Built: | 2026-08-15 19:14:40 UTC |
| Source: | https://github.com/ropensci/EpiStrainDynamics |
Construct model
construct_model( pathogen_structure, method, smoothing_params = smoothing_structure(), dispersion_params = dispersion_structure(), pathogen_noise = FALSE, dow_effect = FALSE )construct_model( pathogen_structure, method, smoothing_params = smoothing_structure(), dispersion_params = dispersion_structure(), pathogen_noise = FALSE, dow_effect = FALSE )
pathogen_structure |
either |
method |
either |
smoothing_params |
argument is optional and defines the structure of the
smoothing terms including optionally setting the smoothing prior tau.
Created with |
dispersion_params |
argument is optional and defines priors for the
overdispersion parameter of the negative binomial likelihood for
the case timeseries. Created using |
pathogen_noise |
logical whether individual pathogen counts have
additional gamma-distributed noise. Default is FALSE. Models with |
dow_effect |
logical whether to incorporate a day of week model. |
a list containing the data, the model parameters, and pathogen
names of class EpiStrainDynamics.model
mod <- construct_model( pathogen_structure = multiple( data = sarscov2, case_timeseries = "cases", time = "date", component_pathogen_timeseries = c("alpha", "delta", "omicron", "other") ), method = p_spline(), smoothing_params = smoothing_structure( "independent", tau_mean = c(0, 0.1, 0.3, 0), tau_sd = rep(1, times = 4) ), dispersion_params = dispersion_structure(phi_mean = 0, phi_sd = 1), pathogen_noise = FALSE, dow_effect = TRUE )mod <- construct_model( pathogen_structure = multiple( data = sarscov2, case_timeseries = "cases", time = "date", component_pathogen_timeseries = c("alpha", "delta", "omicron", "other") ), method = p_spline(), smoothing_params = smoothing_structure( "independent", tau_mean = c(0, 0.1, 0.3, 0), tau_sd = rep(1, times = 4) ), dispersion_params = dispersion_structure(phi_mean = 0, phi_sd = 1), pathogen_noise = FALSE, dow_effect = TRUE )
Checks MCMC convergence diagnostics — R-hat and effective sample size — against user-specified thresholds, and reports any parameters that fail either check.
diagnose_model(fitted_model, rhat_threshold = 1.1, eff_sample_threshold = 100)diagnose_model(fitted_model, rhat_threshold = 1.1, eff_sample_threshold = 100)
fitted_model |
A fitted model object of class |
rhat_threshold |
R-hat threshold for convergence (default 1.1). Values above this threshold indicate chains have not mixed well. |
eff_sample_threshold |
Effective sample size threshold (default 100). Values below this threshold indicate the posterior samples for a parameter are too autocorrelated to reliably estimate its distribution. |
An object of class list, returned invisibly, containing:
convergence |
Logical; |
rhat_issues |
Character vector of parameter names with R-hat above
|
eff_sample_issues |
Character vector of parameter names with
effective sample size below |
max_rhat |
The largest R-hat value across all parameters |
min_neff |
The smallest effective sample size across all parameters |
summary |
The full |
mod <- construct_model( pathogen_structure = single( case_timeseries = sarscov2$cases, time = sarscov2$date ), method = random_walk() ) fit <- fit_model(mod) diagnose_model(fit)mod <- construct_model( pathogen_structure = single( case_timeseries = sarscov2$cases, time = sarscov2$date ), method = random_walk() ) fit <- fit_model(mod) diagnose_model(fit)
This function creates a dispersion structure object that specifies priors
for the overdispersion parameter of the negative binomial likelihood for
the case timeseries. phi controls how much the observed counts vary
around the expected trend beyond Poisson noise: smaller values allow more
overdispersion (noisier counts relative to the mean), larger values
approach Poisson-like variance.
dispersion_structure(phi_mean = NULL, phi_sd = NULL)dispersion_structure(phi_mean = NULL, phi_sd = NULL)
phi_mean |
Numeric scalar specifying the prior mean for the negative binomial dispersion parameter. Must be positive. Optional - if not provided, no priors will be set. |
phi_sd |
Numeric scalar specifying the prior standard deviation for the dispersion parameter. Must be positive. Optional - if not provided, no priors will be set. |
An object of class EpiStrainDynamics.dispersion containing:
mean |
The prior mean for phi |
sd |
The prior standard deviation for phi |
priors_provided |
Integer flag passed to the Stan model: |
# Create dispersion structure disp_struct <- dispersion_structure(phi_mean = 2.0, phi_sd = 0.5)# Create dispersion structure disp_struct <- dispersion_structure(phi_mean = 2.0, phi_sd = 0.5)
S3 generic for fitted models from constructed model object
fit_model( constructed_model, n_chain = 4, n_iter = 2000, n_warmup = floor(n_iter/2), thin = 1, adapt_delta = 0.9, multi_cores = TRUE, verbose = TRUE, suppress_warnings = FALSE, seed = NULL, ... ) ## S3 method for class 'rw_subtyped' fit_model( constructed_model, n_chain = 4, n_iter = 2000, n_warmup = floor(n_iter/2), thin = 1, adapt_delta = 0.9, multi_cores = TRUE, verbose = TRUE, suppress_warnings = FALSE, seed = NULL, ... ) ## S3 method for class 'ps_subtyped' fit_model( constructed_model, n_chain = 4, n_iter = 2000, n_warmup = floor(n_iter/2), thin = 1, adapt_delta = 0.9, multi_cores = TRUE, verbose = TRUE, suppress_warnings = FALSE, seed = NULL, ... ) ## S3 method for class 'rw_multiple' fit_model( constructed_model, n_chain = 4, n_iter = 2000, n_warmup = floor(n_iter/2), thin = 1, adapt_delta = 0.9, multi_cores = TRUE, verbose = TRUE, suppress_warnings = FALSE, seed = NULL, ... ) ## S3 method for class 'ps_multiple' fit_model( constructed_model, n_chain = 4, n_iter = 2000, n_warmup = floor(n_iter/2), thin = 1, adapt_delta = 0.9, multi_cores = TRUE, verbose = TRUE, suppress_warnings = FALSE, seed = NULL, ... ) ## S3 method for class 'rw_single' fit_model( constructed_model, n_chain = 4, n_iter = 2000, n_warmup = floor(n_iter/2), thin = 1, adapt_delta = 0.9, multi_cores = TRUE, verbose = TRUE, suppress_warnings = FALSE, seed = NULL, ... ) ## S3 method for class 'ps_single' fit_model( constructed_model, n_chain = 4, n_iter = 2000, n_warmup = floor(n_iter/2), thin = 1, adapt_delta = 0.9, multi_cores = TRUE, verbose = TRUE, suppress_warnings = FALSE, seed = NULL, ... )fit_model( constructed_model, n_chain = 4, n_iter = 2000, n_warmup = floor(n_iter/2), thin = 1, adapt_delta = 0.9, multi_cores = TRUE, verbose = TRUE, suppress_warnings = FALSE, seed = NULL, ... ) ## S3 method for class 'rw_subtyped' fit_model( constructed_model, n_chain = 4, n_iter = 2000, n_warmup = floor(n_iter/2), thin = 1, adapt_delta = 0.9, multi_cores = TRUE, verbose = TRUE, suppress_warnings = FALSE, seed = NULL, ... ) ## S3 method for class 'ps_subtyped' fit_model( constructed_model, n_chain = 4, n_iter = 2000, n_warmup = floor(n_iter/2), thin = 1, adapt_delta = 0.9, multi_cores = TRUE, verbose = TRUE, suppress_warnings = FALSE, seed = NULL, ... ) ## S3 method for class 'rw_multiple' fit_model( constructed_model, n_chain = 4, n_iter = 2000, n_warmup = floor(n_iter/2), thin = 1, adapt_delta = 0.9, multi_cores = TRUE, verbose = TRUE, suppress_warnings = FALSE, seed = NULL, ... ) ## S3 method for class 'ps_multiple' fit_model( constructed_model, n_chain = 4, n_iter = 2000, n_warmup = floor(n_iter/2), thin = 1, adapt_delta = 0.9, multi_cores = TRUE, verbose = TRUE, suppress_warnings = FALSE, seed = NULL, ... ) ## S3 method for class 'rw_single' fit_model( constructed_model, n_chain = 4, n_iter = 2000, n_warmup = floor(n_iter/2), thin = 1, adapt_delta = 0.9, multi_cores = TRUE, verbose = TRUE, suppress_warnings = FALSE, seed = NULL, ... ) ## S3 method for class 'ps_single' fit_model( constructed_model, n_chain = 4, n_iter = 2000, n_warmup = floor(n_iter/2), thin = 1, adapt_delta = 0.9, multi_cores = TRUE, verbose = TRUE, suppress_warnings = FALSE, seed = NULL, ... )
constructed_model |
prepared model object of class
|
n_chain |
number of MCMC chains, defaults to 4 |
n_iter |
A positive integer specifying the number of iterations for each chain, default value is 2000 |
n_warmup |
A positive integer specifying the number of warmup iterations,default value is half the number of iterations |
thin |
A positive integer specifying the period for saving samples, default value is 1. |
adapt_delta |
Numeric value between 0 and 1 indicating target average
acceptance probability used in |
multi_cores |
A logical value indicating whether to parallelize chains with multiple cores, default is TRUE and uses all available cores - 1. |
verbose |
Logical value controlling the verbosity of output. When TRUE (default), shows all messages, warnings, errors, and progress indicators. When FALSE, suppresses messages and progress while retaining warnings and errors. |
suppress_warnings |
Logical value indicating whether to suppress warnings from Stan. Default is FALSE. When TRUE, warnings are suppressed but errors are still raised. |
seed |
A positive integer seed used for random number generation in MCMC. Default is NULL, which means the seed is generated from 1 to the maximum integer supported by R. |
... |
additional arguments to |
fit model of class EpiStrainDynamics.fit, or if fitting fails,
an error is raised that can be caught and inspected.
mod <- construct_model( pathogen_structure = single( case_timeseries = sarscov2$cases, time = sarscov2$date ), method = random_walk() ) fit <- fit_model(mod) # Suppress progress and messages but keep warnings/errors fit <- fit_model(mod, verbose = FALSE) # Suppress warnings too fit <- fit_model(mod, verbose = FALSE, suppress_warnings = TRUE) # Catch errors and inspect result <- tryCatch( fit_model(mod), error = function(e) e ) if (inherits(result, "EpiStrainDynamics.fit.error")) { cat("Fitting failed:", result$message, "\n") # Can still access the model: result$constructed_model }mod <- construct_model( pathogen_structure = single( case_timeseries = sarscov2$cases, time = sarscov2$date ), method = random_walk() ) fit <- fit_model(mod) # Suppress progress and messages but keep warnings/errors fit <- fit_model(mod, verbose = FALSE) # Suppress warnings too fit <- fit_model(mod, verbose = FALSE, suppress_warnings = TRUE) # Catch errors and inspect result <- tryCatch( fit_model(mod), error = function(e) e ) if (inherits(result, "EpiStrainDynamics.fit.error")) { cat("Fitting failed:", result$message, "\n") # Can still access the model: result$constructed_model }
Computes the epidemiological growth rate, defined as the instantaneous rate of change in log-incidence over time. Mathematically, it represents:
where is incidence (see incidence()) at time .
growth_rate(fitted_model, ...) ## S3 method for class 'ps' growth_rate(fitted_model, ...) ## S3 method for class 'rw' growth_rate(fitted_model, ...) ## S3 method for class 'ps_single' growth_rate(fitted_model, ...) ## S3 method for class 'rw_single' growth_rate(fitted_model, ...)growth_rate(fitted_model, ...) ## S3 method for class 'ps' growth_rate(fitted_model, ...) ## S3 method for class 'rw' growth_rate(fitted_model, ...) ## S3 method for class 'ps_single' growth_rate(fitted_model, ...) ## S3 method for class 'rw_single' growth_rate(fitted_model, ...)
fitted_model |
Fitted model object with class |
... |
Additional arguments passed to metrics calculation |
Growth rate and both describe transmission trends, but on
different scales: growth rate is a direct log-scale rate of change, while
Rt() additionally accounts for the generation interval to translate
that rate into an average number of secondary infections per case.
Growth rate is positive whenever and negative whenever
, since both describe the same underlying growth or decline.
This metric quantifies the proportional change in disease incidence from one time period to the next on a logarithmic scale, where:
Positive values () indicate exponential growth
Negative values () indicate exponential decline
Values near zero () indicate stable incidence
The magnitude indicates the rate of exponential change
For example:
A growth rate of 0.1 means incidence increased by approximately 10.5% ()
A growth rate of -0.05 means incidence decreased by approximately 4.9%
A growth rate of 0 means no change in incidence
This metric function can be run directly on the fitted model output.
named list of class EpiStrainDynamics.metric containing a dataframe
of the calculated metric outcome ($measure), the fit object ($fit), and the
constructed model object ($constructed_model). The measure data frame
contains the median of the epidemiological quantity (y), the 50% credible
interval of the quantity (lb_50 & ub_50), the 95% credible interval
(lb_95 & ub_95), the proportion greater than a defined threshold value
(prop), the pathogen name (pathogen), and the time label (time).
Other metrics:
Rt(),
incidence(),
proportion()
mod <- construct_model( pathogen_structure = single( case_timeseries = sarscov2$cases, time = sarscov2$date ), method = random_walk() ) fit <- fit_model(mod) gr <- growth_rate(fit)mod <- construct_model( pathogen_structure = single( case_timeseries = sarscov2$cases, time = sarscov2$date ), method = random_walk() ) fit <- fit_model(mod) gr <- growth_rate(fit)
Computes epidemiological incidence, defined as the number of new cases occurring at a specific time point, derived by exponentiating the log-incidence estimates from the fitted model:
incidence(fitted_model, dow = NULL, ...) ## S3 method for class 'ps' incidence(fitted_model, dow = NULL, ...) ## S3 method for class 'rw' incidence(fitted_model, dow = NULL, ...) ## S3 method for class 'ps_single' incidence(fitted_model, dow = NULL, ...) ## S3 method for class 'rw_single' incidence(fitted_model, dow = NULL, ...)incidence(fitted_model, dow = NULL, ...) ## S3 method for class 'ps' incidence(fitted_model, dow = NULL, ...) ## S3 method for class 'rw' incidence(fitted_model, dow = NULL, ...) ## S3 method for class 'ps_single' incidence(fitted_model, dow = NULL, ...) ## S3 method for class 'rw_single' incidence(fitted_model, dow = NULL, ...)
fitted_model |
Fitted model object with class |
dow |
Logical indicating whether to include day-of-week effects.
If |
... |
Additional arguments passed to metrics calculation |
This metric quantifies the absolute number of new cases at each time point, where it is:
Always positive (since it's an exponentiated value)
Represents the expected case count at time t
Can be adjusted for day-of-week effects when modeled
Provides uncertainty quantification through posterior credible intervals
Day-of-week adjustment: When day-of-week effects are included in the model, the incidence is further adjusted as:
Where:
is the number of distinct days modelled
(7 for a full weekly cycle)
gives the relative reporting weight for
each day of the week, estimated from the data
maps time to its day of the week
This accounts for systematic variations in case reporting (e.g. lower weekend reporting) that are not part of the underlying transmission trend.
This metric function can be run directly on the fitted model output.
named list of class EpiStrainDynamics.metric containing a dataframe
of the calculated metric outcome ($measure), the fit object ($fit), and the
constructed model object ($constructed_model). The measure data frame
contains the median of the epidemiological quantity (y), the 50% credible
interval of the quantity (lb_50 & ub_50), the 95% credible interval
(lb_95 & ub_95), the proportion greater than a defined threshold value
(prop), the pathogen name (pathogen), and the time label (time).
Other metrics:
Rt(),
growth_rate(),
proportion()
mod <- construct_model( pathogen_structure = single( case_timeseries = sarscov2$cases, time = sarscov2$date ), method = random_walk() ) fit <- fit_model(mod) # Use model's dow setting (default) inc <- incidence(fit) # Explicitly exclude dow effects inc_no_dow <- incidence(fit, dow = FALSE) # Explicitly include dow effects (if model has them) inc_with_dow <- incidence(fit, dow = TRUE)mod <- construct_model( pathogen_structure = single( case_timeseries = sarscov2$cases, time = sarscov2$date ), method = random_walk() ) fit <- fit_model(mod) # Use model's dow setting (default) inc <- incidence(fit) # Explicitly exclude dow effects inc_no_dow <- incidence(fit, dow = FALSE) # Explicitly include dow effects (if model has them) inc_with_dow <- incidence(fit, dow = TRUE)
Influenza-like illness data were retrieved from the World Health Organization’s Global Influenza Programme. We collated weekly data for Australia from the week starting January 2, 2012 to the week starting December 25, 2023 inclusive. The data described: (1) the weekly number of cases of influenza-like illness; and (2) the weekly number of specimens positive for influenza by subtype. We grouped the influenza specimens into: influenza A subtype not determined; influenza A H3N2; influenza A H1N1 (influenza A H1N1, influenza A H1N1pdm09); influenza B (influenza B Yamagata, influenza B Victoria, influenza B lineage not determined); and other (unknown causes which we do not have data to determine).
influenzainfluenza
influenzaA data frame with 426 rows and 7 columns:
Integer, daily total number of cases of influenza-like illness
Date, between 1 January 2012 to week starting 1 March 2020
Integer, daily number of cases of unsubtyped influenza A
Integer, daily number of cases of influenza B
Integer, daily number of cases of influenza A subtype H3N2
Integer, daily number of cases of influenza A subtype H1N1
Integer, number of cases of unspecified influenza-like illness
Multiple pathogen structure
multiple(data, case_timeseries, component_pathogen_timeseries, time = NULL)multiple(data, case_timeseries, component_pathogen_timeseries, time = NULL)
data |
dataframe containing columns with all relevant data, or a time series object (ts, xts, zoo, tsibble, etc.) |
case_timeseries |
Column name containing case counts. Must be numeric
or a |
component_pathogen_timeseries |
vector of column names with additional
pathogen case count timeseries to model. Must be numeric or a |
time |
Column name with time data. Required for non-time-series
input data. Flexible format - can be date, index, or others, accepted
as |
named list including pathogen_structure, pathogen_names, and data
of class EpiStrainDynamics.pathogen_structure
Other pathogen_structure:
single(),
subtyped()
# Using a data frame multiple( data = sarscov2, case_timeseries = "cases", component_pathogen_timeseries = c("alpha", "delta", "omicron", "other"), time = "date" ) # Using a time series object (time argument is optional) sarscov2_xts <- xts::xts( sarscov2[, c("cases", "alpha", "delta", "omicron", "other")], order.by = sarscov2$date ) multiple( data = sarscov2_xts, case_timeseries = "cases", component_pathogen_timeseries = c("alpha", "delta", "omicron", "other") )# Using a data frame multiple( data = sarscov2, case_timeseries = "cases", component_pathogen_timeseries = c("alpha", "delta", "omicron", "other"), time = "date" ) # Using a time series object (time argument is optional) sarscov2_xts <- xts::xts( sarscov2[, c("cases", "alpha", "delta", "omicron", "other")], order.by = sarscov2$date ) multiple( data = sarscov2_xts, case_timeseries = "cases", component_pathogen_timeseries = c("alpha", "delta", "omicron", "other") )
Penalised splines is one of two optional Bayesian smoothing prior methods
that can be selected and used in the model definition with
EpiStrainDynamics. The main benefit of selecting the penalised spline
method over random walks is that it can capture dynamical effects (fine
enough temporal resolution) while not being too computationally expensive.
p_spline(spline_degree = 3, days_per_knot = 3)p_spline(spline_degree = 3, days_per_knot = 3)
spline_degree |
polynomial degree of the individual spline segments used to construct the overall curve (must be a positive whole number) |
days_per_knot |
number of days for each knot (must be a positive whole number) |
list with method and model parameters of class
EpiStrainDynamics.method
Other method:
random_walk()
# Valid usage p_spline(spline_degree = 2L, days_per_knot = 5L) p_spline(spline_degree = 3, days_per_knot = 7) # These will produce validation errors (as intended): # Non-positive values try(p_spline(spline_degree = 0, days_per_knot = 5)) try(p_spline(spline_degree = 3, days_per_knot = -1)) # Non-whole numbers try(p_spline(spline_degree = 2.5, days_per_knot = 5)) try(p_spline(spline_degree = 3, days_per_knot = 4.2)) # Non-numeric values try(p_spline(spline_degree = "invalid", days_per_knot = 5))# Valid usage p_spline(spline_degree = 2L, days_per_knot = 5L) p_spline(spline_degree = 3, days_per_knot = 7) # These will produce validation errors (as intended): # Non-positive values try(p_spline(spline_degree = 0, days_per_knot = 5)) try(p_spline(spline_degree = 3, days_per_knot = -1)) # Non-whole numbers try(p_spline(spline_degree = 2.5, days_per_knot = 5)) try(p_spline(spline_degree = 3, days_per_knot = 4.2)) # Non-numeric values try(p_spline(spline_degree = "invalid", days_per_knot = 5))
S3 methods for plotting metrics calculated from the output sof either
incidence(), growth_rate(), Rt(), or proportion().
## S3 method for class 'incidence' plot(x, xlab = "Time", ...) ## S3 method for class 'growth_rate' plot(x, xlab = "Time", ...) ## S3 method for class 'Rt' plot(x, xlab = "Time", ...) ## S3 method for class 'proportion' plot(x, xlab = "Time", ...)## S3 method for class 'incidence' plot(x, xlab = "Time", ...) ## S3 method for class 'growth_rate' plot(x, xlab = "Time", ...) ## S3 method for class 'Rt' plot(x, xlab = "Time", ...) ## S3 method for class 'proportion' plot(x, xlab = "Time", ...)
x |
Metrics calculation output of class |
xlab |
Time label for x axis, defaults to "Time" |
... |
Additional arguments passed to plot |
ggplot2 plot output
mod <- construct_model( pathogen_structure = single( case_timeseries = sarscov2$cases, time = sarscov2$date ), method = random_walk() ) fit <- fit_model(mod) gr <- growth_rate(mod) plot(gr)mod <- construct_model( pathogen_structure = single( case_timeseries = sarscov2$cases, time = sarscov2$date ), method = random_walk() ) fit <- fit_model(mod) gr <- growth_rate(mod) plot(gr)
Prints a concise summary instead of dumping the full data and standata list to the console.
## S3 method for class 'EpiStrainDynamics.model' print(x, ...)## S3 method for class 'EpiStrainDynamics.model' print(x, ...)
x |
an |
... |
further arguments passed to or from other methods (unused) |
x, invisibly
mod <- construct_model( pathogen_structure = single( data = sarscov2, case_timeseries = "cases", time = "date" ), method = random_walk() ) print(mod)mod <- construct_model( pathogen_structure = single( data = sarscov2, case_timeseries = "cases", time = "date" ), method = random_walk() ) print(mod)
Computes epidemiological proportion, defined as the relative fraction of cases attributable to specific pathogen(s) or strain(s) at a given time point, calculated as the ratio of incidence from selected pathogen(s) to a reference group:
proportion( fitted_model, numerator_combination = NULL, denominator_combination = NULL, ... )proportion( fitted_model, numerator_combination = NULL, denominator_combination = NULL, ... )
fitted_model |
Fitted model object with class |
numerator_combination |
Named pathogens or subtypes to be included in proportion numerator, or NULL. If NULL, it will use each pathogen. |
denominator_combination |
Named pathogens or subtypes to be included in proportion denominator, or NULL. If NULL, it will use all pathogens. |
... |
Additional arguments passed to metrics calculation |
Where incidences are derived from the exponential of log-incidence estimates:
Where the numerator and denominator are each a user-specified set of
pathogens or subtypes (via numerator_combination/denominator_combination),
and , index the pathogens included in each.
This metric quantifies the relative contribution of specific pathogen(s) or strain(s) to the total disease burden. Key characteristics:
Values between 0 and 1 () when denominator
includes numerator components
Values can exceed 1 () when denominator excludes
numerator components
Represents the fractional share of cases at each time point
Time-varying to capture changing pathogen/strain dynamics
Flexible combinations:
Individual proportions: Each pathogen relative to all pathogens (default)
Custom numerator: Specific pathogen(s) of interest (e.g., variant of concern)
Custom denominator: Either 'all' pathogens or a specified subset
Subset comparisons: Compare specific groups (e.g., Alpha vs. Delta + Omicron)
This metric function can be run directly on the fitted model output.
named list of class EpiStrainDynamics.metric containing a dataframe
of the calculated metric outcome ($measure), the fit object ($fit), the
constructed model object ($constructed_model), the resolved pathogen names
used as the numerator ($numerator_combination), and the resolved pathogen
names used as the denominator ($denominator_combination). When
numerator_combination is left as the default (NULL), every pathogen name
is listed in $numerator_combination, but each was computed as its own
separate proportion line (one per pathogen), not summed into a single group
the way $denominator_combination always is. The measure data frame
contains the median of the epidemiological quantity (y), the 50% credible
interval of the quantity (lb_50 & ub_50), the 95% credible interval
(lb_95 & ub_95), the proportion greater than a defined threshold value
(prop), the pathogen name (pathogen), and the time label (time).
Other metrics:
Rt(),
growth_rate(),
incidence()
mod <- construct_model( pathogen_structure = multiple( case_timeseries = sarscov2$cases, time = sarscov2$date, component_pathogen_timeseries = list( alpha = sarscov2$alpha, delta = sarscov2$delta, omicron = sarscov2$omicron, other = sarscov2$other ) ), method = p_spline() ) fit <- fit_model(mod) prop <- proportion(fit) # or a unique combination, compared to all pathogens prop2 <- proportion(fit, numerator_combination = c("alpha", "delta", "omicron") ) # or a user-specified combination in both numerator and denominator prop3 <- proportion(fit, numerator_combination = "alpha", denominator_combination = c("alpha", "delta", "omicron") )mod <- construct_model( pathogen_structure = multiple( case_timeseries = sarscov2$cases, time = sarscov2$date, component_pathogen_timeseries = list( alpha = sarscov2$alpha, delta = sarscov2$delta, omicron = sarscov2$omicron, other = sarscov2$other ) ), method = p_spline() ) fit <- fit_model(mod) prop <- proportion(fit) # or a unique combination, compared to all pathogens prop2 <- proportion(fit, numerator_combination = c("alpha", "delta", "omicron") ) # or a user-specified combination in both numerator and denominator prop3 <- proportion(fit, numerator_combination = "alpha", denominator_combination = c("alpha", "delta", "omicron") )
Random walk is one of two optional Bayesian smoothing prior methods that can
be selected and used in the model definition with EpiStrainDynamics. The
random walk is a stochastic process that describes a path of a series of
random steps on a mathematical space, and each next step's direction only
depends on the current position, not the previous path. No additional
arguments must be supplied to define the random walk method.
random_walk()random_walk()
list with method identified as random walk of class
EpiStrainDynamics.method
Other method:
p_spline()
random_walk()random_walk()
Computes the time-varying reproduction number (Rt), defined as the average number of secondary infections generated by each infected individual at time t, accounting for the generation interval distribution:
Rt(fitted_model, gi_dist, tau_max = 7, ...) ## S3 method for class 'ps' Rt(fitted_model, gi_dist, tau_max = 7, ...) ## S3 method for class 'rw' Rt(fitted_model, gi_dist, tau_max = 7, ...) ## S3 method for class 'ps_single' Rt(fitted_model, gi_dist, tau_max = 7, ...) ## S3 method for class 'rw_single' Rt(fitted_model, gi_dist, tau_max = 7, ...)Rt(fitted_model, gi_dist, tau_max = 7, ...) ## S3 method for class 'ps' Rt(fitted_model, gi_dist, tau_max = 7, ...) ## S3 method for class 'rw' Rt(fitted_model, gi_dist, tau_max = 7, ...) ## S3 method for class 'ps_single' Rt(fitted_model, gi_dist, tau_max = 7, ...) ## S3 method for class 'rw_single' Rt(fitted_model, gi_dist, tau_max = 7, ...)
fitted_model |
Fitted model object with class |
gi_dist |
Function returning the generation interval probability for a given day. Must accept a numeric vector and return a non-negative numeric vector of the same length; does not need to sum to 1 (see Description). |
tau_max |
Integer maximum generation interval in days (default: 7) |
... |
Additional arguments passed to metrics calculation |
Where:
is the current incidence
is the generation interval probability for day k
is the normalization
constant
is the maximum generation interval in days
This metric quantifies current transmission potential by comparing present incidence to the weighted historical incidence that could have generated it, where:
: Epidemic is growing (each case generates >1 secondary
case)
: Epidemic is stable (replacement level transmission)
: Epidemic is declining (each case generates <1
secondary case)
Threshold of 1 is epidemiologically meaningful for control decisions
Generation interval considerations:
Accounts for transmission timing using probability distribution
Recent cases contribute more to current transmission potential
Accepts user-defined generation interval distributions, validated for structural properties (vectorized, non-negative, finite)
Does not need to sum to 1 – normalized internally using
Validation does not assess whether the shape is epidemiologically plausible (e.g. unimodal, decaying); as with other Rt estimation methods, supplying a distribution informed by published estimates for the pathogen(s) being modelled is the user's responsibility
This metric function can be run directly on the fitted model output.
named list of class EpiStrainDynamics.metric containing a dataframe
of the calculated metric outcome ($measure), the fit object ($fit), and the
constructed model object ($constructed_model). The measure data frame
contains the median of the epidemiological quantity (y), the 50% credible
interval of the quantity (lb_50 & ub_50), the 95% credible interval
(lb_95 & ub_95), the proportion greater than a defined threshold value
(prop), the pathogen name (pathogen), and the time label (time).
Other metrics:
growth_rate(),
incidence(),
proportion()
mod <- construct_model( pathogen_structure = single( case_timeseries = sarscov2$cases, time = sarscov2$date ), method = random_walk() ) fit <- fit_model(mod) rt <- Rt(fit, gi_dist = function(x) 4 * x * exp(-2 * x), tau_max = 7)mod <- construct_model( pathogen_structure = single( case_timeseries = sarscov2$cases, time = sarscov2$date ), method = random_walk() ) fit <- fit_model(mod) rt <- Rt(fit, gi_dist = function(x) 4 * x * exp(-2 * x), tau_max = 7)
Daily SARS-CoV-2 case numbers by specimen date for the United Kingdom from 2020 - 2022 retrieved from the UK Health Security Agency's data dashboard. downloaded data describing the daily number of variants detected by collection date. The data classified all sequences based on ‘major lineage calls’. We considered groupings of the variants based on their major lineage calls or other, consisting of all lineages with a designation not consistent with any of the major lineage calls.
sarscov2sarscov2
sarscov2A data frame with 830 rows and 6 columns:
Date, between 23 September 2020 and 31 December 2022
Numeric, daily number of cases
Integer, daily number of cases of B.1.1.7 (Alpha variant)
Integer, daily number of cases of B.1.617.2 (Delta variant)
Integer, daily number of cases of BA.1, BA.2, BA.2.75, BA.4, BA.5, BQ.1 (Omicron variants)
Integer, daily number of cases of B.1.177, XBB (a recombinant of omicron sub-variants), and all other lineages with a designation not consistent with any of the major lineage calls
<https://ukhsa-dashboard.data.gov.uk/covid-19-archive-data-download; https://datadryad.org/dataset/doi:10.5061/dryad.hx3ffbgm2>
Single pathogen structure
single(data, case_timeseries, time = NULL)single(data, case_timeseries, time = NULL)
data |
dataframe containing columns with all relevant data, or a time series object (ts, xts, zoo, tsibble, etc.) |
case_timeseries |
Column name containing case counts. Must be numeric
or a |
time |
name of column with time data. Required for non-time-series
input data. Flexible format - can be date, index, or others, accepted
as |
formatted list with pathogen structure and data of class
EpiStrainDynamics.pathogen_structure.
Other pathogen_structure:
multiple(),
subtyped()
# Using a data frame single( data = sarscov2, case_timeseries = "cases", time = "date" ) # Using a time series object (time argument is optional) sarscov2_xts <- xts::xts(sarscov2[, c("cases", "alpha")], order.by = sarscov2$date) single( data = sarscov2_xts, case_timeseries = "cases" )# Using a data frame single( data = sarscov2, case_timeseries = "cases", time = "date" ) # Using a time series object (time argument is optional) sarscov2_xts <- xts::xts(sarscov2[, c("cases", "alpha")], order.by = sarscov2$date) single( data = sarscov2_xts, case_timeseries = "cases" )
This function creates a standardized smoothing structure object that specifies
both the smoothing structure and associated priors for EpiStrainDynamics models.
tau (denoted in Eales et al. 2022, Epidemics) is the
smoothing parameter that penalises how much the underlying trend's growth
rate is allowed to change over time. Smaller values enforce a smoother
trend; larger values allow it to bend more sharply.
smoothing_structure(smoothing_type = "shared", tau_mean = NULL, tau_sd = NULL)smoothing_structure(smoothing_type = "shared", tau_mean = NULL, tau_sd = NULL)
smoothing_type |
Character string specifying the smoothing type:
|
tau_mean |
Optional numeric vector specifying the prior mean(s) for tau
parameter. Can be provided for |
tau_sd |
Numeric vector specifying the prior standard deviation(s)
for tau parameter. Can be provided for |
An object of class EpiStrainDynamics.smoothing containing:
smoothing_type |
The specified smoothing structure type |
tau_priors |
Prior specifications for tau |
priors_provided |
Integer flag passed to the Stan model: |
# Shared smoothing with scalar priors shared_smooth <- smoothing_structure("shared", tau_mean = 0, tau_sd = 1) # Independent smoothing with vector priors indep_smooth <- smoothing_structure("independent", tau_mean = c(0, 0, 0), tau_sd = c(1, 1, 1) )# Shared smoothing with scalar priors shared_smooth <- smoothing_structure("shared", tau_mean = 0, tau_sd = 1) # Independent smoothing with vector priors indep_smooth <- smoothing_structure("independent", tau_mean = c(0, 0, 0), tau_sd = c(1, 1, 1) )
Subtyped pathogen structure
subtyped( data, case_timeseries, unsubtyped_timeseries, subtyped_timeseries, other_pathogen_timeseries, time = NULL )subtyped( data, case_timeseries, unsubtyped_timeseries, subtyped_timeseries, other_pathogen_timeseries, time = NULL )
data |
dataframe containing columns with all relevant data, or a time series object (ts, xts, zoo, tsibble, etc.) |
case_timeseries |
Column name containing case counts. Must be numeric
or a |
unsubtyped_timeseries |
vector of column names with additional
unsubtyped pathogen case count timeseries. Must be numeric or a
|
subtyped_timeseries |
vector of column names with additional
subtyped pathogen case count timeseries. Must be numeric or a
|
other_pathogen_timeseries |
vector of column names with additional
pathogen case count timeseries to model. Must be numeric or a |
time |
name of column with time data. Required for non-time-series
input data. Flexible format - can be date, index, or others, accepted
as |
named list including pathogen_structure, pathogen_names, and data
of class EpiStrainDynamics.pathogen_structure
Other pathogen_structure:
multiple(),
single()
# Using a data frame subtyped( data = influenza, case_timeseries = "ili", unsubtyped_timeseries = "inf_A", subtyped_timeseries = c("inf_H3N2", "inf_H1N1"), other_pathogen_timeseries = c("inf_B", "other"), time = "week" ) # Using a time series object (time argument is optional) influenza_xts <- xts::xts( influenza[, c("ili", "inf_A", "inf_H3N2", "inf_H1N1", "inf_B", "other")], order.by = influenza$week ) subtyped( data = influenza_xts, case_timeseries = "ili", unsubtyped_timeseries = "inf_A", subtyped_timeseries = c("inf_H3N2", "inf_H1N1"), other_pathogen_timeseries = c("inf_B", "other") )# Using a data frame subtyped( data = influenza, case_timeseries = "ili", unsubtyped_timeseries = "inf_A", subtyped_timeseries = c("inf_H3N2", "inf_H1N1"), other_pathogen_timeseries = c("inf_B", "other"), time = "week" ) # Using a time series object (time argument is optional) influenza_xts <- xts::xts( influenza[, c("ili", "inf_A", "inf_H3N2", "inf_H1N1", "inf_B", "other")], order.by = influenza$week ) subtyped( data = influenza_xts, case_timeseries = "ili", unsubtyped_timeseries = "inf_A", subtyped_timeseries = c("inf_H3N2", "inf_H1N1"), other_pathogen_timeseries = c("inf_B", "other") )