-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathui.R
More file actions
242 lines (195 loc) · 14.6 KB
/
ui.R
File metadata and controls
242 lines (195 loc) · 14.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
library(shiny)
library(shinyjs)
library(shinythemes)
library(shinyWidgets)
helpPopup <- function(title, content,
placement=c('right', 'top', 'left', 'bottom'),
trigger=c('click', 'hover', 'focus', 'manual')) {
tagList(
singleton(
tags$head(
tags$script("$(function() { $(\"[data-toggle='popover']\").popover(); })")
)
),
tags$a(
href = "#", class = "btn btn-sm", `data-toggle` = "popover",
`data-container`="body",
title = title, `data-content` = content, `data-animation` = TRUE,
`data-placement` = match.arg(placement, several.ok=TRUE)[1],
`data-trigger` = match.arg(trigger, several.ok=TRUE)[1],
icon("question")
)
)
}
# Define UI for PPV application
shinyUI(fluidPage(theme = shinytheme("spacelab"),
title = "Corona / Covid-19 growth",
shinyjs::useShinyjs(),
h2(HTML("Visualization of Covid-19 confirmed cases")),
HTML('<div class="alert alert-danger alert-dismissible" role="danger"><button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>Please report any bugs as issues at <a href="https://github.com/nicebread/corona">Github</a>, or contribute with pull requests!<br>The CSSE data set currently changes its structure; the visualization of US states has been disabled until CSSE fixes the state level data sets.</div>'),
HTML('<div class="alert alert-info alert-dismissible" role="info"><button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>Disclaimer: This visualization is for research and educational purposes only and is not intended to be a tool for decision-making. There are many uncertainties and debates about the details of COVID-19 infection and case numbers. Please read the section "Putting these numbers in context" below.</div>'),
br(),
# Sidebar to select inputs
fluidRow(
# ---------------------------------------------------------------------
column(4,
h3("Data source:"),
radioButtons("datasource", "", c(
"European Centre for Disease Prevention and Control" = "ECDC",
"Johns Hopkins CSSE" = "CSSE")
),
h3("Target variable:"),
selectInput("target", "", choices = c(
"Confirmed cumulative cases" = "cum_cases",
"Confirmed cumulative deaths" = "cum_deaths_noZero",
"Confirmed cumulative cases per capita*" = "cum_cases_per_100000",
"Confirmed cumulative deaths per capita**" = "cum_deaths_per_100000_noZero"),
#"Daily growth of confirmed cases" = "dailyGrowth"),
selected = "cum_cases", multiple=FALSE, selectize=TRUE
),
p("* 100,000 x (cumulative cases / population)", style = "font-style: italic; font-size: 0.85em; color:grey; line-height:110%"
),
p("** 100,000 x (cumulative deaths / population)", style = "font-style: italic; font-size: 0.85em; color:grey; line-height:110%"
),
conditionalPanel( # do not show reference line for daily growth plot
#condition = "input.target != 'dailyGrowth'",
condition = "1==1",
h3("Exponential fit:"),
#prettySwitch("manualReferenceLine", label="Set exponential reference line manually", value = FALSE, status = "default", bigger=TRUE),
awesomeRadio(inputId = "fitLineType", label = "Type of exponential fit line:", choices = c("no fit line"="none", "automatic"="automatic", "manual"="manual"), inline = TRUE),
conditionalPanel(
condition = "input.fitLineType != 'none'",
p('The exponential curve is ', em("not"), ' an epidemiological model (although in early stages an epidemic can show exponential growth). Importantly, the projection of the exponential curve is ', em("not"), ' a proper epidemiological forecast of the development.', style = "font-style: italic; font-size: 0.85em; color: red; line-height:110%")
),
conditionalPanel(
condition = "input.fitLineType == 'manual'",
p("Using the two sliders, you can manually adjust the reference line.", style = "font-style: italic; font-size: 0.85em; color:grey; line-height:110%"
),
sliderInput("offset", label = "Offset at start:", min = 1, max = 5000, value = 100, step = 5),
sliderInput("percGrowth", label = "% daily growth:", min = 0, max = 100, value = 33, step = 1)
),
conditionalPanel(
condition = "input.fitLineType == 'automatic'",
sliderInput("estRange", label = "Estimate growth rate between these 'days since X cumulative cases' only:", min = 1, max = 100, value = c(1, 100), step = 1),
p("Intercept and exponential growth rate are estimated from all data in the plot which is in the date range indicated by the slider above. This is mostly useful when only a single country is selected. When multiple countries are selected, the average growth rate is estimated.", style = "font-style: italic; font-size: 0.85em; color:grey; line-height:110%"),
uiOutput("ui_estimationNote")
),
conditionalPanel(
condition = "input.logScale == 'log'",
h3("Reference lines (doubling every X days)"),
checkboxInput("refLines", "Show reference lines", value=TRUE),
conditionalPanel(
condition = "input.fitLineType != 'automatic'",
sliderInput("refLineOffset", label = "Offset at start:", min = 1, max = 5000, value = 100, step = 5)
)
)
),
h3("Alignment:"),
conditionalPanel(
#condition = "input.target != 'dailyGrowth'",
condition = "1==1",
sliderInput("align_value", label = "Align countries at the day when a country passed this number at the target variable:", min = 0, max = 1000, value = 100, step = 5)),
conditionalPanel(
condition = "input.target == 'dailyGrowth'",
sliderInput("align_value_daily", label = "Align countries at the day when a country passed this number of cumulative confirmed cases:", min = 0, max = 1000, value = 100, step = 5)),
h3("State selection:"),
sliderInput("minCases", label = "Hide countries/states with less than X cases:", min = 1, max = 10000, value = 1000, step = 10),
# Panels for selecting states/countries appear conditionally based on the chosen data
conditionalPanel(
condition = "input.datasource == 'CSSE_State'",
actionButton("selectAllStates", "Select all states"),
actionButton("deselectAllStates", "Deselect all states"),
htmlOutput("state_selector")
),
conditionalPanel(
condition = "input.datasource != 'CSSE_State'",
actionButton("selectAllCountries", "Select all countries"),
actionButton("deselectAllCountries", "Deselect all countries"),
htmlOutput("country_selector")
# ---------------------------------------------------------------------
# Output column
)),
column(8,
fluidRow(column(10,
#uiOutput("ui_downloadNote"),
h3("Display options:"),
conditionalPanel(
condition = "input.usePlotly == false & input.target != 'dailyGrowth'",
radioGroupButtons("logScale", label = "y-axis transformation: ", choices = c("Linear"="linear", "Logarithmic"="log"))
),
conditionalPanel(
condition = "input.target != 'dailyGrowth'",
checkboxInput("usePlotly", "Use interactive plot (experimental!)", value=FALSE),
checkboxInput("showRandomSlopes", "Show predictions for each country in plot (random slopes)", value=FALSE)
),
conditionalPanel(
condition = "input.target == 'dailyGrowth'",
sliderInput("smoother_span", label = "Smoother span:", min = 0.15, max = 2, value = 0.75, step = .01),
checkboxInput("smoother_se", "Show smoother CI", value=FALSE)
),
conditionalPanel(
condition = "input.datasource != 'CSSE_State' & input.target != 'dailyGrowth'",
checkboxInput("showAnnotation", "Show annotations in plot", value=FALSE),
uiOutput("ui_annotationWarning"),
textAreaInput("annotation", "Annotations (provide as CSV values)", "Country, StartDate, Label, Source
Italy, 2020-03-10, Start national lockdown, https://en.wikipedia.org/wiki/2020_Italy_coronavirus_lockdown
Germany, 2020-03-23, Start national lockdown, https://www.zdf.de/nachrichten/politik/coronavirus-ausgangsbeschraenkung-verschaerfung-ueberblick-100.html", width = "800px")
)
)#,
# column(2,
# HTML("<br><br><br><br>"),
# conditionalPanel(
# condition = "input.usePlotly == false",
# downloadButton("DownloadFig", "Download Plot")
# )
# )
),
conditionalPanel(
condition = "input.usePlotly != true",
uiOutput("normalPlot")
),
conditionalPanel(
condition = "input.usePlotly == true",
uiOutput("interactivePlot")
),
h2("Putting these numbers in context"),
help("This is a growing (and non-exhaustive!) collection of caveats."),
HTML('
<ul>
<li>The numbers are confirmed cases after testing. There presumably is a huge <b>rate of undetected cases</b>. One paper published in Science magazine estimates that 86% of all infections were undocumented <a href="https://science.sciencemag.org/content/early/2020/03/13/science.abb3221.abstract" target="_blank">(Li et al., 2020)</a></li>
<li><b>Between-country comparisons of absolute numbers are difficult</b> as there are huge differences in the amount of testing. (Countries that test more get more confirmed cases, at least unless all cases are detected. Countries that do not test at all have no reported cases at all.)</li>
<li>If testing practices do not change within a country (which is a big IF), temporal <b>within-country comparisons can be considered valid</b>.</li>
<ul>
<li>That also means: If countries slow down testing after/during an intervention, it looks like as if the intervention is effective.</li>
</ul>
<li>Between-country <b>comparisons of deaths and death rates are also problematic</b>. For example, <a href="https://twitter.com/G_House__MD/status/1238485575591698433?s=20" target="_blank">Germany does not do/ does less post-mortem testing</a> for Covid-19, while Italy [citation needed] and <a href="https://www.cdc.gov/coronavirus/2019-ncov/hcp/guidance-postmortem-specimens.html" target="_blank">USA</a> seem to do that. This might explain the huge differences in death rates between Germany, Italy, and other countries.</li>
<li>There are huge <b>differences between countries in <i>who</i> gets tested</b>. Some test only severe case, some test random samples of people (also without symptoms). In the former case, calculated death rates are much higher due to selection bias.</li>
<li>Related to the previous point: <b>Case-fatality risks</b> (i.e., "How many of the sick people die?") <b>are notoriously hard to calculate</b> and prone to many biases (<a href="https://journals.plos.org/plosntds/article?id=10.1371/journal.pntd.0003846" target="_blank">Lipsitch et al., 2015</a>).</li>
<li><b>Demographics differ between countries / affected regions</b>. It has been argued that <a href="https://www.wired.com/story/why-the-coronavirus-hit-italy-so-hard/" target="_blank">Italy has been hit hard</a> because it has a comparably old population (in particular the affected regions).</li>
<li>The exponential curve which you can fit to the data is <i>not</i> an epidemiological model (although in early stages an epidemic can show exponential growth). Importantly, the projection of the exponential curve is <i>not</i> a proper epidemiological forecast of the development. For a more refined simulation based on the SIR model, see this <a href="https://alhill.shinyapps.io/COVID19seir/?fbclid=IwAR2aXJT79M2AmZxMdy8jsiEuSC4i7ijU8Av6oB4dmlZIeJ2VQgL7Tt3QGxA" target="_blank">interactive app</a>.</li>
</ul>
'),
h3("Other sources of such considerations:"),
HTML('
<ul>
<li><a href="https://www.cebm.net/global-covid-19-case-fatality-rates/">Global Covid-19 Case Fatality Rates (Jason Oke, Carl Heneghan)</a></li>
<li><a href="https://blog.datawrapper.de/coronaviruscharts/#considerations">17 (or so) responsible live visualizations about the coronavirus, for you to use</a> by Lisa Charlotte Rost</li>
</ul>
<p>Several Twitter threads warn against specific Corona visualizations (which seem to contain errors), or against visualizing this data at all, or that only experts in epidemiology should do such visualizations:</p>
<ul>
<li><a href="https://twitter.com/danitte/status/1240305200541216769?s=20">Dania Orta-Alemán</a> points out wrong visualizations</li>
<li>Amanda Makulec: <a href="https://medium.com/nightingale/ten-considerations-before-you-create-another-chart-about-covid-19-27d3bd691be8">Ten Considerations Before You Create Another Chart About COVID-19</a> (blog post)</li>
</ul>
')
)
),
HTML('This visualization is inspired by a figure from the <a href="https://www.ft.com/content/a26fbf7e-48f8-11ea-aeb3-955839e06441">Financial Times</a>, created by <a href="https://twitter.com/jburnmurdoch" target="_blank">John Burn-Murdoch</a>.<br>'),
HTML('Data sources for Covid-19 cases : <a href="https://www.ecdc.europa.eu/en/publications-data/download-todays-data-geographic-distribution-covid-19-cases-worldwide" target="_blank">European Centre for Disease Prevention and Control</a> and <a href="https://github.com/CSSEGISandData/COVID-19" target="_blank">Johns Hopkins CSSE</a> (both are updated daily)<br>'),
HTML('Data sources for country population: <a href="https://data.worldbank.org/indicator/SP.POP.TOTL" target="_blank">The World Bank data</a><br><br>'),
HTML('2020. Contributors: <a href="https://www.nicebread.de" target="_blank">Felix Schönbrodt</a>, <a href="https://github.com/astefan1" target="_blank">Angelika Stefan</a>, <a href="https://github.com/zuphilip" target="_blank">Philipp Zumstein</a>, <a href="https://github.com/pab2163" target="_blank">Paul A. Bloom</a><br>'),
HTML('Open source code on Github: <a href="https://github.com/nicebread/corona" target="_blank">https://github.com/nicebread/corona</a>')
))
# Other apps:
# https://alhill.shinyapps.io/COVID19seir/?fbclid=IwAR2aXJT79M2AmZxMdy8jsiEuSC4i7ijU8Av6oB4dmlZIeJ2VQgL7Tt3QGxA
# https://gorkang.shinyapps.io/2020-corona/
# https://covid19-dash.github.io/