Introduction

With the rise of widely available high-speed internet connections, web applications have become more an more popular. However, to create a web app, a lot of programming experience in HTML and JavaScript was needed. Especially for people who’s main job is not to be a programmer this presented a hurdle in creating interesting web applications.

In this void, RStudio released it’s product called Shiny. Shiny is a means of creating web applications entirely in R. The client-server communication, HTML, layout and JavaScript programming is entirely handled by Shiny. This makes creating web applications feasible for data analysts who are not necessarly experienced web-developers.

Basic structure of web applications

The structure of a web-application in generally takes the following form:

The person working with the web app opens a web browser, and points this to the URL of the web application (e.g. www.example.org/web_app). This triggers a reaction from the server, sending the HTML, CSS and JavaScript code needed to run the web application back to the client. The application then runs itself inside the web browser of the client.

A typical web application consist of a number of user interface (UI) elements, say a button or checkbox. Each of these elements can be interacted with, for example push the button. This button push then triggers an action, running a piece of code. This can then change the state of the web application, for example retrieve a piece of information from the server or draw a picture in the application. This style of programming is called event-driven programming. The piece of code that is executed based on an event is called an event handler.

The structure of Shiny apps

Shiny apps follow this typical structure of web applications. However, as a user you only have to specify which UI elements you want to show, and the underlying R code that draws a plot, shows some text, or a table. These pieces of information are stored in two files:

Building the HTML, CSS and JavaScript code is done from these two R files, as is generating all the event handlers. In addition, when you update a UI element, all the relevant content code (e.g. a plot) is updated automatically. So, building a full web application can purely be done using R.

Building the user interface

The structure of the user interface is specified in the ui.R file. The most basic example is:

shinyUI(fluidPage(
  titlePanel("title panel"),

  sidebarLayout(
    sidebarPanel( "sidebar panel"),
    mainPanel("main panel")
  )
))
image

image

Which shows a three panel layout: a title panel at the top, a sidebar panel to the side, and a main panel to the left:

To expand this UI you can add one or more UI elements to the sidebarPanel or the mainPanel. For example

shinyUI(fluidPage(
    titlePanel("title panel"),
    sidebarLayout(
      sidebarPanel( "sidebar panel",
                    selectInput('element_id', label = 'Select one option', choices = LETTERS[1:10])),
      mainPanel("main panel", 
                h1('The title of some text'), 
                p('And here is some content that is put into the first paragraph'))
    )
))

adds a dropdown box of options to the sidebar panel using selectInput, and some text using h1 for headers and p for normal text in paragraphs. This closely mimics the corresponding HTML tags <H1> and <p>. To add more elements, simply add more as input to sidebarPanel.

A full list of all the UI element options are simply too much to report here, and I refer to the Shiny reference pages for all the details.

Integrating dynamic content

Up to now, the user interface does not contain dynamic content: the option selection does nothing and the text is static. We can change this using the server.R file. For example, if we want to print a sentence in the main panel that says: ‘You selected ’ we can add the following server.R:

