Data Visualization: Reactive Functions in Shiny

We now know that Shiny for R is a powerful tool for data scientists to display their work quickly and easily to a broad audience, so let's get to some nitty gritty about what it takes to create Shiny visualizations.  We're not going to get into syntax (unless I want to scare everyone off), let's focus on its basic structures and why it comes naturally to those of us who're not web programmers by trade.

All Shiny applications have two basic functions: ui.R and server.R, and it is the relationship between these two functions that determines the functions you use.

Static Pages

A static page is the most basic architecture to start with and can be written using only these basic functions:

  • ui.R
    • shinyUI()
    • pageWithSidebar()
    • sidebarPanel()
    • mainPanel()
  • server.R
    • shinyServer()
    • output$variable <- reactive()
    • output$image <- reactivePlot()

All we've set up is a control panel on the left-hand-side and the plot outputs on the right-hand-side.  You can prompt the user to upload a file with "fileInput()", used within "sidebarPanel()", but to begin with it may be easier to use a file you're familiar with and wont throw you any curve balls.

You can simply display the table of values given using "tableOutput()", but the goal here is to use the side panel to explore your data, and visualizing the table is much more effective than displaying it.  You can prompt the user for basic yes/no information through "radioButtons()", you can ask them to choose specific columns with "selectInput()", or the user can select multiple values using "checkboxGroupInput()".

If you're not a web programmer by trade, and especially if you're primarily used to linear programming, it is important to note that functions in ui.R are always linked to something in server.R by the "input" and "output" variables, literally.  Shiny has reserved these two variables for passing information to server.R, "input", and passing processing information back to ui.R, "output".  For whatever reason, possibly because there are so many different variable names, server.R uses the full reference "input$inVariable", but ui.R only needs the "outVariable" portion of "output$outVariable".  So for example, you may create a plot in server.R:

output$imagePlot <- reactivePlot(function() { code code }

But ui.R only uses:

div(class="span6", plotOutput("imagePlot")),

I threw the div() function in there just to show the html relationship.

Dynamic Pages

There are a couple ways in which we can make the pages more dynamic, we have already given the user control over what data is used in the output graphs, but we can also dynamically choose what options we present to the user.  Based on initial choices (such as type of data), we can change the range on control functions (such as slider bars), and we can change the type of graph that's produced based on the input data and configuration.

To have the configuration panel change we use:

output$variable <- reactiveUI( function() { code code }

Shiny will keep track of that output variable and if it is to be used in ui.R, this reactiveUI function will be called first; I have seen race conditions so don't have too many functions feeding back on themselves.  To create a slide-bar, to say limit values of a joint PDF, place the "sliderInput()" function within reactiveUI:

output$slideRange <- reactiveUI( function() { code ; code ; sliderInput()}

Then render the output in ui.R with this simple call within sidebarPanel():

uiOutput("slideRange")

The same approach is used to change radio buttons based on the header within an uploaded file, or to add completely new sections to sidebarPanel().  Creating dynamic chart types is easy, just include conditional statements in server.R based on the "input" variables.  So if we're working with a fully-populated dataset create nice histograms or a heat map, but if it's sparsely populated create a force-graph or social-graph.  There must be a corresponding function in ui.R for each chart however, so you are limited by how you set up the page to begin with; your three charts may change their look, but there will always be three.

If many graphs depend on a single set of calculations, it may be prudent to use "reactiveValues()" coupled with the "observe()" function so you don't have to call the same function multiple times.  Shiny will keep track of what's been changed and what hasen't, so all you have to do is call the variable and Shiny will make sure it's up to date.  When you're operating on a large data set, this is essential for a real-time interface.  If we were displaying the psychological results of the Three Stooges, we might create a reactive variable like the following that our reactiveUI() functions would reference:

v <- reactiveValues( Names = c("Larry","Moe","Curly") , sane = NULL) observe(function() { #Calculations on v$Names and v$sane }

As a note for posterity, though we haven't listed many functions, a good data viz is simple and leads the user by your design.  Use the proverbial "eye chart" approach and you'll be lucky if the person dives in at all, too little and the user doesn't 'play' with the viz and doesn't explore their own data.  This can be likened to gamification, although I'm not advocating Farmville for data science; I remember reading as a kid that the original Super Mario Bros. for Nintendo was designed to be difficult yet allow me to win enough that I couldn't put it down.  Although I'm not sure who Bowser's equivalent is in your data, if you're looking to make an impression on your user remember that I still know where all the secret warp levels are.