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:

# 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)

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:

Note

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.”