• Illumined Insights
  • Posts
  • Shiny Apps in R: An Introduction to Building Interactive Data Visualizations

Shiny Apps in R: An Introduction to Building Interactive Data Visualizations

Part 3: Enhancing Your Shiny App

Welcome to the “Illumined Insights” newsletter! Thank you so much for subscribing. This weekly newsletter touches on all things analytics and data science with a focus on areas such as data visualization and sports analytics.

We’re back after a short hiatus. I’ll fill you in on the details later, but I’ve accepted a faculty position at a new university and have been spending the last couple of months preparing for the move. With that out of the way, we should be back on a somewhat regular newsletter schedule.

This week we move to Part 3 of our series on creating a Shiny application in R. If you missed Part 1, check it out here: Part 1. If you missed Part 2, check it out here: Part 2.

Stephen Hill, Ph.D.

In Parts 1 and 2 of this short series on building a Shiny app in R we built a simple R Shiny application to explore the Palmer penguins dataset from the “palmerpenguins” R package. We then demonstrated how to publish your Shiny app online via Shinyapps.io.

This week we enhance our Shiny app by adding reactivity and a button to control user input. Let’s start by looking at how to add a button and how a button works. Before we do that, we need to think about how R works from a “reactive” standpoint. Typically, a Shiny app will update as a user input is provided. For example, if we change the Penguin species in our Shiny app, we immediately see a corresponding change in the output (i.e., our plot updates to show the newly selected species). This seems fine. However, there are some apps where this behavior is undesirable.

For example, in a Shiny app input we might be asked to type in a name. The problem here is that Shiny will update the app after each letter is typed. This may be OK if the updating process is quick. But what if Shiny has to perform difficult calculations that we will require a non-trivial amount of computation time? In the example of typing a name as an input, we’d much rather Shiny wait until we’re done typing the whole name before it performs its calculations.

So, how do we stop Shiny from having to perform all of these repeated calculations that slow our app down? The key is the use of “reactive expressions”. Reactive elements in Shiny will cache their data and only change when they become aware that their input data has changed. Using a button then allows us to precisely control the timing of when the changed inputs are recognized. We can change multiple inputs without the corresponding output(s) changing. Only when the button is pressed is the output then changed.

Let’s look at the “ui” code for our Shiny app with a button incorporated (see below). The key is the “actionButton” function.

ui <- fluidPage(
  titlePanel("Palmer Penguins Explorer"),
  sidebarLayout(
    sidebarPanel(
      selectInput("species", label = "Penguin species:",
                  choices = c("All", "Adelie", "Gentoo", "Chinstrap")),
      selectInput("x_var", label = "X-axis variable:",
                  choices = names(penguins)[4:7]),
      selectInput("y_var", label = "Y-axis variable:",
                  choices = names(penguins)[4:7]),
      sliderInput("size", label = "Point size:",
                  min = 1, max = 10, value = 5),
      actionButton("plot_button", label = "Update Plot")
    ),
    mainPanel(
      plotOutput("penguin_plot")
    )
  )
)

Next is the “server” code (below). We’ve added some complexity by incorporating reactive expressions. We embed an “if” statement inside a reactive expression that watches to see if the selected species input is changed. We also use a reactive expression for the plot. We store the plot data (i.e., the displayed output) in a reactive object. The “observeEvent” function is then used to wait for and then observe the user clicking of the action button. When this click is observed, the plot is generated and displayed.

server <- function(input, output) {
  
  # Reactive expression for filtered data
  filtered_data <- reactive({
    if (input$species == "All") {
      penguins
    } else {
      penguins %>%
        filter(species == input$species)
    }
  })
  
  # Reactive expression for the ggplot object
  plot_data <- reactive({
    ggplot(filtered_data(), aes_string(x = input$x_var, y = input$y_var)) +
      geom_point(alpha = 0.5, size = input$size, color = "steelblue") +
      ggtitle(paste(input$x_var, "vs.", input$y_var, "for", input$species)) +
      theme_bw() +
      theme(plot.title = element_text(hjust = 0.5))
  })
  
  # Reactive values object to store the plot data
  plot_rv <- reactiveValues(plot = NULL)
  
  # Observe button click 
  observeEvent(input$plot_button, {
    plot_rv$plot <- plot_data()
  })
  
  # Render the plot stored in the reactive values object
  output$penguin_plot <- renderPlot({
    req(plot_rv$plot) # Ensure plot exists before rendering
    plot_rv$plot
  })
}

shinyApp(ui, server)

When we run this app (locally or after being deployed to shinyapps.io) we get an app that looks like this (below). The button is the most notable new feature. Also notice that no plot was rendered. The app is waiting to observe the button click before a plot will be generated. Let’s change a few inputs and then click the “Update Plot” button and see what happens (second image below). Notice that the button click resulted in the generation of a plot from the specified inputs.

Updated Shiny app

Shiny app output after input change and button click

This brings our series on building a basic Shiny app to a close. We have barely scratched the surface of Shiny’s capabilities. Where do we go from here if we want to learn more? I would start with excellent Shiny tutorial series provided by Posit: here. For additional depth, check out “Mastering Shiny”: here.

Are you interested in learning more about data visualization using R? Click below to get notified about my upcoming book “Data Visualization in R”.

Each week we’ll feature a dataset that we find interesting, useful, etc. This week we look at Zillow’s set of datasets. I recently went through the home selling and buying process and Zillow was an invaluable tool. Explore Zillow’s wealth of data and practice data visualization, data cleaning, etc.

Feedback?

Did you enjoy this week’s newsletter? Do you have a topic, tool, or technique that you would like to see featured in a future edition? I’d love to hear from you!

Support the Newsletter?

Support this newsletter with a “coffee” (optional, but appreciated).

Start Your Own Newsletter?

This newsletter is created on and distributed via Beehiiv, the world’s best newsletter platform. Want to start your own newsletter? Click below to get started. Please note that this is an affiliate link. I may receive a small commission if you sign up for Beehiiv via this link.