1 estimators_logic
<- function(input
, output
, react_values
) {
2 # Initialize a data frame to hold estimates.
3 react_values$estimates_table
<- data.frame(Dataset
= character(0))
4 # Initialize a list to hold added estimators.
5 react_values$estimators
<- list()
7 add_id(input
, output
, react_values
)
8 add_idea(input
, output
, react_values
)
9 add_seq_bayes(input
, output
, react_values
)
10 add_wp(input
, output
, react_values
)
12 render_estimates(output
, react_values
)
13 delete_estimators(input
, react_values
)
14 export_estimates(output
, react_values
)
17 # If an estimator is added, ensure it is not a duplicate and add it to the list
18 # of estimators. This function should be called at the end of each
19 # estimator-specific 'add' function, after validating their parameters.
20 add_estimator
<- function(method
, new_estimator
, output
, react_values
) {
21 num_estimators
<- length(react_values$estimators
)
23 # Check whether the new estimator is a duplicate, and warn if so.
24 for (i
in seq_len(num_estimators
)) {
25 if (identical(new_estimator
, react_values$estimators
[[i
]])) {
26 showNotification("Error: This estimator has already been added.",
27 duration
= 3, id
= "notify-error"
33 # Add the new estimator to the list of estimators.
34 react_values$estimators
[[num_estimators
+ 1]] <- new_estimator
36 showNotification("Estimator added successfully.",
37 duration
= 3, id
= "notify-success"
40 # Evaluate all the new estimator on all existing datasets and create a new
41 # column in the estimates table.
42 update_estimates_col(new_estimator
, react_values
)
45 # Ensure serial intervals are specified as positive numbers.
46 validate_mu
<- function(method
, input
, output
) {
47 mu
<- suppressWarnings(as.numeric(trimws(input
[[paste0("mu_", method
)]])))
48 if (is.na(mu
) || mu
<= 0) {
49 output
[[paste0("mu_", method
, "_warn")]] <- renderText(
50 "The serial interval must be a positive number."
54 output
[[paste0("mu_", method
, "_warn")]] <- renderText("")
58 # Incidence Decay (ID).
59 add_id
<- function(input
, output
, react_values
) {
60 observeEvent(input$add_id
, {
61 mu
<- validate_mu("id", input
, output
)
63 new_estimator
<- list(
64 method
= "id", mu
= mu
, mu_units
= input$mu_id_units
66 add_estimator("id", new_estimator
, output
, react_values
)
71 # Incidence Decay and Exponential Adjustment (IDEA).
72 add_idea
<- function(input
, output
, react_values
) {
73 observeEvent(input$add_idea
, {
74 mu
<- validate_mu("idea", input
, output
)
76 new_estimator
<- list(
77 method
= "idea", mu
= mu
, mu_units
= input$mu_idea_units
79 add_estimator("idea", new_estimator
, output
, react_values
)
84 # Sequential Bayes (seqB).
85 add_seq_bayes
<- function(input
, output
, react_values
) {
86 observeEvent(input$add_seq_bayes
, {
87 mu
<- validate_mu("seq_bayes", input
, output
)
89 kappa
<- trimws(input$kappa
)
90 kappa
<- if (kappa
== "") 20 else suppressWarnings(as.numeric(kappa
))
92 if (is.na(kappa
) || kappa
<= 0) {
93 output$kappa_warn
<- renderText(
94 "The maximum prior must be a positive number."
96 } else if (!is.null(mu
)) {
97 output$kappa_warn
<- renderText("")
98 new_estimator
<- list(
99 method
= "seq_bayes", mu
= mu
,
100 mu_units
= input$mu_seq_bayes_units
, kappa
= kappa
102 add_estimator("seq_bayes", new_estimator
, output
, react_values
)
107 # White and Pagano (WP).
108 add_wp
<- function(input
, output
, react_values
) {
109 observeEvent(input$add_wp
, {
110 if (input$wp_mu_known
== "Yes") {
111 mu
<- validate_mu("wp", input
, output
)
113 new_estimator
<- list(method
= "wp",
114 mu
= mu
, mu_units
= input$mu_wp_units
116 add_estimator("wp", new_estimator
, output
, react_values
)
119 grid_length
<- trimws(input$grid_length
)
120 max_shape
<- trimws(input$max_shape
)
121 max_scale
<- trimws(input$max_scale
)
124 grid_length
<- if (grid_length
== "") 100 else as.numeric(grid_length
)
125 max_shape
<- if (max_shape
== "") 10 else as.numeric(max_shape
)
126 max_scale
<- if (max_scale
== "") 10 else as.numeric(max_scale
)
131 if (is.na(grid_length
) || grid_length
<= 0) {
132 output$grid_length_warn
<- renderText(
133 "The grid length must be a positive integer."
137 output$grid_length_warn
<- renderText("")
140 if (is.na(max_shape
) || max_shape
<= 0) {
141 output$max_shape_warn
<- renderText(
142 "The maximum shape must be a positive number."
146 output$max_shape_warn
<- renderText("")
149 if (is.na(max_scale
) || max_scale
<= 0) {
150 output$max_scale_warn
<- renderText(
151 "The maximum scale must be a positive number."
155 output$max_scale_warn
<- renderText("")
159 new_estimator
<- list(method
= "wp", mu
= NA, grid_length
= grid_length
,
160 max_shape
= max_shape
, max_scale
= max_scale
162 add_estimator("wp", new_estimator
, output
, react_values
)
168 # Convert an estimator's specified serial interval to match the time units of
170 convert_mu_units
<- function(data_units
, estimator_units
, mu
) {
171 if (data_units
== "Days" && estimator_units
== "Weeks") {
173 } else if (data_units
== "Weeks" && estimator_units
== "Days") {
179 # Add a column to the estimates table when a new estimator is added.
180 update_estimates_col
<- function(estimator
, react_values
) {
181 dataset_rows
<- seq_len(nrow(react_values$data_table
))
182 estimates
<- dataset_rows
184 for (row
in dataset_rows
) {
185 estimate
<- eval_estimator(estimator
, react_values$data_table
[row
, ])
186 estimates
[row
] <- estimate
189 estimates
<- data.frame(estimates
)
190 colnames(estimates
) <- estimates_col_name(estimates
, estimator
)
192 react_values$estimates_table
<- cbind(
193 react_values$estimates_table
, estimates
197 # Evaluate the specified estimator on the given dataset.
198 eval_estimator
<- function(estimator
, dataset
) {
199 cases
<- as.integer(unlist(strsplit(dataset
[, 3], ",")))
201 if (estimator$method
== "id") {
202 mu
<- convert_mu_units(dataset
[, 2], estimator$mu_units
, estimator$mu
)
203 estimate
<- round(Rnaught
::id(cases
, mu
), 2)
204 } else if (estimator$method
== "idea") {
205 mu
<- convert_mu_units(dataset
[, 2], estimator$mu_units
, estimator$mu
)
206 estimate
<- round(Rnaught
::idea(cases
, mu
), 2)
207 } else if (estimator$method
== "seq_bayes") {
208 mu
<- convert_mu_units(dataset
[, 2], estimator$mu_units
, estimator$mu
)
209 estimate
<- round(Rnaught
::seq_bayes(cases
, mu
, estimator$kappa
), 2)
210 } else if (estimator$method
== "wp") {
211 if (is.na(estimator$mu
)) {
212 estimate
<- Rnaught
::wp(cases
, serial
= TRUE,
213 grid_length
= estimator$grid_length
,
214 max_shape
= estimator$max_shape
, max_scale
= estimator$max_scale
216 estimated_mu
<- round(sum(estimate$supp
* estimate$pmf
), 2)
217 estimate
<- paste0(round(estimate$r0
, 2), " (μ = ", estimated_mu
,
218 " ", tolower(dataset
[, 2]), ")"
221 mu
<- convert_mu_units(dataset
[, 2], estimator$mu_units
, estimator$mu
)
222 estimate
<- round(Rnaught
::wp(cases
, mu
), 2)
229 # Create the column name of an estimator when it is
230 # added to the estimates table.
231 estimates_col_name
<- function(estimates
, estimator
) {
232 if (estimator$method
== "id") {
233 return(paste0("ID", " (μ = ", estimator$mu
, " ",
234 tolower(estimator$mu_units
), ")"
236 } else if (estimator$method
== "idea") {
237 return(paste0("IDEA", " (μ = ", estimator$mu
, " ",
238 tolower(estimator$mu_units
), ")"
240 } else if (estimator$method
== "seq_bayes") {
241 return(paste0("seqB", " (μ = ", estimator$mu
, " ",
242 tolower(estimator$mu_units
), ", κ = ", estimator$kappa
, ")"
244 } else if (estimator$method
== "wp") {
245 if (is.na(estimator$mu
)) {
246 return(paste0("WP (", estimator$grid_length
, ", ",
247 round(estimator$max_shape
, 3), ", ", round(estimator$max_scale
, 3), ")"
250 return(paste0("WP", " (μ = ", estimator$mu
, " ",
251 tolower(estimator$mu_units
), ")"
257 # Render the estimates table whenever it is updated.
258 render_estimates
<- function(output
, react_values
) {
260 output$estimates_table
<- DT
::renderDataTable(react_values$estimates_table
,
261 selection
= list(target
= "column", selectable
= c(0)),
262 escape
= FALSE, rownames
= FALSE,
264 columnDefs
= list(list(className
= "dt-left", targets
= "_all"))
270 # Delete columns from the estimates table,
271 # as well as the corresponding estimators.
272 delete_estimators
<- function(input
, react_values
) {
273 observeEvent(input$estimators_delete
, {
274 cols_selected
<- input$estimates_table_columns_selected
275 react_values$estimators
<- react_values$estimators
[-cols_selected
]
276 react_values$estimates_table
[, cols_selected
+ 1] <- NULL
280 # Export estimates table as a CSV file.
281 export_estimates
<- function(output
, react_values
) {
282 output$estimates_export
<- downloadHandler(
283 filename
= function() {
285 "Rnaught_estimates_", format(Sys.time(), "%y-%m-%d_%H-%M-%S"), ".csv"
288 content
= function(file
) {
289 output_table
<- data.frame(
290 lapply(react_values$estimates_table
, sub_entity
)
292 colnames(output_table
) <- sub_entity(
293 colnames(react_values$estimates_table
)
295 write.csv(output_table
, file
, row.names
= FALSE)
300 # Substitute HTML entity codes with natural names.
301 sub_entity
<- function(obj
) {
302 obj
<- gsub("μ", "mu", obj
)
303 obj
<- gsub("κ", "kappa", obj
)