Hour 24. Building Web Applications with Shiny


What You’ll Learn in This Hour:

Image The structure of a simple application

Image The basics of reactive programming

Image Creating interactive documents

Image Sharing Shiny applications


In this final hour, we are going to look at another of the tools that allows you to extend your R code, in particular giving you the ability to interactively share your analysis and results. Although you might initially be put off by the idea of building a web application, we are going to introduce a package that allows you to generate web applications entirely in R, writing only R code. This is currently one of the most popular packages available in R, with more and more packages being added to CRAN that use this framework.

A Simple Shiny Application

The package we are going to use to generate web applications is shiny. This package has been available through CRAN for almost three years, but its widespread usage has grown rapidly over the last year. One of the main reasons it has become so popular is that it makes the power of a web application available to R users without the need to learn HTML or JavaScript. In this section, we look at the basics of creating an application.

Structure of a Shiny Application

Before we get started with writing code, it is worth getting familiar with the components that make up a Shiny application. During development we need to think about two main components: First of all, the user interface. What will the application look like? How will components be arranged on the page? Second of all, we need to think about what is called “the server.” What will the application do? When an option is changed, what needs to happen?

It is possible—and for bigger applications, recommended—to build a Shiny application in two scripts named ui.R and server.R, but here we will work in just a single file. Throughout this hour, we are simply going to create a ui object and a server object that will be passed to the function shinyApp. We are going to contain all these components in a single script. If we save this script as app.R, we will obtain some shortcuts in RStudio that allow us to run the application at the click of a button.

You can see an example of how the file will look in RStudio in Figure 24.1. In the script app.R, you can see the overall structure of the script and the outlines for the ui and server components, which we will return to in the next sections, along with the call to the shinyApp function. Also, you can see the Run App button at the top of the script window. By selecting the drop-down menu, you can see the options available in this graphic. This controls whether the app is opened in a separate window, in the viewer pane, or in your default web browser. As you can see in the example in Figure 24.1, this particular application, which at this point is empty, will open in the viewer pane.

Image

FIGURE 24.1 Example of the app.R file in RStudio and the additional Run App options

Having seen the empty components of a Shiny application, we now need to think about what will go into them. We start off by looking at the user interface, which is controlled by the ui object.

The ui Component

As stated earlier, the ui object is where we define how our application is going to look. It is here that we specify the input components, the type of outputs, and how they will all be arranged.

As a very simple example, let’s consider an application that has a simple text input and uses it as the title for a histogram, which we will also output in the application. The code for this application can be seen in Listing 24.1.

LISTING 24.1 A Simple User Interface


 1: library(shiny)
 2:
 3: ui <- fluidPage(
 4:
 5:   textInput(inputId = "title", label = "Enter title text:"),
 6:
 7:   plotOutput(outputId = "histogram")
 8:
 9: )
10:
11: server <- function(input, output){}
12:
13: shinyApp(ui = ui, server = server)


You will notice that the only component we have changed here is the ui object. We will return to the server object in the next section. The ui object is created initially by a call to the function fluidPage. This is a function that controls the layout of the application. There are many more layout options that are beyond the scope of this hour.

Let’s consider the elements we have contained in our ui object. The first element we have provided is the textInput function, which creates a text input box in the application. This is one of many input functions that includes check boxes, numeric selectors and sliders, and drop-down selections, to name a few. All of these input functions follow the same structure, with the first two arguments always being the same, as shown in line 5 of Listing 24.1. The first argument is inputId. This is the name we are going to use to refer to this element in the code for our application. Each input object needs to have a unique name so that it can be identified, and you will see how this name is used in the next section. The next argument is label. This is a character string that appears in the user interface to tell the user what the purpose of the component is. If this is not included, the user won’t know what they are supposed to put into this text box or what it will do.


Note: Input Functions and Shiny Documentation

The shiny package is maintained by RStudio, who provides extensive documentation both in the shiny package and online. For more information on all the available input functions, as well as outputs and layouts, see the documentation available on the Shiny web pages at shiny.rstudio.com.


