# load the necessary libraries
library(tidyverse)
library(shiny)
library(readr)
library(janitor)
library(purrr)
library(lubridate)
library(DT)
library(ggthemes)
library(rvest)
library(polite)
Class Activity 18
Basic Shiny App for Tracking COVID-19 Trends
The Shiny app you are developing will allow users to interactively select parameters such as month, year, and county to visualize COVID-19 trends in Minnesota based on real-time data scraping and manipulation.
Read Data
<- bow(url = "https://usafacts.org/visualizations/coronavirus-covid-19-spread-map/state/minnesota") %>%
table_usafacts scrape() %>% html_nodes("a") %>% # find all links
html_attr("href") %>% # get the url
str_subset(".csv") # find those that end in csv
library(lubridate)
<- read_csv(table_usafacts[2]) %>% filter(State == "MN") %>%
covid_data select(-countyFIPS, -StateFIPS, -State) %>%
slice(-1) %>%
pivot_longer(-1, names_to = "Dates", values_to = "Cases") %>%
::clean_names() %>%
janitormutate(county_name = str_remove(county_name, " County"),
dates = ymd(dates),
counties = factor(county_name),
month = month(dates),
year = year(dates)) %>%
select(-county_name)
head(covid_data)
# A tibble: 6 × 5
dates cases counties month year
<date> <dbl> <fct> <dbl> <dbl>
1 2020-01-22 0 Aitkin 1 2020
2 2020-01-23 0 Aitkin 1 2020
3 2020-01-24 0 Aitkin 1 2020
4 2020-01-25 0 Aitkin 1 2020
5 2020-01-26 0 Aitkin 1 2020
6 2020-01-27 0 Aitkin 1 2020
# County level data
<- covid_data %>% pull(counties) %>% unique()
county_names <- map(county_names, ~filter(covid_data, counties == .x)) county_data
You’re already given the code to scrape, clean, and manipulate the data. Ensure that the data frame covid_data
is correctly formatted, with each row representing a unique combination of date and county case counts. The transformations include:
- Pivoting the data to a long format where each date-case pair is a row.
- Cleaning column names and extracting relevant date components (e.g., month, year).
Building the Shiny UI
The ui
object defines the layout and appearance of your app. Start with a basic structure:
Title Panel
: Displays the title of your app at the top.Sidebar Layout
: Contains two main parts:Sidebar Panel
: Includes interactive controls like sliders, radio buttons, and dropdown menus to filter the data.Main Panel
: Displays the outputs (graphs and tables) based on user inputs.
<- fluidPage(
ui titlePanel(" a title to my app "), # separated by comma
sidebarLayout(
sidebarPanel(" this is where user interacts "),
mainPanel(" this is the results section")
)
)
<- function(input, output) {}
server shinyApp(ui = ui, server = server)
Adding Interactive Inputs
Populate the sidebar with input controls: - Sliderinput
: Allows selection of the range of months. - RadioButtons
: Enables choosing the year. - SelectInput
: Dropdown to select the county from available data.
<- fluidPage(
ui titlePanel("Tracking Covid in Minnesota"),
sidebarLayout(
sidebarPanel(
sliderInput(inputId = "month", label = "Month?", min = 1, max = 12, value = 6),
radioButtons(inputId = "year", label = "Year?", choices = 2020:2023),
selectInput(inputId = "county", label = "County?", choices = county_names, selected = "Rice")
),mainPanel(" the results go here")
)
)
<- function(input, output) {}
server shinyApp(ui = ui, server = server, options = list(height = 800))
Configuring Outputs in UI
Define placeholders in the main panel where outputs will be rendered:
plotOutput
: To display trend plots.tableOutput
: To show data tables.
<- fluidPage(
ui titlePanel("Tracking Covid in Minnesota"),
sidebarLayout(
sidebarPanel(
sliderInput(inputId = "month", label = "Month?", min = 1, max = 12, value = 6),
radioButtons(inputId = "year", label = "Year?", choices = 2020:2023),
selectInput(inputId = "county", label = "County?", choices = county_names, selected = "Rice")
),mainPanel(
plotOutput("myplot"),
::dataTableOutput("mydata")
DT
)
)
)
<- function(input, output) {}
server shinyApp(ui = ui, server = server, options = list(height = 800))
Writing the Server Function
The server
part of your Shiny app listens for changes in the inputs and dynamically updates the outputs. Here’s how you can set it up:
Reactive Expression
: Creates a subset ofcovid_data
based on user inputs. This reactive expression ensures that any change in inputs automatically triggers data filtering.- Render Functions:
renderPlot
: Generates and renders a plot based on the filtered data.renderDataTable
: Displays the filtered data as a table.
Running the App
<- fluidPage(
ui titlePanel("Tracking Covid in Minnesota"),
sidebarLayout(
sidebarPanel(
sliderInput(inputId = "months", label = "Month?", min = 1, max = 12, value = c(3,6)),
radioButtons(inputId = "years", label = "Year?", choices = 2020:2023, selected = 2023),
selectInput(inputId = "county", label = "County?", choices = county_names, selected = "Rice")
),mainPanel(
plotOutput(outputId = "plot"), br(),
::dataTableOutput(outputId = "table")
DT
)
) )
<- function(input, output) {
server <- reactive({
filtered_data subset(covid_data,
%in% input$county &
counties >= input$months[1] & month <= input$months[2] &
month == input$years) })
year
$plot <- renderPlot({
outputggplot(filtered_data(), aes(x=dates, y=cases, color=counties)) + theme_economist_white()+
geom_point(alpha=0.5, color = "blue") + theme(legend.position = "none") +
ylab("Number of Cases") + xlab("Date")})
$table <- DT::renderDataTable({
outputfiltered_data()})
}
<- shinyApp(ui = ui, server = server, options = list(height = 1200))
app app
(Time permitting) Additional Layout
To enhance your Shiny app, first install and load the shinythemes
package to access additional aesthetic themes. Update your UI to utilize a navbarPage
structure, incorporating tabPanel
elements to separate the plot and data table into distinct tabs. Apply one of the themes, such as “cerulean”, to improve the visual appeal and user experience of your app. Replace the month and year selection inputs in your Shiny app with a dateRangeInput
to allow users to select a specific date range for viewing COVID-19 trends.
library(shinythemes)
<- navbarPage(theme = shinytheme("cerulean"), title = "Tracking Covid in Minnesota",
ui1 tabPanel("Plot",
fluidPage(
titlePanel("Covid Trends by County"),
sidebarLayout(
sidebarPanel(
dateRangeInput("dateRange",
"Select Date Range",
start = min(covid_data$dates),
end = max(covid_data$dates),
min = min(covid_data$dates),
max = max(covid_data$dates)
),selectInput(inputId = "dv", label = "County", choices = levels(covid_data$counties), selected = "Aitkin")
),mainPanel(
plotOutput(outputId = "plot")
)
)
)
),tabPanel("Data",
fluidPage(
titlePanel("Covid Data Table"),
sidebarLayout(
sidebarPanel(),
mainPanel(
::dataTableOutput(outputId = "table")
DT
)
)
)
)
)
<- function(input, output) {
server1 <- reactive({
filtered_data filter(covid_data,
%in% input$dv &
counties >= input$dateRange[1] & dates <= input$dateRange[2])
dates
})
$plot <- renderPlot({
output<- filtered_data()
data ggplot(data, aes(x = dates, y = cases, color = counties)) +
geom_point(alpha = 0.5, color = "blue") +
labs(y = "Number of Cases", x = "Date") +
theme_minimal()
})
$table <- DT::renderDataTable({
outputfiltered_data()
})
}
<- shinyApp(ui = ui1, server = server1, options = list(height = 1200))
app1 app1