Read-me-first Shiny tutorial III: debugging with print statements

Now that we’ve examined Shiny buzzers and butlers, let’s look at how we might debug a Shiny app that won’t run right. Again, we’ll start with the code Shiny currently autoloads when you create a new Shiny Web App in RStudio:

library(shiny)

# Define UI for application that draws a histogram
ui <- fluidPage(
 
   # Application title
   titlePanel("Old Faithful Geyser Data"),
 
   # Sidebar with a slider input for number of bins 
   sidebarLayout(
      sidebarPanel(
         sliderInput("bins",
                     "Number of bins:",
                     min = 1,
                     max = 50,
                     value = 30)
      ),
 
      # Show a plot of the generated distribution
      mainPanel(
         plotOutput("distPlot")
      )
   )
)

# Define server logic required to draw a histogram
server <- function(input, output) {
 
   output$distPlot <- renderPlot({
      # generate bins based on input$bins from ui.R
      x <- faithful[, 2] 
      bins <- seq(min(x), max(x), length.out = input$bins + 1)
 
      # draw the histogram with the specified number of bins
      hist(x, breaks = bins, col = 'darkgray', border = 'white')
   })
}

# Run the application 
shinyApp(ui = ui, server = server)

First we’ll add another input to this app. It will be a text input that will allow us to provide a title for the plot. To begin, take a look at the line in the code above that begins with the sidebarPanel() function. If you look up this function on RStudio’s help tab, you find the definition:

sidebarPanel(..., width = 4)

When you see the three dots in an R function definition, it means “one or more other parameters.” (Leave the width parameter as a curiosity for now.) In the sample code above, sidebarPanel() has one parameter, which is a sliderInput() function. We’re going to add another parameter after that one, so begin with a comma after the first parameter and add the new parameter, textInput(“title”, “Title for plot”), on the next line. It should look like this:

                     value = 30),
         textInput("title", "Title for plot")

In the textInput() function, the first parameter, “title,” is an id and the second, “Title for plot,” is a label. You’ll see how it works in a minute here. First though, we also have to make two tiny changes to the server section of the code. We’re going to add another parameter to the hist() function, which draws histograms. This involves another comma, plus main = input$title inside the hist() definition. The other change is to add print(“Running renderPlot…”) on the next line. That should look like this:

      hist(x, breaks = bins, col = 'darkgray', 
           border = 'white', main = input$title)
      print("Running renderPlot...")

Note that there’s no comma after the hist() function; this is a code block, inside a renderPlot() function. You can tell because the parameter is surrounded by { curly braces }, which is how R knows that a function’s parameter is a block of code.

Now click RStudio’s Run App button and take a look at what those changes have wrought. In the browser you should see a new text input. When you type in it, two things happen: what you type appears as the plot title in the web window, and in RStudio’s console you’ll see “Running renderPlot…” Give it a go.

Each time you enter a character, Shiny turns on the buzzer that wakes up the renderPlot() butler and the plot is redrawn. Even with this simple example, it’s easy to type faster than renderPlot() can keep up with. Shiny buzzers can only be on or off, so the buzzes don’t stack up. You won’t see a one-to-one correspondence between quickly-typed characters and the “Running renderPlot…” line, but if you type slowly you’ll be able to see in the console that renderPlot() runs continuously while you type.

There are several ways that it can be helpful to use the print() function in your code. For example, if you need to know the value of a variable, print it. If you need to know if a function ran, put a print() inside the function. When you’re running the app on your own computer, the messages appear in the RStudio console; if your app is running on a server they appear in the server log. This is a very simple, very basic debugging tool that I’ve found surprisingly useful.

Now, speaking of bugs, let’s put some in our app and see what happens. Let’s start by removing that first comma we added in the ui definition. It’s at the end of the line before our new textInput(). Remove it and click Run App. Impressive, no?

You get an unhelpful error message in the web browser. Now look at the RStudio console. You’ll see a much more helpful multi-line message there. If you examine the message carefully, you’ll find the name of the source file and the line number where things went bad. Although this error message includes “Possible missing comma at”, another error message for a missing comma that you’ll often see, at least if you forget commas as much is I do, is the much less helpful “unexpected symbol,” which you’ll also see here.

Now put that comma back, but add one after the hist() function in the server code, right before the print() statement we added. Now notice that after you move the cursor to a different line, RStudio itself will put red X circle in front of the line number for that line. If you hover the cursor over the red X circle, RStudio will even attempt to tell you what the error is.

Checking for the red X circle lets you debug before you even run the app. It’s not perfect; sometimes the circles can be slow to appear and disappear. If you have one that won’t go away after you’ve fixed a line, try adding and removing a space from the line.

RStudio’s red X circles and adding print()s to Shiny code are the two easiest debugging tools you’ll have. RStudio includes much more powerful tools for debugging R code, but I get pretty far just with these two very simple tools. Next we’re going to take a deeper look at the ui section of Shiny apps.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.