Before we consider the next component, notice that on line 5 of Listing 24.1 there is a comma to end the line. This is because we are about to provide another argument to the function fluidPage. Although it is easy to forget to include commas and brackets in the correct places, when we start creating the Shiny application, it does get much easier. A good indicator of a missed comma is the error message Unexpected symbol, although the latest versions of RStudio now includes in-editor error checking to help you identify a missed component a little easier.

The final component in the user interface object is an output function. In this case, we are returning a plot, so we are using the plotOutput function. Just as with inputs, there are a variety of output objects we can create, and the output function we will use depends on the object we are creating. Outputs can include text, tables, and images as well as HTML. Just like with the input object, we need to give an output object a name. Here, we have used the argument outputId to give a unique name to this component. You will see how this is used in the next section.

At this point we can run the application, but you will notice that the only thing you see is the text entry. Entering text will not do anything because we haven’t told the application what to do with that text. We don’t have a plot at the moment because we haven’t told the application to create one. We will do all of this with the server component.

The server Component

The server element of a Shiny application is the part that controls what the application does. In our simple example it would control what output is generated and what happens when we change the plot title. The server component is actually a function with two arguments: input and output. We must always use these exact arguments. You can see this in both Figure 24.1 and Listing 24.1, line 11. Inside the function we then create the output objects that will be rendered in the user interface. Let’s continue the example we started in the last section. Listing 24.2 shows the extended code with the server function now completed.

LISTING 24.2 Adding the server Function


 1: library(shiny)
 2:
 3: ui <- fluidPage(
 4:
 5:   textInput(inputId = "title", label = "Enter title text:"),
 6:
 7:   plotOutput(outputId = "histogram")
 8:
 9: )
10:
11: server <- function(input, output){
12:
13:    output$histogram <- renderPlot({
14:
15:      hist(rnorm(100), main = input$title, xlab = "Simulated Data")
16:
17:    })
18: }
19:
20: shinyApp(ui = ui, server = server)


You can see on line 13 that we have created an element in the output list called histogram. This is going to be an output object that is passed to an output function—in this case, it is being passed to the plotOutput function in the ui object. The name of the element that we create in the server needs to match the name we have given to the output function in the user interface so that the object will be displayed.

We create the objects themselves by using “render” functions. There is a corresponding render function for each output function we use in the user interface. Inside of the render function we put all of the code that we need to create the output object. In this case we have included a call to the function hist, which generates some random normal data to plot. Included in this function call is the reference to input$title. Here, we are asking Shiny to get the input object named title that we created in the user interface. Again, notice that the name matches the inputId element that we gave in the ui object (line 5 of Listing 24.2). This means that when the plot is created, it will take the value of the input$title element and pass it to the main argument of hist. As you will see when you run this application, whenever we change the title, the plot will update to have the new value of input$title.

This is now a complete application with inputs and reactive outputs. If you run this application, you will see something similar to Figure 24.2; note that the layout may be slightly different depending on window size. You will notice that as you change the text in the text input box, the application updates the graphic to include this new text.

Image

FIGURE 24.2 Complete application generated from code in Listing 24.2

Although this is just a simple application, we can extend the number of inputs and outputs to generate much more complex applications, with multiple inputs contributing to multiple outputs. One of the great advantages of shiny is that it is based entirely in R, so we have access to all the manipulation, visualization, and analysis tools we have seen throughout this book. All of them can be run from a Shiny application, with their outputs returned and updated as inputs change. An example of a more extensive application built on shiny is shown in Figure 24.3. This application has been extended further with the package shinydashboard and contains a number of pages that allow the user to interact with their data in different ways. To get to an application of this kind, we need to look at another concept that is going to help us a lot as we build bigger and more complex applications.

Image

FIGURE 24.3 Example of a more extensive Shiny application

Reactive Functions

You might have noticed when running the application in Listing 24.2 that every time you changed the title, the plot was regenerated, and this caused the data to be resampled. This was because both the simulation of the data and the updating of the plot were contained within the same “reactive” function, in this case a “render” function. Therefore, when the input element changed, shiny knew to update the plot, but this also re-simulated the data. If you look back at the code, you will notice that in line 15 we have both the rnorm function and the input$title object. We can actually change this behavior by working with multiple reactive functions.

Why Do We Need Reactive Functions?

