# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
# load packages
library(shiny)
library(readr)
library(ggplot2)
# load the data
dem_data <- read_csv()
# Create list of named values for the input selection
vars <- c()
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel(),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
selectInput(),
selectInput()
),
# Show a plot of the generated distribution
mainPanel(
plotOutput()
)
)
)
# Define server logic required to draw a scatter plot
server <- function(input, output, session) {
# Render the plot output
output$scatterplot <- renderPlot({
})
}
# Run the application
shinyApp(ui = ui, server = server)
Module 6.1
Building Your App
By now you should have a good sense of what kind of app you want to create for your final project. In this module, I want to walk you through the process of executing your plan and give you some general tips on how to work out potential bugs in your app. I would also point you to this Shiny cheatsheet.
One key piece of advice I will offer is to scaffold the construction of your app. Don’t try to write all of the code at once. This is especially true if you have a lot of moving parts to your app. Start with small, workable parts and build on them.
1. Wrangle some “working data”
The first thing you want to do is to wrangle some of the data that you need for the app. Crucially, you do not need to wrangle all of the data at first.
Let’s assume for a second, that you want to have an app that builds a scatter plot looking at the relationship between measures of democracy and governance from V-Dem and a large number of development indicators from WDI. You could go ahead and wrangle all of the data from both data sets and merge it or you could start out with a small number of V-Dem or WDI indicators to work with. I would encourage you to use a smaller number of indicators as a proof of concept. Then, go back and merge in more data when you know your app works.
2. Use separate files to wrangle your data and build your visualizations
Start the process of wrangling your data in a separate R script or Quarto document rather than trying to do it all in your app.R
file. Use glimpse()
and View()
to make sure that your data looks like it should. This sounds basic, but a lot the errors you are going to get in your app are going to come from bad data. Similarly, use a separate file to test your visualization or analysis with one or a handful of indicators to make sure the basic code is working properly.
3. Start building with comments and function calls
Again in the spirit of scaffolding your code, start by writing out comments stating what you want each section of the code to do and then introduce the function calls without any arguments. This can give you a clear picture of the basic components and architecture of your app. it can also help to make sure that you have all of your parentheses and brackets in the right place. Here is an example based on the scatter plot app that we built in an earlier module:
4. Test your UI code separately from your server code
Once you have your UI code built, try running it with a blank server function or commenting out the “guts” of the server function. You should be able to see whether the inputs like drop-down menus, sliders, check boxes and radio buttons are in the right place and working properly. Here is an example from our earlier scatter plot app:
You can comment out multiple lines of code by highlighting them and hitting ctrl + shift + c on a PC or command + shift + c on a Mac.
#
# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
# load packages
library(shiny)
library(readr)
library(ggplot2)
# load the data
dem_data <- read_csv("dem_data.csv")
# Create list of named values for the input selection
vars <- c("Democracy" = "polyarchy",
"Clientelism" = "clientelism",
"Corruption" = "corruption",
"Women's Empowerment" = "womens_emp",
"Wealth" = "gdp_pc",
"Infant Mortality" = "inf_mort",
"Life Expectancy" = "life_exp",
"Education" = "education")
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Democracy and Development"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
selectInput('xcol', 'X Variable', vars),
selectInput('ycol', 'Y Variable', vars, selected = vars[[6]])
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("scatterplot")
)
)
)
# Define server logic required to draw a scatter plot
server <- function(input, output, session) {
# # Render the plot output
# output$scatterplot <- renderPlot({
#
# # Build scatter plot with ggplot2
# ggplot(dem_data, aes(x = get(input$xcol), y = get(input$ycol))) +
# geom_point(aes(color = region)) +
# geom_smooth(method = "loess") +
# scale_color_viridis_d(option = "plasma") +
# theme_minimal() +
# labs(
# x = names(vars[which(vars == input$xcol)]), # select names in vars that
# y = names(vars[which(vars == input$ycol)]), # match input selections
# caption = "Source: V-Dem Institute",
# color = "Region"
# )
# })
}
# Run the application
shinyApp(ui = ui, server = server)
5. Make sure your reactive data frames have parentheses after them
If you are building a data frame with a reactive()
function, then you need to add parentheses after the name of the data frame when you reference it later. For example, in the line chart app that we did earlier in the course, we used a reactive function to download an indicator from the FRED database and store these data in an object called fred_indicator
. But when we wanted to filter those data later on in a second reactive function, we referred to them as fred_indicator()
.
6. Make sure that you are calling the input appropriately
Think about where your server function needs to use the input from the user. Then, when you are calling the input, make sure you are using the appropriate name for the input. For example, in our scatter plot app, we took input from the user for two selected indicators (input$xcol
and input$ycol
) and used these to make a scatter plot.
7. Consider nonstandard evaluation when using ggplot
Consider the fact that ggplot2
uses nonstandard evaluation and wrap the inputs in your aes()
call in get()
where appropriate. Nonstandard evaluation refers to the idea that ggplot2
takes the name of the column directly so that you don’t have to quote it. get()
transforms the text input from the user to the actual column name. For example, in our scatter plot app we used get()
to transform the user input to column names for our x and y variables: ggplot(dem_data, aes(x = get(input$xcol), y = get(input$ycol)))
.
8. In the server code, use print()
to debug
Sometimes it is hard to tell if your reactive functions are producing the output you want. One trick is to use print()
or glimpse()
in your output function to see what is going on. This should produce the output of the reactive function in your console. Here is an example from our line chart app:
# Define the server function
server <- function(input, output) {
# Download data from FRED with reactive function.
# Only updates when user selects new indicator
fred_indicator <- reactive({
fredr(series_id = input$indicator,
observation_start = start_date,
observation_end = end_date)
})
# Filter data according to selected years
# Only updates when user selects new date range
fred_data <- reactive({
fred_indicator() |>
filter(between(date, input$range[1], input$range[2]))
})
# Render line chart
output$lineChart <- renderPlot({
print(fred_data(), n = 100) #### USE print() TO VIEW OUTPUT IN CONSOLE
#glimpse(fred_data()) #### ALTERNATIVELY, USE glimpse()
# Build plot with ggplot2
ggplot(fred_data(), aes(x = date, y = value)) +
geom_line(color = "navyblue") +
labs(
x = "",
y = names(vars[which(vars == input$indicator)])
)+
theme_minimal() +
# add recession shading
add_rec_shade(st_date = input$range[1],
ed_date = input$range[2],
shade_color = "darkgrey",
y_min = min(fred_data()$value),
y_max = max(fred_data()$value))
})
}
9. Think about what your code is doing
One thing that I have emphasized throughout the course that is super-relevant here is to have a theory about what your code is doing. What is each part of the code supposed to do to the data? What is your objective with that chunk of code? And is it accomplishing what you hoped it would accomplish? Experimenting can help when you are stuck, but experimenting really only works when you have an idea of what your experiment is trying to test. This is especially true in the context of a Shiny app where it is hard to tell what is gong on “behind the scenes.”