@@ -111,67 +111,63 @@ presymptomatic_transmission_to_alpha <- function(presymptomatic_transmission) {
111111 res $ minimum
112112}
113113
114- # ' Calculate proportion of runs that have controlled outbreak
114+ # ' Outbreak extinction functions
115115# '
116- # ' @inherit detect_extinct details
116+ # ' @description
117+ # ' `extinct_prob()`: Calculate proportion of runs that have controlled outbreak
117118# '
118- # ' @inheritParams detect_extinct
119+ # ' ` detect_extinct()`: Calculate whether outbreaks went extinct or not
119120# '
120- # ' @return a single `numeric` with the probability of extinction
121- # ' @export
121+ # ' @details
122+ # ' The data passed to `scenario` has to be produced by [scenario_sim()].
123+ # ' It cannot be produced by [outbreak_model()] as it requires the `sim` column,
124+ # ' which is only appended in [scenario_sim()].
122125# '
123- # ' @examples
124- # ' res <- scenario_sim(
125- # ' n = 10,
126- # ' initial_cases = 1,
127- # ' offspring = offspring_opts(
128- # ' community = \(n) rnbinom(n = n, mu = 2.5, size = 0.16),
129- # ' isolated = \(n) rnbinom(n = n, mu = 0.5, size = 1)
130- # ' ),
131- # ' delays = delay_opts(
132- # ' incubation_period = \(n) rweibull(n = n, shape = 2.32, scale = 6.49),
133- # ' onset_to_isolation = \(n) rweibull(n = n, shape = 1.65, scale = 4.28)
134- # ' ),
135- # ' event_probs = event_prob_opts(
136- # ' asymptomatic = 0,
137- # ' presymptomatic_transmission = 0.5,
138- # ' symptomatic_ascertained = 0.2
139- # ' ),
140- # ' interventions = intervention_opts(quarantine = FALSE),
141- # ' sim = sim_opts(cap_max_days = 350, cap_cases = 4500)
142- # ' )
143- # ' extinct_prob(res, cap_cases = 4500)
144- extinct_prob <- function (outbreak_df_week , cap_cases , week_range = 12 : 16 ) {
145-
146- checkmate :: assert_data_frame(outbreak_df_week )
147- checkmate :: assert_number(cap_cases , lower = 0 )
148- checkmate :: assert_numeric(week_range )
149-
150- n <- max(outbreak_df_week $ sim )
151-
152- extinct_runs <- detect_extinct(outbreak_df_week , cap_cases , week_range )
153- sum(extinct_runs $ extinct ) / n
154- }
155-
156-
157- # ' Calculate whether outbreaks went extinct or not
126+ # ' ***Warning***: the output from [scenario_sim()] contains an `cap_cases`
127+ # ' attribute which is used by [extinct_prob()] and [detect_extinct()],
128+ # ' therefore if you modify the output of [scenario_sim()] before passing
129+ # ' to [extinct_prob()] be careful not to drop the attribute (e.g.
130+ # ' from subsetting the `data.table`).
158131# '
159- # ' @details
160- # ' The `cap_cases` argument should be equal to the value supplied to
161- # ' [outbreak_model()] (possibly passed from [scenario_sim()]).
132+ # ' @param scenario a `data.table`: weekly cases output by [scenario_sim()]
133+ # ' @param extinction_week By default `NULL` but also accepts a positive
134+ # ' `integer` scalar or `integer` vector to test if the outbreak has gone
135+ # ' extinct (i.e. no new cases) by this week (zero indexed):
136+ # ' * By default (`NULL`) the extinction status is stored in the output of
137+ # ' `scenario_sim()` which is supplied to the `scenario` argument. If
138+ # ' `extinction_week` is not specified or specified as `NULL` then the
139+ # ' pre-computed extinction status from the outbreak simulation will be used.
140+ # ' This is defined as all infectious cases have had the opportunity to
141+ # ' transmit but no new cases are generated.
142+ # ' * A single `integer` to test if extinction has occurred by this week.
143+ # ' For example, `extinction_week = 5` tests whether the outbreak is
144+ # ' extinct by week 5 (inclusive) until the end of the outbreak.
145+ # ' * An `integer` vector of length two can be supplied to provide the lower
146+ # ' and upper bounds (inclusive) of the week range to test for whether
147+ # ' extinction occurred by this window. For example
148+ # ' `extinction_week = c(5, 10)` will test whether the outbreak went extinct
149+ # ' by week 5 and there we no new cases between weeks 5 and 10 (inclusive).
150+ # ' * An `integer` vector of length _n_ can be supplied to provide the weeks
151+ # ' to test for whether extinction occurred by this window. For example
152+ # ' `extinction_week = 12:16` will test that there are no new infections
153+ # ' between 12 and 16 weeks after the initial cases (inclusive). These
154+ # ' integer sequences will most likely be contiguous but the function
155+ # ' does allow non-contiguous integer sequences.
156+ # '
157+ # ' If extinction occurs before the `extinction_week` window then the outbreak
158+ # ' extinction is considered extinct, however, if the extinction occurs within
159+ # ' the `extinction_week` window it is not considered extinct. Therefore,
160+ # ' using a single `integer` for `extinction_week` and thinking of this as
161+ # ' "_has the outbreak gone extinct by week X_".
162162# '
163- # ' @param outbreak_df_week a `data.table`: weekly cases produced by the
164- # ' outbreak model
165- # ' @inheritParams sim_opts
166- # ' @param week_range a positive `integer` vector: giving the (zero indexed)
167- # ' week range to test for whether an extinction occurred. Default is `12:16`.
168- # ' @importFrom data.table as.data.table fifelse
163+ # ' @importFrom data.table setDT fifelse data.table
169164# '
170- # ' @return A `data.table`, with two columns `sim` and `extinct`, for a binary
165+ # ' @return
166+ # ' `extinct_prob()`: a single `numeric` with the probability of extinction
167+ # '
168+ # ' `detect_extinct()`: a `data.table`, with two columns `sim` and `extinct`, for a binary
171169# ' classification of whether the outbreak went extinct in each simulation
172170# ' replicate. `1` is an outbreak that went extinct, `0` if not.
173- # ' @autoglobal
174- # ' @export
175171# '
176172# ' @examples
177173# ' res <- scenario_sim(
@@ -193,16 +189,89 @@ extinct_prob <- function(outbreak_df_week, cap_cases, week_range = 12:16) {
193189# ' interventions = intervention_opts(quarantine = FALSE),
194190# ' sim = sim_opts(cap_max_days = 350, cap_cases = 4500)
195191# ' )
196- # ' detect_extinct(outbreak_df_week = res, cap_cases = 4500)
197- detect_extinct <- function (outbreak_df_week , cap_cases , week_range = 12 : 16 ) {
192+ # '
193+ # ' # calculate probability of extinction
194+ # ' extinct_prob(res)
195+ # '
196+ # ' # determine if each outbreak simulation replicate has gone extinct
197+ # ' detect_extinct(res)
198+ # '
199+ # ' # calculate extinction in the last 2 weeks of the simulated outbreak
200+ # ' # (i.e. the penultimate and last week of the outbreak)
201+ # ' extinct_prob(res, extinction_week = max(res$week) - 1)
202+ # '
203+ # ' # calculate extinction as no new cases between weeks 12 and 16 of the outbreak
204+ # ' extinct_prob(res, extinction_week = 12:16)
205+ # ' @name extinction
206+ NULL
198207
199- checkmate :: assert_data_frame(outbreak_df_week )
200- checkmate :: assert_number(cap_cases , lower = 0 )
201- checkmate :: assert_integerish(week_range )
208+ # ' @rdname extinction
209+ # ' @export
210+ extinct_prob <- function (scenario ,
211+ extinction_week = NULL ) {
212+
213+ extinct_runs <- detect_extinct(
214+ scenario = scenario ,
215+ extinction_week = extinction_week
216+ )
217+ sum(extinct_runs $ extinct ) / max(scenario $ sim )
218+ }
219+
220+ # ' @rdname extinction
221+ # ' @autoglobal
222+ # ' @export
223+ detect_extinct <- function (scenario ,
224+ extinction_week = NULL ) {
225+
226+ extinct <- attr(scenario , which = " extinct" , exact = TRUE )
227+ if (is.null(extinction_week )) {
228+ if (! is.null(extinct )) {
229+ message(
230+ " Calculating extinction using the extinction status from " ,
231+ " the simulation."
232+ )
233+ return (
234+ data.table(
235+ sim = 1 : max(scenario $ sim ),
236+ extinct = as.integer(extinct )
237+ )
238+ )
239+ } else {
240+ stop(
241+ " `extinction_week` not specified and `scenario` is missing the " ,
242+ " `extinct` attribute.\n Use `scenario_sim()` to simulate `scenario`, " ,
243+ " or specify `extinction_week`." ,
244+ call. = FALSE
245+ )
246+ }
247+ }
248+
249+ checkmate :: assert_data_frame(scenario )
250+ checkmate :: assert_integerish(extinction_week , min.len = 1 )
251+
252+ if (length(extinction_week ) == 1 ) {
253+ extinction_week <- extinction_week : max(scenario $ week )
254+ } else if (length(extinction_week ) == 2 ) {
255+ extinction_week <- min(extinction_week ): max(extinction_week )
256+ }
257+ stopifnot(
258+ " `extinction_week` not in simulated outbreak data" =
259+ all(extinction_week %in% scenario $ week )
260+ )
261+ message(
262+ " Calculating extinction as no new cases within weeks: " ,
263+ min(extinction_week ), " to " , max(extinction_week ), " (inclusive)."
264+ )
265+
266+ cap_cases <- attr(scenario , which = " cap_cases" , exact = TRUE )
267+ stopifnot(
268+ " `scenario` is missing the `cap_cases` attribute.
269+ Use `scenario_sim()` to simulate `scenario`" = ! is.null(cap_cases )
270+ )
202271
203- outbreak_df_week <- as.data.table( outbreak_df_week )
204- outbreak_df_week <- outbreak_df_week [week %in% week_range ]
205- outbreak_df_week [, list (
272+ scenario <- setDT( scenario )
273+ scenario <- scenario [week %in% extinction_week ]
274+ scenario [, list (
206275 extinct = fifelse(all(weekly_cases == 0 & cumulative < cap_cases ), 1 , 0 )
207276 ), by = sim ][]
208277}
0 commit comments