COVID-19 shiny / plotly dashboard

[This article was first published on Sebastian Engel-Wolf blog, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Governments and COVID-19: Which one stops it faster, better, has fewer people dying? These questions get answered with my dashboard.

A contribution to the shiny-contest:


How did Corona spread? Using the animation feature of R-shiny this can be easily tracked.

COVID-19 is the major topic in all news channels. The place I live in is Munich, Germany. Within weeks Germany moved from 3 patients in the hospital next to my home, to have 20,000 patients. As a data-scientist, I did not only see the numbers but the exponential growth. I wanted to know:

  • How is the German government performing?
  • How do other countries stop the disease from spreading?
  • How long does it take for the disease to spread?
  • For how long is there exponential growth?
  • How many people do actually die?

To enable this I got pretty fast using shiny. With shiny you can select countries, date-ranges, make flexible tables with datatable. Great! Additionally, I used plotly to zoom into all plots, get better legends, make it easy to browse through my data.
What else… shinymaterial makes the whole app look nice. It’s a great package and comes with easy use on mobile devices. I guess that’s it.
Now I can answer all my questions by browsing through the app. It’s easy to see how well South Korea managed Corona for example. You can also see how long it took for people to die in German hospitals, while the outbreak was rather fast in Italy. Moreover, the app shows, that in the US up-to now (Apr 3rd) the spread is not really stopped.

Go to the app to see how your country performs:

If all this Corona data is too much for you, you can also check out the fun data section inside the app.


I used the following packages to build the app:

All code of this App is hosted on github:

To clean the data I mainly wrote a script which does the following:

  • Clean the Regions for CSSE Data dependent on different dates (encoding was changed 3x in 3 weeks)
  • Aggregate data per country
  • Merge data sets for confirmed, deaths, recovered to also compile the active cases
  • Aggregate per date to visualize data on the map

All this code can be found in data_gen.R

To build up the app I used shiny-modules. How to build modular shiny apps I explained several times already: App – from Truck and Trailer. This time I used standard shiny modules without classes. Each of the pages shown inside the app is such a module. So one for the map, one for the timeline charts, one for Italy….

To render the plots I only used plotly. Plotly allows the user to select certain lines, scroll into the plot and move a round. With few lines of code it is possible to create a line chart which can be grouped and colored per group:

plotly() %>% add_trace(
        data = simple_data,
        x = ~as.numeric(running_day),
        y = ~as.numeric(active),
        name = country_name,
        type = if(type == "lines") NULL else type,
        line = list(color = palette_col[which(unique(plot_data_intern2$country) == country_name)])

The result looks like this:

An important feature I wanted to build in was a table, where a lot of measurements per country are available. I set up these measurements:

  • Maximum time of exponential growth in a row: The number of days a country showed exponential growth (doubling of infections in short time) in a row. This means there was no phase of slow growth or decrease in between.
  • Days to double infections: This gives the time it took until today to double the number of infections. A higher number is better, because it takes longer to infect more people
  • Exponential growth today: Whether the countries number of infections is still exponentially growing
  • Confirmed cases: Confirmed cases today due to the Johns Hopkins CSSE data set
  • Deaths: Summed up deaths until today due to the Johns Hopkins CSSE data set
  • Population: Number of people living inside the country
  • Confirmed cases on 100,000 inhabitants: How many people have been infected if you would randomely choose 100,000 people from this country.
  • mortality Rate: Percentage of deaths per confirmed case

With the datatable package this table is scrollable and searchable. Even on mobile devices:

Last but not least, I wanted to have a map that changes over time. This was enabled using the leaflet package. leafletProxy enables to add new circles everytime the data_for_display changes. The code for the map would look like this:

leafletProxy(mapId = "outmap") %>%
       clearGroup(curr_date()) %>%
       addCircles(data = data_for_display,
                  lng = ~Long, lat = ~Lat,
                  radius = ~active_scaled,
                  popup = ~text,
                  fillColor = ~color, stroke = FALSE, fillOpacity = 0.5,
                  group = stringr::str_match(date_to_choose, "\\d{4}\\-\\d{2}\\-\\d{2}")[1,1]

With shiny, the date-slider could easily be animated

shiny::sliderInput(inputId = session$ns("datum"),
                   min = as.POSIXct("2020-02-01"),
                   max = max(all_dates()),
                   value = max(all_dates()),
                   step = 86400,
                   label = "Date", timeFormat="%Y-%m-%d", 
                   animate = animationOptions(interval = 200))

The result is the video from above:


To leave a comment for the author, please follow the link and comment on their blog: Sebastian Engel-Wolf blog. offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)