Hopefully at this point you can see that a reactive function is useful. In this example, it may be undesirable to the end user that the data re-simulates just because we want to change the title. But suppose that the simulation was very large, or we wanted to read in a large dataset, or even perform a complex analysis before generating a graphic. We don’t want changing the title to be connected to re-running all of these components. Reactive functions therefore allow us to separate out each of the components of our application so that we can run the code as few times as possible.

When we start to develop larger applications, it is vital that we think about what is being run and how often. It’s so important, in fact, that we should start to practice this with small applications that we create. When it comes to a Shiny application, we want to run code as little as possible. For any application, you need to consider, how often do I want to run this section of code? Do I really want to run it each time I change any option?

It is important to be aware that any element that is contained within the input list (for example, input$title) is a reactive value. A reactive value must be contained inside a reactive function in a Shiny application. We didn’t mention this earlier, but the render functions are actually reactive functions, which is why they can appear here. There are, however, a number of other reactive functions that we can use to aid the development of our applications and to reduce how often code is run.

Creating a Simple Reactive Function

As mentioned earlier, all of the render functions are reactive functions, but it is often the case that there is some action we want to perform separately to generating the output. This could be reading in data, manipulating data, fitting a model, or all of these components. The simplest and most versatile function for performing these actions is the function reactive.

This function allows us to create a function that will only be called again when any of the inputs inside the function are changed. Consider the example we have been working with, but let’s add in an extra component that tells our application how many random normal values to simulate. Instead of putting this simulation inside the renderPlot function, we are going to contain it inside a reactive function. The code we would use to do this can be seen in Listing 24.3.

LISTING 24.3 Incorporating Reactive Functions


 1: library(shiny)
 2:
 3: ui <- fluidPage(
 4:
 5:   numericInput(inputId = "num", label = "Number of Simulations:", value = 100),
 6:
 7:   textInput(inputId = "title", label = "Enter title text:"),
 8:
 9:   plotOutput(outputId = "histogram")
10:
11: )
12:
13: server <- function(input, output){
14:
15:    data <- reactive(rnorm(input$num))
16:
17:    output$histogram <- renderPlot({
18:
19:      hist(data(), main = input$title, xlab = "Simulated Data")
20:
21:    })
22: }
23:
24: shinyApp(ui = ui, server = server)


The main thing to notice here is how we have incorporated the reactive function. You will notice on line 15 that we have created an object called data. This is in fact a function object that we will call later. We have then included the call to rnorm inside the reactive function. At the point that we want to use this data, in the call to hist on line 19, we now call this data function. The difference now is that the data function will only regenerate the simulated data when the input$num value changes, rather than each time the hist function is called. You will be able to see this behavior if you run this code and try changing both the numeric value and the title.

You can have as many reactive functions as you want in an application, and you can even have nested reactive functions. For instance, you may have a renderPlot function that plots the output from a model. This renderPlot may call a reactive function that fits the model, which in turn calls a reactive function that reads in or simulates your data. In addition, some other reactive functions are available in the shiny package that will handle reactive values differently. For more information, take a look at the help files for the functions isolate, observeEvent, and eventReactive.

Interactive Documents

In the last hour, you saw how to create dynamic documents that allow you to generate a report or even a presentation entirely from R, mixing both the document content and the R code. Here, you have now seen another means of sharing analysis, in the form of Shiny applications. However, we can in fact combine the two. We are able to create a document that includes Shiny components. We can quickly open a template document of the correct format from RStudio using the New R Markdown menu you saw in Hour 23, “Dyanmic Reporting.” Instead of selecting the Document option, we can instead choose Shiny. You will notice that this gives you the additional “runtime” option in the document header.

We include Shiny components inside an R code chunk in the same way we would include any other code. When it comes to the Shiny component, we include inputs in exactly the same way. We can use an inputPanel function to group together all of the inputs. In a Shiny document, we don’t need to include the usual output functions; we simple include the render functions that would usually be in the server function. So if we wanted to include the same inputs and outputs as we have seen in this hour, but inside a markdown document, our code chunk would look like the following:

```{r, echo=FALSE}
inputPanel(
  numericInput(inputId = "num", label = "Number of Simulations:", value = 100),

  textInput(inputId = "title", label = "Enter title text:")
)

data <- reactive(rnorm(input$num))

renderPlot({

    hist(data(), main = input$title, xlab = "Simulated Data")

    })
```