shinyUI(fluidPage(
    titlePanel("title panel"),
    sidebarLayout(
      sidebarPanel( "sidebar panel",
                    selectInput('element_id', label = 'Select one option', choices = LETTERS[1:10]),
                    textInput('title_text_box_id', label = 'Enter a title for the plot')),
      mainPanel("main panel", 
                h1('The title of some text'), 
                p('And here is some content that is put into the first paragraph'),
                p(textOutput('dynamicText'))
    )
))
shinyServer(function(input, output) {
  output$dynamicText <- renderText({
    sprintf('You selected %s', input$element_id)
  })
})

where we see a dynamic piece of text that is built based on input$element_id. Note that this is the ID we chose for the selectInput in ui.R. This ID is the way that Shiny couples the UI elements to the dynamic content in server.R. The input variable to shinyServer contains all the UI elements, the output variable returns the dynamic content to the UI. Also note the add p(textOutput('dynamicText')) that puts the dynamic element in the UI.

It this case, we generate dynamic text using the renderText function. Another example is renderPlot, which can be used to create dynamic plots. For a full list, I refer to the Shiny reference pages. An example using renderPlot uses the following server.R and ui.R file:

shinyUI(fluidPage(
    titlePanel("title panel"),
    sidebarLayout(
      sidebarPanel( "sidebar panel",
                    selectInput('element_id', label = 'Select one option', choices = LETTERS[1:10]),
                    textInput('title_text_box_id', label = 'Enter a title for the plot')),
      mainPanel("main panel", 
                h1('The title of some text'), 
                p('And here is some content that is put into the first paragraph'),
                p(textOutput('dynamicText')),
                plotOutput('dynamicPlot'))
    )
))
library(ggplot2)

shinyServer(function(input, output) {
  output$dynamicText <- renderText({
    sprintf('You selected %s', input$element_id)
  })
  output$dynamicPlot <- renderPlot({
    ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point() + ggtitle(input$title_text_box_id)
  })
})

Note that I added a plotOutput UI element in the main panel. This refers to the dynamicPlot in server.R, which in turn contains the ggplot2 code that draws the plot. Notices that the ggtitle function there uses input$title_text_box_id as input. So, the content of the ‘Enter a title for the plot’ text box is used as the title for the plot through ggtitle. Note that Shiny automatically detects that the content of the text box has changed, and that the plot should be updated.

Also note the inclusion of library(ggplot2) at the top of the script. This is needed to be able to draw ggplot2 plots. Any R package needed needs to be loaded like this.

Important note on code execution

The location of code in server.R determines how often it is run, and thus impacts the performance of the app. There are three zones where code is run more or less often:

# Zone 1
library(ggplot2)

shinyServer(function(input, output) {
  # Zone 2
  output$dynamicPlot <- renderPlot({
    # Zone 3
  })
})

If you put your code in the wrong zone, the app’s performance can be seriously impacted. For example, if reading a large static dataset is put into Zone 3, this large dataset will be read each time the plot needs to be redrawn.

Reactive expressions

Normally, Shiny automatically reruns the code in the different zones given above. But consider the following example:

# Zone 1
library(ggplot2)

shinyServer(function(input, output) {
  # Zone 2
  output$dynamicPlot <- renderPlot({
    dat = get_data(input$ui_element1, input$ui_element2)
    plot(dat, input$ui_element3)
    # Zone 3
  })
})

The get_data function is called each time any of the UI elements is updated. However, if only ui_element3 is updated the data would not have to be reloaded. This is where reactive expressions come in:

# Zone 1
library(ggplot2)

shinyServer(function(input, output) {
  # Zone 2
  input_data = reactive({
    dat = get_data(input$ui_element1, input$ui_element2)
  })
  output$dynamicPlot <- renderPlot({
    plot(input_data(), input$ui_element3)
    # Zone 3
  })
})

we wrap the data loading in a reactive block, and in the plotting refer to input_data(). The advantage of this is that for a reactive expression Shiny first checks if the data actually needs to be updated or not. If only ui_element3 is updated, the reactive simply returns the previously stored data (i.e. caching), and the plot is redrawn without the costly step of reading the data.

Embedding Shiny in existing applications

The workflow described above builds a totally self contained web application using R. However, Shiny can also be embedded within existing web applications, for example adding R plotting capabilities. This is however a lot more involved than building Shiny apps like presented above. Therefore, this outside the scope of this tutorial. For advanced Shiny topics, I refer to the set of articles on the Shiny website.

Excercises

  1. Open a new Shiny app in RStudio (File > New project > New directory > Shiny Web Application), which is essentially a new project in itself. Then edit the ui.R and server.R to the final example given above, and play around with the app. Note that you can run the app using Run app button at the top right of your code panel.

In the next couple of excercises you will build a visualisation app for the mtcars dataset.

  1. Using the standard sidebar layout, create an app that has two dropdown menus in the sidebar and a ggplot2 plot in the main panel. The two dropdown menus select which variables from mtcars will be plotted on the x and y axis. Tip: have a look at aes_string from ggplot2.

  2. Add a checkbox which enables the user to choose whether or not to include a stat_smooth.

  3. Add a checkbox and a dropdown menu that allow the user to toggle the use of facetting and select which variable to facet with.

In the next couple of excercises we will build a stock price visualisation app (inspired by Shiny tutorial):

  1. We will download the stock price data using getSymbols form the quantmod package. Ensure that auto.assign is set to FALSE, and your can use the from and to input arguments to select time. Extract stockprice data for AAPL (Apple), YHOO (Yahoo), and GS (Goldman Sachs).

  2. Create a chart of that data by calling the candleChart function from quantmod on the resulting object from getSymbols. Change the theme from black to white and inspect the differences.

  3. Create a new Shiny app, and create a ui.R that results in this user interface:

    The list of possible stock symbols is AAPL (Apple), YHOO (Yahoo), and GS (Goldman Sachs). The themes that can be selected is white and black. Select appropriate defaults for the data range.

  4. Create a server.R that loads the data for the selected stock symbol, and the selected data range using getSymbols and plots it using candleChart.

  5. Ensure that you have used a reactive expression for reading the stock data. Ensure that the theme of the graph can be changed without reloading the data.