1 # Main logic block for estimator-related interactions.
2 estimators_logic
<- function(input
, output
, react_values
) {
3 # Initialize a data frame to hold estimates.
4 react_values$estimates_table
<- data.frame(
5 Estimator
= character(0),
6 `Serial interval`
= character(0),
10 # Initialize a list to hold added estimators.
11 react_values$estimators
<- list()
13 add_id(input
, output
, react_values
)
14 add_idea(input
, output
, react_values
)
15 add_seq_bayes(input
, output
, react_values
)
16 add_wp(input
, output
, react_values
)
18 render_estimates(output
, react_values
)
19 delete_estimators(input
, react_values
)
20 export_estimates(output
, react_values
)
23 # If an estimator is added, ensure it is not a duplicate and add it to the list
24 # of estimators. This function should be called at the end of each
25 # estimator-specific 'add' function, after validating their parameters.
26 add_estimator
<- function(method
, new_estimator
, output
, react_values
) {
27 num_estimators
<- length(react_values$estimators
)
29 # Check whether the new estimator is a duplicate, and warn if so.
30 for (i
in seq_len(num_estimators
)) {
31 if (identical(new_estimator
, react_values$estimators
[[i
]])) {
33 "Error: This estimator has already been added.", duration
= 3
39 # Add the new estimator to the list of estimators.
40 react_values$estimators
[[num_estimators
+ 1]] <- new_estimator
42 showNotification("Estimator added successfully.", duration
= 3)
44 # Evaluate the new estimator on all existing datasets and create a new row in
45 # the estimates table.
46 update_estimates_row(new_estimator
, react_values
)
49 # Ensure serial intervals are specified as positive numbers.
50 validate_mu
<- function(method
, input
, output
) {
51 mu
<- suppressWarnings(as.numeric(trimws(input
[[paste0("mu_", method
)]])))
52 if (is.na(mu
) || mu
<= 0) {
53 output
[[paste0("mu_", method
, "_warn")]] <- renderText(
54 "The serial interval must be a positive number."
58 output
[[paste0("mu_", method
, "_warn")]] <- renderText("")
62 # Incidence Decay (ID).
63 add_id
<- function(input
, output
, react_values
) {
64 observeEvent(input$add_id
, {
65 mu
<- validate_mu("id", input
, output
)
67 new_estimator
<- list(
68 method
= "id", mu
= mu
, mu_units
= input$mu_id_units
70 add_estimator("id", new_estimator
, output
, react_values
)
75 # Incidence Decay and Exponential Adjustment (IDEA).
76 add_idea
<- function(input
, output
, react_values
) {
77 observeEvent(input$add_idea
, {
78 mu
<- validate_mu("idea", input
, output
)
80 new_estimator
<- list(
81 method
= "idea", mu
= mu
, mu_units
= input$mu_idea_units
83 add_estimator("idea", new_estimator
, output
, react_values
)
88 # Sequential Bayes (seqB).
89 add_seq_bayes
<- function(input
, output
, react_values
) {
90 observeEvent(input$add_seq_bayes
, {
91 mu
<- validate_mu("seq_bayes", input
, output
)
93 kappa
<- trimws(input$kappa
)
94 kappa
<- if (kappa
== "") 20 else suppressWarnings(as.numeric(kappa
))
96 if (is.na(kappa
) || kappa
< 1) {
97 output$kappa_warn
<- renderText(
98 "The maximum prior must be a number greater than or equal to 1."
100 } else if (!is.null(mu
)) {
101 output$kappa_warn
<- renderText("")
102 new_estimator
<- list(
103 method
= "seq_bayes", mu
= mu
,
104 mu_units
= input$mu_seq_bayes_units
, kappa
= kappa
106 add_estimator("seq_bayes", new_estimator
, output
, react_values
)
111 # White and Pagano (WP).
112 add_wp
<- function(input
, output
, react_values
) {
113 observeEvent(input$add_wp
, {
114 if (input$wp_mu_known
== "Yes") {
115 mu
<- validate_mu("wp", input
, output
)
117 new_estimator
<- list(method
= "wp",
118 mu
= mu
, mu_units
= input$mu_wp_units
120 add_estimator("wp", new_estimator
, output
, react_values
)
123 grid_length
<- trimws(input$grid_length
)
124 max_shape
<- trimws(input$max_shape
)
125 max_scale
<- trimws(input$max_scale
)
128 grid_length
<- if (grid_length
== "") 100 else as.numeric(grid_length
)
129 max_shape
<- if (max_shape
== "") 10 else as.numeric(max_shape
)
130 max_scale
<- if (max_scale
== "") 10 else as.numeric(max_scale
)
135 if (is.na(grid_length
) || grid_length
<= 0) {
136 output$grid_length_warn
<- renderText(
137 "The grid length must be a positive integer."
141 output$grid_length_warn
<- renderText("")
144 if (is.na(max_shape
) || max_shape
<= 0) {
145 output$max_shape_warn
<- renderText(
146 "The maximum shape must be a positive number."
150 output$max_shape_warn
<- renderText("")
153 if (is.na(max_scale
) || max_scale
<= 0) {
154 output$max_scale_warn
<- renderText(
155 "The maximum scale must be a positive number."
159 output$max_scale_warn
<- renderText("")
163 new_estimator
<- list(method
= "wp", mu
= NA, grid_length
= grid_length
,
164 max_shape
= max_shape
, max_scale
= max_scale
166 add_estimator("wp", new_estimator
, output
, react_values
)
172 # Convert an estimator's specified serial interval to match the time units of
174 convert_mu_units
<- function(data_units
, estimator_units
, mu
) {
175 if (data_units
== "Days" && estimator_units
== "Weeks") {
177 } else if (data_units
== "Weeks" && estimator_units
== "Days") {
183 # Add a row to the estimates table when a new estimator is added.
184 update_estimates_row
<- function(estimator
, react_values
) {
185 dataset_rows
<- seq_len(nrow(react_values$data_table
))
188 if (nrow(react_values$data_table
) > 0) {
189 estimates
<- dataset_rows
190 for (row
in dataset_rows
) {
191 estimate
<- eval_estimator(estimator
, react_values$data_table
[row
, ])
192 estimates
[row
] <- estimate
196 new_row
<- data.frame(
197 t(c(estimator_name(estimator
), estimator_mu_text(estimator
), estimates
))
199 colnames(new_row
) <- colnames(react_values$estimates_table
)
201 react_values$estimates_table
<- rbind(
202 react_values$estimates_table
, new_row
206 # Evaluate the specified estimator on the given dataset.
207 eval_estimator
<- function(estimator
, dataset
) {
208 cases
<- as.integer(unlist(strsplit(dataset
[, 3], ",")))
212 if (estimator$method
== "id") {
213 mu
<- convert_mu_units(dataset
[, 2], estimator$mu_units
, estimator$mu
)
214 estimate
<- round(Rnaught
::id(cases
, mu
), 2)
215 } else if (estimator$method
== "idea") {
216 mu
<- convert_mu_units(dataset
[, 2], estimator$mu_units
, estimator$mu
)
217 estimate
<- round(Rnaught
::idea(cases
, mu
), 2)
218 } else if (estimator$method
== "seq_bayes") {
219 mu
<- convert_mu_units(dataset
[, 2], estimator$mu_units
, estimator$mu
)
220 estimate
<- round(Rnaught
::seq_bayes(cases
, mu
, estimator$kappa
), 2)
221 } else if (estimator$method
== "wp") {
222 if (is.na(estimator$mu
)) {
223 estimate
<- Rnaught
::wp(cases
, serial
= TRUE,
224 grid_length
= estimator$grid_length
,
225 max_shape
= estimator$max_shape
, max_scale
= estimator$max_scale
227 estimated_mu
<- round(sum(estimate$supp
* estimate$pmf
), 2)
228 estimate
<- paste0(round(estimate$r0
, 2), " (SI = ", estimated_mu
,
229 " ", tolower(dataset
[, 2]), ")"
232 mu
<- convert_mu_units(dataset
[, 2], estimator$mu_units
, estimator$mu
)
233 estimate
<- round(Rnaught
::wp(cases
, mu
), 2)
238 }, error
= function(e
) {
241 " [Estimator: ", sub(" .*", "", estimator_name(estimator
)),
242 ", Dataset: ", dataset
[, 1], "]"
250 # Create the name of an estimator to be added to the first column of the
252 estimator_name
<- function(estimator
) {
253 if (estimator$method
== "id") {
255 } else if (estimator$method
== "idea") {
257 } else if (estimator$method
== "seq_bayes") {
258 return(paste0("seqB", " (κ = ", estimator$kappa
, ")"))
259 } else if (estimator$method
== "wp") {
260 if (is.na(estimator$mu
)) {
261 return(paste0("WP (", estimator$grid_length
, ", ",
262 round(estimator$max_shape
, 3), ", ", round(estimator$max_scale
, 3), ")"
270 # Create the text to be displayed for the serial interval in the second column
271 # of the estimates table.
272 estimator_mu_text
<- function(estimator
) {
273 if (is.na(estimator$mu
)) {
276 paste(estimator$mu
, tolower(estimator$mu_units
))
279 # Render the estimates table whenever it is updated.
280 render_estimates
<- function(output
, react_values
) {
282 output$estimates_table
<- DT
::renderDataTable(react_values$estimates_table
,
283 escape
= FALSE, rownames
= FALSE,
285 columnDefs
= list(list(className
= "dt-left", targets
= "_all"))
291 # Delete rows from the estimates table and the corresponding estimators.
292 delete_estimators
<- function(input
, react_values
) {
293 observeEvent(input$estimators_delete
, {
294 rows_selected
<- input$estimates_table_rows_selected
295 react_values$estimators
<- react_values$estimators
[-rows_selected
]
296 react_values$estimates_table
<-
297 react_values$estimates_table
[-rows_selected
, ]
301 # Export estimates table as a CSV file.
302 export_estimates
<- function(output
, react_values
) {
303 output$estimates_export
<- downloadHandler(
304 filename
= function() {
306 "Rnaught_estimates_", format(Sys.time(), "%y-%m-%d_%H-%M-%S"), ".csv"
309 content
= function(file
) {
310 output_table
<- data.frame(
311 lapply(react_values$estimates_table
, sub_entity
)
313 colnames(output_table
) <- sub_entity(
314 colnames(react_values$estimates_table
)
316 write.csv(output_table
, file
, row.names
= FALSE)
321 # Substitute HTML entity codes with natural names.
322 sub_entity
<- function(obj
) {
323 obj
<- gsub("κ", "kappa", obj
)