An example of how this would render in the document can be seen in Figure 24.4. The important thing to note is that this is no longer a static file. Because we have a Shiny element in the document, we need to have an R session available to run it. You will notice that the options in RStudio are no longer “Knit,” but “Run Document.”

Image

FIGURE 24.4 A Shiny element inside a reactive document

If you have an existing Shiny application and you simply want to embed it into a document, you can do so by using the shinyAppDir function, pointing to the location of your Shiny application and setting elements such as the width and height of the application in the document.

Sharing Shiny Applications

Creating a Shiny application that will allow you to share your work is very simple and flexible, but an important thing to consider is how you are going to share your application. Up to this point you have probably just run your examples on your own machine with your own version of R. If you want to allow others to work with you and run analysis or investigate outcomes, you need to be able to provide your application. You can of course simply send the files to other users or incorporate the application into an R package, but this requires the users to have R installed and all of the correct packages. However, often the reason you want to create a Shiny application is to share what you are doing with non-R users.

The best way to share your application in this case is to have your application hosted on a server that allows you to send a single URL to the end users. There are a couple of ways you can do this. First of all, you can have your application hosted by RStudio with shinyapps.io. This is a service for which you can sign up, with one of a range of packages that allows you to have RStudio host your application for you. Alternatively, you can host your application on your own server using Shiny Server. There is both a free and a pro version available, with the pro version adding features such as authentication. Much more information about all of these services is available from the RStudio website, which will allow you to determine the best approach for you.

Summary

In this final hour, we introduced the basics of the package shiny, giving you enough tools to get started. There is much, much more that you can do with a Shiny application that is beyond the scope of this hour. The shiny package is maintained by RStudio, which offers extensive material describing some of the features not covered here, including controlling the layout of an application, many more of the input and output options, how you can work with data, and how you can customize your application with CSS components, just to name a few. Everything you have seen in this book has given you the foundations to go on and learn more about what can be done with R and understand for yourself the corresponding documentation. In the final two hours, we have introduced just two of the popular means of sharing R with non-R users, but what can be achieved goes far beyond our coverage. Hopefully this has given you a taste of what is possible so that you can jump in and try it out for yourself.

Q&A

Q. Can I open my Shiny application in my web browser?

A. Yes, you can. You can do this by changing the default option in the Run App drop-down menu, or you can use the Open in New Window button in the viewer window/pane.

Q. Why can’t I run any other code while my Shiny app is running?

A. While you are running your Shiny application, your R session will be blocked. This is because while the app is active, R code is being run and re-run. Because you can’t run multiple processes at the same time in R, you cannot run any other code while you run your Shiny application.

Workshop

The workshop contains quiz questions and exercises to help you solidify your understanding of the material covered. Try to answer all questions before looking at the “Answers” section that follows.

Quiz

1. Which component controls what the application will look like?

2. What two arguments do you need to give to all input functions?

3. There are two arguments you must give to a server function. Which of the following options is not required?

A. input

B. output

C. session

4. What are the main benefits of using a reactive function?

Answers

1. The ui component controls how the application will look to the end user.

2. All of the input functions, whether text, a number, or a drop-down menu, start with the arguments inputId and label. The argument inputId is used by the application to reference the objects, whereas the label argument is used in the user interface to tell the user the purpose of the element.

3. Both the input and output arguments are required arguments to the server function that you need to give in exactly this format. You can optionally use the session argument to pass session information to the Shiny application server function.

4. One of the main benefits of using a reactive function is that you can break up the running of the application. Rather than tasks being re-run when they do not need to be, you can use reactive functions to ensure that they are only re-run when an input option is changed.

Activities

1. Create an application that takes three inputs:

Image A numeric slider of values between 1 and 500

Image A drop-down menu to select color values

Image A text string to give the plot title

2. Update the application to return a histogram of simulated values using all of the preceding options.

3. Extend the application to include a check box that adds a vertical reference line at the median value of the data.

4. Ensure that the data is not re-simulated each time an option is changed.

5. Use the available documentation to update the layout of the application to ensure that all the inputs are in a column on the left and outputs in a column to the right.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset