19
Building Interactive Web Applications with Shiny

Adding interactivity to a data report is a highly effective way of communicating information and enabling users to explore a data set. This chapter describes the Shiny1 framework for building interactive applications using R. This will allow you to create dynamic systems in which users can choose what information they want to see, and how they want to see it.

1Shiny: http://shiny.rstudio.com

Shiny provides a structure for communicating between a user interface (i.e., a web browser) and a data server (i.e., an R session), allowing users to interactively change the “code” that is run and the data that are output. This not only enables developers to create interactive data presentations, but provides a way for users to interact directly with an R session (without requiring them to write any code).

19.1 The Shiny Framework

Shiny is a web application framework for R. As opposed to a simple (static) webpage as you would create with R Markdown, a web application is an interactive, dynamic webpage—the user can click on buttons, check boxes, or input text to change how and what data is presented. Shiny is a framework in that it provides the “code” for producing and enabling this interaction, while you as the developer “fill in the blanks” by providing variables or functions that the provided code will use to create the interactive page.

Sharing data with others requires your code to perform two different tasks: it needs to process and analyze information, and then present that information for the user to see. Moreover, with an interactive application, the user is able to interact with the presented data (e.g., click on a button or enter a search term into a form). That user input then needs to be used to re-process the information, and then re-present the output results.

The Shiny framework provides a structure for applications to perform this exchange: it enables you to write R functions that are able to output (serve) results to a web browser, as well as an interface for showing those outputs in the browser. Users can interact with this interface to send information to the server, which will then output new content for the user. Passing these inputs and outputs back and forth (as illustrated in Figure 19.1) allows Shiny to provide a dynamic and interactive user experience!

A figure shows the content communication between a browser and an R session. The interface passes user input to R and R passes R output to the browser.
Figure 19.1 Passing content between an R session and a web browser.

Fun Fact

Because Shiny is rendering a user interface for a web browser, it actually generates a website. That is, the framework will create all of the necessary components (HTML elements), their styles (CSS rules), and the scripts (JavaScript code) to enable interactivity. But don’t worry: you don’t need to know anything about these languages; Shiny code is written entirely in R. However, if you already know a few things about web development, you can augment the Shiny-generated elements and interactivity to really make your application shine.

19.1.1 Shiny Core Concepts

The Shiny framework involves a number of different components; you will need to be familiar with and distinguish between these terms to understand how to implement Shiny apps.

  • User interface (UI): The UI of a Shiny app defines how the application is displayed in the browser. The UI provides a webpage that renders R content such as text or graphics (just like a knitted R Markdown document). Moreover, a Shiny UI supports interactivity through control widgets, which are interactive controls for the application (think: buttons or sliders). The UI can specify a layout for these components, allowing you to organize your content in side-by-side panels, or across multiple tabs.

  • Server: The server of a Shiny app defines and processes the data that will be displayed by the UI. Generally speaking, a server is a program running on a computer (often remotely) that receives requests and provides (“serves”) content based on the request. For example, when you request information from a web API, you submit a request to a server that processes the request and returns the desired information. In a Shiny application, you can think of the server as an interactive R session that the user will use to “run” data processing functions by interacting with the UI in the web browser (not in RStudio). The server takes in inputs from the user (based on their interactions) and runs functions that provide outputs (e.g., text or charts) for the UI to display. These data processing functions are reactive, which means they are automatically rerun whenever the input changes (they “react” to it). This allows the output to be dynamic and interactive.

  • Control widget: An element in the UI that allows the user to provide input to the server—for example, a text input box, a dropdown menu, or a slider. Control widgets store input values, which are automatically updated as the user interacts with the widget. Updates to the value stored by the widget are sent from the UI to the server, which will react to those changes to generate new content to display.

  • Reactive output: An element in the UI that displays dynamic (changing) content produced by the server—for example, a chart that dynamically updates when the user selects different data to display, or a table that responds to a search query. A reactive output will automatically update whenever the server sends it a new value to display.

  • Render function: Functions in the server that produce output that can be understood and displayed by the UI’s reactive outputs. A render function will automatically “re-execute” whenever a related control widget changes, producing an updated value that will be read and displayed by a reactive output.

  • Reactivity: Shiny apps are designed around reactivity: updating some components in the UI (e.g., the control widgets) will cause other components (e.g., the render functions in the server) to “react” to that change and automatically re-execute. This is similar to how equations in a spreadsheet program like Microsoft Excel work: when you change the value in one cell, any others that reference it “react” and change as well.

19.1.2 Application Structure

A Shiny application is written in a script file named app.R (it must to have that exact name so that RStudio will handle the file correctly). This file should be saved in the root directory of a project (i.e., the root of a git repository). You can create this file and folder yourself, or alternatively you can create a new Shiny project through the RStudio interface (via File > New File > Shiny Web App…).

Shiny is made available through the shiny package—another external package (like dplyr and ggplot2) that you will need to install and load before you can use it:

install.packages("shiny") # once per machine
library("shiny")          # in each relevant script

This will make all of the framework functions and variables you will need to work with available.

Mirroring Figure 19.1, Shiny applications are separated into two components (parts of the application): the UI and the server.

  1. The UI defines how the application is displayed in the browser. The UI for a Shiny application is defined as a value, almost always one returned from calling one of Shiny’s layout functions.

    The following example UI defines a fluidPage() (where the content will “fluidly” flow down the page based on the browser size) that contains three content elements: static text content for the page heading, a text input box where the user can type a name, and the output text of a calculated message value (which is defined by the server). These functions and their usage are described in more detail in Section 19.2.

    # The UI is the result of calling the `fluidPage()` layout function
    my_ui <- fluidPage(
      # A static content element: a 2nd level header that displays text
      h2("Greetings from Shiny"),
    
      # A widget: a text input box (save input in the `username` key)
      textInput(inputId = "username", label = "What is your name?"),
    
      # An output element: a text output (for the `message` key)
      textOutput(outputId = "message")
    )
  2. The server defines and processes the data that will be displayed by the UI. The server for a Shiny application is defined as a function (in contrast, the UI is a value). This function needs to take in two lists as arguments, conventionally called input and output. The values in the input list are received from the user interface (e.g., web browser), and are used to create content (e.g., calculate information or make graphics). This content is then saved in the output list so that it can be sent back to the UI to be rendered in the browser. The server uses render functions to assign these values to output so that the content will automatically be recalculated whenever the input list changes. For example:

    # The server is a function that takes `input` and `output` arguments
    my_server <- function(input, output) {
      # Assign a value to the `message` key in the `output` list using
      # the renderText() method, creating a value the UI can display
      output$message <- renderText({
        # This block is like a function that will automatically rerun
        # when a referenced `input` value changes
    
        # Use the `username` key from `input` to create a value
        message_str <- paste0("Hello ", input$username, "!")
    
        # Return the value to be rendered by the UI
        message_str
      })
    }

    (The specifics of the server and its functions are detailed in Section 19.3.)

The UI and the server are both written in the app.R file. They are combined by calling the shinyApp() function, which takes a UI value and a server function as arguments. For example:

# To start running your app, pass the variables defined in previous
# code snippets into the `shinyApp()` function
shinyApp(ui = my_ui, server = my_server)

Executing the shinyApp() function will start the app. Alternatively, you can launch a Shiny app using the “Run App” button at the top of RStudio (see Figure 19.2). This will launch a viewer window presenting your app (Figure 19.3); you can also click the “Open in Browser” button at the top to show the app running in your computer’s web browser. Note that if you need to stop the app, you can close the window or click the “Stop Sign” icon that appears on the RStudio console.

A screenshot depicts the launch of a Shiny app using the "Run App" button.
Figure 19.2 Use RStudio to run a Shiny app. The "Run App" button starts the application, while the “Stop Sign” icon in the console stops it.
A screenshot displays the Shiny application webpage.
Figure 19.3 A Shiny application that greets a user based on an input name, running in the RStudio viewer. Note the “Open in Browser” and Refresh buttons at the top.

Tip

If you change the UI or the server, you generally do not need to stop and start the app. Instead, you can refresh the browser or viewer window, and it will reload with the new UI and server.

When this example application is run, Shiny will combine the UI and server components into a webpage that allows the user to type a name into an input box; the page will then say “Hello” to whatever name is typed in (as shown in Figure 19.3). As the user types into the input box (created by the textInput() function), the UI sends an updated username value to the server; this value is stored in the input argument list as input$username. The renderText() function in the server then reacts to the change to the input$username value, and automatically re-executes to calculate a new renderable value that is stored in output$message and sent back to the UI (illustrated in Figure 19.4). Through this process, the app provides a dynamic experience in which the user types into a box and sees the message change in response. While this is a simple example, the same structure can be used to create searchable data tables, change the content of interactive graphics, or even specify the parameters of a machine learning model!

A figure depicts the variables passing between a UI and a server.
Figure 19.4 Variables passing between a UI and a server. The server function accepts inputs from the UI and generates a set of outputs that are passed back to the UI to be rendered.

Tip

The reactivity involved in Shiny apps can make them difficult to debug. Code statements don’t flow directly from top to bottom as with most scripts, and Shiny may produce somewhat obscure error messages in the console when something goes wrong. As with R Markdown, a good strategy for identifying problematic code is to systematically remove (“comment out”) segments of your project and attempt to rerun your application.

For additional advice on how to fix issues in Shiny apps, see the official Debugging Shiny applicationsa guide.

ahttps://shiny.rstudio.com/articles/debugging.html

A Shiny app divides responsibilities between its UI and server: the UI is responsible for presenting information, while the server is responsible for processing information. Enabling such a separation of concerns is a fundamental principle when designing computer programs, as it allows developers to isolate their problem solving and more easily create scalable and collaborative projects. Indeed, this division is the same separation recommended in splitting code across .R and .Rmd files.

While it is possible to define both the UI and server in the same app.R file, you can further emphasize this separation of concerns by instead defining the UI and server in separate files (e.g., my_ui.R and my_server.R). You can then use the source() function to load those variables into the app.R script for combining. Such a division can help keep your code more organized and understandable, particularly as your apps grow larger.

If you name the separate files exactly ui.R and server.R (and have the last value returned in each script be the UI value and the server function, respectively), RStudio will be able to launch your Shiny application without having a unified app.R file. Even so, it is better practice to use a single app.R script to run your Shiny app, and then source() in the UI and server to keep them separated.

Caution

Avoid creating both an app.R and files named exactly ui.R and server.R in your project. This can confuse RStudio and cause your application not to run. Pick one approach or the other!

Going Further

You can use the Shiny framework to add interactive widgets to HTML documents created using R Markdown! See the Introduction to Interactive Documents article.a Note that the webpage will still need to be hosted somewhere that supports a Shiny server (such as shinyapps.io, described in Section 19.4).

ahttps://shiny.rstudio.com/articles/interactive-docs.html

19.2 Designing User Interfaces

To enable the rapid discovery of information, you will want to create interfaces that prioritize pertinent information in a clear and organized fashion. The Shiny framework provides structural elements that you can use to construct such a well-organized page.

When you write code defining a UI, you are defining how the app will be displayed in the browser. You create a UI by calling a layout function such as fluidPage(), which will return a UI definition that can be used by the shinyApp() function. Layout functions take as arguments the content elements (pieces of content) that you want the layout to contain (and thus will be shown in the app’s UI):

# A "pseudocode" example rendering 3 UI elements in a fluid layout
ui <- fluidPage(element1, element2, element3)

A layout function can take as many content elements as needed, each as an additional argument (often placed onto separate lines for readability). For example, the UI shown in Figure 19.2 has three content elements: one produced by the h2() function, one produced by the textInput() function, and one produced by the textOutput() function.

Many different types of content elements can be passed to a layout function, as described in the following sections.

Tip

You can initially implement your app with an “empty” server function as a way to design and test your UI—a UI does not require any actual content in the server! See Section 19.3 for an example of an empty server function.

19.2.1 Static Content

The simplest type of content element a UI can include is a static content element. These elements specify content that will not change as the user interacts with the page. They are generally used to provide further explanatory information about what the user is looking at—similar to the Markdown portion of an R Markdown document.

Content elements are created by calling specific functions that create them. For example, the h1() function will create an element that has a first-level heading (similar to using a # in Markdown). These functions are passed arguments that are the content (usually strings) that should be shown:

# A UI whose layout contains a single static content element
ui <- fluidPage(
  h1("My Static App")
)

Static content functions can alternatively be referenced as elements of the tags list (e.g., tags$h1()), so they are also known as “tag functions.” This is because static content functions are used to produce HTML,2 the language used to specify the content of webpages (recall that a Shiny app is an interactive webpage). As such, static content functions are all named after HTML tags. But since Markdown is also compiled into HTML tags (as when you knit an R Markdown document), many static content functions correspond to Markdown syntax, such as those described in Table 19.1. See the HTML Tags Glossary3 for more information about the meaning of individual functions and their common arguments.

2HTML tutorials and reference from Mozilla developer network: https://developer.mozilla.org/en-US/docs/Web/HTML

3Shiny HTML Tags Glossary: https://shiny.rstudio.com/articles/tag-glossary.html

Table 19.1 Some example static content functions and their Markdown equivalents

Static Content Function

Markdown Equivalent

Description

h1("Heading 1")

# Heading 1

A first-level heading

h2("Heading 2")

## Heading 2

A second-level heading

p("some text")

some text (on own line)

A paragraph (of plain text)

em("some text")

_some text_

Emphasized (italic) text

strong("some text")

**some text**

Strong (bold) text

a("some text", href = "url")

[some text](url)

A hyperlink (anchor)

img("description", src = "path")

![description](path)

An image

Static content functions can be passed multiple unnamed arguments (i.e., multiple strings), all of which are included as that kind of static content. You can even pass other content elements as arguments to a tag function, allowing you to “nest” formatted content:

# Create a UI using multiple nested content elements
ui <- fluidPage(
  # An `h1()` content element that contains an `em()` content element
  # This will render like the Markdown content `# My _Awesome_ App`
  h1("My", em("Awesome"), "App"),

  # Passing multiple string arguments will cause them to be concatenated (within
  # the same paragraph)
  p("My app is really cool.", "It's the coolest thing ever!"),
)

It is common practice to include a number of static elements (often with such nesting) to describe your application—similar to how you would include static Markdown content in an R Markdown document. In particular, almost all Shiny apps include a titlePanel() content element, which provides both a second-level heading (h2()) element for the page title and specifies the title shown in the tab of a web browser.

Going Further

If you are familiar with HTML syntax, you can write such content directly using the HTML() function, passing in a string of the HTML you want to include. Similarly, if you are familiar with CSS, you can include stylesheets using the includeCSS() content function. See the article Style Your Apps with CSSa for other options and details.

ahttps://shiny.rstudio.com/articles/css.html

19.2.2 Dynamic Inputs

While your application will include many static elements, the true power and purpose of Shiny come from its support for user interactions. In a Shiny app, users interact with content elements called control widgets. These elements allow users to provide input to the server, and include elements such as text input boxes, dropdown menus, and sliders. Figure 19.5 shows a sample of widgets available in the Shiny package.

A screenshot shows the sample of widgets available in the shiny application.
Figure 19.5 Examples of control widgets that can be included in the UI of a Shiny application (image from shiny.rstudio.com).

Each widget handles user input by storing a value that the user has entered—whether by typing into a box, moving a slider, or clicking a button. When the user interacts with the widget and changes the input, the stored value automatically changes as well. Thus you can almost think of each widget’s value as a “variable” that the user is able to modify by interacting with the web browser. Updates to the value stored by the widget are sent to the server, which will react to those changes to generate new content to display.

Like static content elements, control widgets are created by calling an appropriate function—most of which include the word “input” in the name. For example:

  • textInput() creates a box in which the user can enter text. The “Greeting” app described previously includes a textInput().

  • sliderInput() creates a slider that the user can drag to choose a value (or range of values).

  • selectInput() creates a dropdown menu the user can choose from.

  • checkboxInput() creates a box the user can check (using checkboxGroupInput() to group them).

  • radioButtons() creates “radio” buttons (the user can select only one of these buttons at a time, just like selecting the station on a radio).

See the documentation4 for a complete list of available control widgets, and the widgets gallery5 for examples.

4Shiny reference: http://shiny.rstudio.com/reference/shiny/latest/

5Shiny Widgets Gallery: http://shiny.rstudio.com/gallery/widget-gallery.html

All widget functions take at least two arguments:

  • An inputId (a string) or “name” for the widget’s value. This is the “key” that allows the server to access that widget’s value (literally, it is the key for that value in the input list argument).

  • A label (as a string or static content element) that will be shown alongside the widget and tell the user what the value represents. The label can be an empty string ("") if you don’t want to show anything.

Other arguments may be required by a particular widget. For example, a slider widget requires a min, max, and (starting) value, as in the code below.

Control widgets are used to solicit input values from the user, which are then sent to the server for processing. See Section 19.3 for details on how to use these input values.

# A UI containing a single slider
ui <- fluidPage(
  sliderInput(
    inputId = "age",           # key this value will be assigned to
    label = "Age of subjects", # label to display alongside the slider
    min = 18,                  # minimum slider value
    max = 80,                  # maximum slider value
    value = 42                 # starting value for the slider
  )
)

19.2.3 Dynamic Outputs

To display output values from the server, a UI uses reactive output elements. This kind of content element is similar to a static content element, but instead of displaying unchanging content, it displays dynamic (changing) content produced by the server—for example, a chart that dynamically updates when a user selects different data to display, or a table that responds to a search query. A reactive output will automatically update whenever the server sends it a new value to display.

As with other content elements, reactive outputs are created by calling an appropriate function, most of which include the word “output” in the name. For example:

  • textOutput() displays output as plain text; use htmlOutput() if you want to render HTML content.

  • tableOutput() displays output as a data table (similar to kable() in R Markdown). Note that the dataTableOutput() function from the DT package will display an interactive table.

  • plotOutput() displays a graphical plot, such as one created with the ggplot2 package. The plotlyOutput() function from the plotly package can be used to render an interactive plot, or you can make a ggplot2 plot interactive.6

    6Interactive Plots: http://shiny.rstudio.com/articles/plot-interaction.html

  • verbatimTextOutput() displays content as a formatted code block, such as if you wanted to print a non-string variable like a vector or data frame.

Each of these functions takes as an argument the outputId (a string) or “name” for the value that will be displayed. The function uses this “key” to access the value that is output by the server. For example, you could show the following information generated by your server:

# A UI containing different reactive outputs
ui <- fluidPage(
  textOutput(outputId = "mean_value"),  # display text stored in `output$mean_value`
  tableOutput(outputId = "table_data"), # display table stored in `output$table_data`
  plotOutput(outputId = "my_chart")     # display plot stored in `output$my_chart`
)

Note that each function may support additional arguments as well (e.g., to specify the size of a plot). See the documentation for details on individual functions.

Caution

Each page can show a single output value just once (because it needs to be given a unique id in the generated HTML). For example, you can’t include textOutput(outputId = "mean_value") twice in the same UI.

Remember

As you build your application’s UI, be careful to keep track of the names (inputId and outputId) you give to each control widget and reactive output; you will need these to match with the values referenced by the server!

19.2.4 Layouts

You can specify how content is organized on the page by using different layout content elements. Layout elements are similar to other content elements, but are used to specify the position of different pieces of content on the page—for example, organizing content into columns or grids, or breaking up a webpage into tabs.

Layout content elements are also created by calling associated functions; see the Shiny documentation or the Layout Guide7 for a complete list. Layout functions all take as arguments a sequence of other content elements (created by calling other functions) that will be shown on the page following the specified layout. For example, the previous examples use a fluidPage() layout to position content from top to bottom in a way that responds to the size of the browser window.

7Shiny Application Layout Guide: http://shiny.rstudio.com/articles/layout-guide.html

Because layouts themselves are content elements, it’s also possible to pass the result of calling one layout function as an argument to another. This allows you to specify some content that is laid out in “columns," and then have the “columns” be placed into a “row” of a grid. As an example, the commonly used sidebarLayout() function organizes content into two columns: a “sidebar” (shown in a gray box, often used for control widgets or related content) and a “main” section (often used for reactive outputs such as plots or tables). Thus sidebarLayout() needs to be passed two arguments: a sidebarPanel() layout element that contains the content for the sidebar, and a mainPanel() layout element that contains the content for the main section:

ui <- fluidPage(  # lay out the passed content fluidly
  sidebarLayout(  # lay out the passed content into two columns
    sidebarPanel( # lay out the passed content inside the "sidebar" column
      p("Sidebar panel content goes here")
    ),
    mainPanel(    # lay out the passed content inside the "main" column
      p("Main panel content goes here"),
      p("Layouts usually include multiple content elements")
    )
  )
)

An example of a sidebar layout is also shown in Figure 19.6.

A screenshot depicts the "multi-page" application built with Shiny's layout functions.
Figure 19.6 A “multi-page” application built with Shiny’s layout functions, including navbarPage() and sidebarLayout(). Red notes are added.

Caution

Because Shiny layouts are usually responsive to web browser size, on a small window (such as the default app viewer) the sidebar may be placed above the content—since there isn’t room for it to fit nicely on the side!

Since a layout and its content elements are often nested (similar to some static content elements), you almost always want to use line breaks and indentation to make that nesting apparent in the code. With large applications or complex layouts, you may need to trace down the page to find the closing parenthesis ) that indicates exactly where a particular layout’s argument list (passed in content) ends.

Because layout functions can quickly become complex (with many other nested content functions), it is also useful to store the returned layouts in variables. These variables can then be passed into higher-level layout functions. The following example specifies multiple “tabs” of content (created using the tabPanel() layout function), which are then passed into a navbarPage() layout function to create a page with a “navigation bar” at the top to browse the different tabs. The result is shown in Figure 19.6.

# Define the first page content; uses `tabPanel()` and `sidebarLayout()`
# layout functions together (as an example)
page_one <- tabPanel(
  "First Page", # label for the tab in the navbar
  titlePanel("Page 1"), # show with a displayed title

  # This content uses a sidebar layout
  sidebarLayout(
    sidebarPanel(
      textInput(inputId = "username", label = "What is your name?")
    ),
    mainPanel(
      h3("Primary Content"),
      p("Plots, data tables, etc. would go here")
    )
  )
)

# Define content for the second page
page_two <- tabPanel(
  "Second Page" # label for the tab in the navbar
  # ...more content would go here...
)

# Define content for the third page
page_three <- tabPanel(
  "Third Page" # label for the tab in the navbar
  # ...more content would go here...
)

# Pass each page to a multi-page layout (`navbarPage`)
ui <- navbarPage(
  "My Application", # application title
  page_one,         # include the first page content
  page_two,         # include the second page content
  page_three        # include the third page content
)

The Shiny framework can be used to develop highly complex layouts just by calling R functions. For more examples and details on how to achieve particular layout and UI effects, check the Shiny documentation and application gallery.

Fun Fact

Much of Shiny’s styling and layout structure is based on the Bootstrapa web framework, which is how it supports layouts that are responsive to window size. Note that Shiny uses Bootstrap 3, not the more recent Bootstrap 4.

ahttp://getbootstrap.com/docs/3.3/

19.3 Developing Application Servers

To generate dynamic data views that can be shown in the UI (as reactive outputs), you will need to specify how you want that data to be manipulated based on the user input (through control widgets). In the Shiny framework, you define this manipulation as the application’s server.

You create a Shiny server by defining a function (rather than calling a provided one, as with a UI). The function must be defined to take at least two arguments: a list to hold the input values, and a list to hold the output values:

# Define a server function for a Shiny app
server <- function(input, output) {
  # assign values to `output` here
}

Note that a server function is just a normal function, albeit one that will be executed to “set up” the application’s reactive data processing. Thus you can include any code statements that would normally go in a function—though that code will be run only once (when the application is first started) unless defined as part of a render function.

When the server function is called to set up the application, it will be passed the input and output list arguments. The first argument (input) will be a list containing any values stored by the control widgets in the UI: each inputId (“name”) in a control widget will be a key in this list, whose value is the value currently stored by the widget. For example, the textInput() shown in Figure 19.2 has an inputId of username, so would cause the input list to have a username key (referenced as input$username inside of the server function). This allows the server to access any data that the user has input into the UI. Importantly, these lists are reactive, so the values inside of them will automatically change as the user interacts with the UI’s control widgets.

The primary purpose of the server function is to assign new values to the output list (each with an appropriate key). These values will then be displayed by the reactive outputs defined in the UI. The output list is assigned values that are produced by render functions, which are able to produce output in a format that can be understood by the UI’s outputs (reactive outputs can’t just display plain strings). As with the UI’s reactive output functions, the server uses different render functions for the different types of output it provides, as shown in Table 19.2.

Table 19.2 Some example render functions and their associated reactive outputs

Render Function (Server)

Reactive Output (UI)

Content Type

renderText()

textOutput()

Unformatted text (character strings)

renderTable()

tableOutput()

A simple data table

renderDataTable()

dataTableOutput()

An interactive data table (use the DT package)

renderPlot()

plotOutput()

A graphical plot (e.g., created with ggplot2)

renderPlotly()

plotlyOutput()

An interactive Plotly plot

renderLeaflet()

leafletOutput()

An interactive Leaflet map

renderPrint()

verbatimTextOutput()

Any output produced with print()

The result of a render function must be assigned to a key in the output list argument that matches the outputId (“name”) specified in the reactive output. For example, if the UI includes textOutput(outputId = "message"), then the value must be assigned to output$message. If the keys don’t match, then the UI won’t know what output to display! In addition, the type of render function must match the type of reactive output: you can’t have the server provide a plot to render but have the UI try to output a table for that value! This usually means that the word after “render” in the render function needs to be the same as the word before “Output” in the reactive output function. Note that Shiny server functions will usually have multiple render functions assigning values to the output list—one for each associated reactive output in the UI.

All render functions take as an argument a reactive expression. A reactive expression is a lot like a function: it is written as a block of code (in braces {}) that returns the value to be rendered. Indeed, the only difference between writing a function and writing a reactive expression is that you don’t include the keyword function or a list of arguments—you just include the block (the braces and the code inside it).

# Create a server function that defines a `message` output based on a
# `username` input
server <- function(input, output) {
  # Define content to be displayed by the `message` reactive output
  # `renderText()` is passed a reactive expression
  output$message <- renderText({
    # A render function block can include any code used in a regular function
    my_greeting <- "Hello "

    # Because the render function references an `input` value, it will be
    # automatically rerun when that value changes, producing an updated output
    message_str <- paste0(my_greeting, input$username, "!")
    message_str # return the message to be output
  })
}

Going Further

Reactive expressions technically define a closure, which is a programming concept used to encapsulate functions and the context for those functions.

What is significant about render functions is that they will automatically “rerun” their passed-in code block every time a value they reference in the input list changes. So if the user interacts with the username control widget in the UI (and thereby changes the input$username value), the function in the preceding example will be executed again—producing a new value that will be reassigned to output$message. And once output$message changes, any reactive output in the UI (e.g., a textOutput()) will update to show the latest value. This makes the app interactive!

Remember

In effect, render functions are functions that will be rerun automatically when an input changes, without you having to call them explicitly! You can think of them as the functions you define for how the output should be determined—and those functions will be rerun when the input changes.

Thus your server defines a series of “functions” (render functions) that specify how the output should change based on changes to the input—when that input changes, the output changes along with it.

Tip

Data values that are not reactive (that will not change based on user interaction) can be defined elsewhere in the server function, as normal. If you want a nonreactive data value to be available to the UI as well—such as one that contains configuration or static data range information—you should create it outside of the server function in the app.R file, or in a separate global.R file. See the Scoping Rules for Shiny Apps articlea for details.

ahttps://shiny.rstudio.com/articles/scoping.html

Going Further

Understanding the flow of data in and between render functions and other reactive expressions is the key to developing complex Shiny applications. For more details on reactivity in Shiny, see RStudio’s articles on reactivity,a particularly Reactivity: An Overviewb and How to Understand Reactivity in R.c

ahttps://shiny.rstudio.com/articles/#reactivity

bhttps://shiny.rstudio.com/articles/reactivity-overview.html

chttps://shiny.rstudio.com/articles/understanding-reactivity.html

19.4 Publishing Shiny Apps

While the previous sections discussed building and running Shiny apps on your own computer, the entire point of an interactive application is to be able to share it with others. To do that, you will need a website that is able to host the application so that others can navigate to it using their web browsers. However, you can’t just use GitHub Pages to host the application because—in addition to the UI—you need an R interpreter session to run the server that the UI can connect to (and GitHub does not provide R interpreters). For this reason, sharing a Shiny app with the world is a bit more involved than simply pushing the code to GitHub.

While there are a few different solutions for hosting Shiny apps, the simplest is hosting through shinyapps.io8. shinyapps.io is a platform provided by RStudio that is used for hosting and running Shiny apps. Anyone can deploy and host five small(ish) applications to the platform for free, though deploying large applications costs money.

8shinyapps.io web hosting for Shiny apps: https://www.shinyapps.io

To host your app on shinyapps.io, you will need to create a free account.9 You can sign up with GitHub (recommended) or a Google account. After you sign up, follow the site’s instructions:

9shinyapps.io signup: https://www.shinyapps.io/admin/#/signup

  • Select an account name, keeping in mind it will be part of the URL people use to access your application.

  • Install the required rsconnect package (it may have been included with your RStudio download).

  • Set your authorization token (“password”) for uploading your app. To do this, click the green “Copy to Clipboard” button, and then paste that selected command into the Console in RStudio. You should need to do this just once per machine.

    Don’t worry about the listed “Step 3 - Deploy”; you should instead publish directly through RStudio!

After you have set up an account, you can publish your application by running your app through RStudio (i.e., by clicking the “Run App” button), and then clicking the “Publish” button in the upper-right corner of the app viewer (see Figure 19.7).

A screenshot depicts the function of the Publish button in the app viewer.
Figure 19.7 Click the Publish button in the upper-right corner of a Shiny app to publish it to shinyapps.io.

After a minute of processing and uploading, your app should become available online to use at the URL:

# The URL for a Shiny app hosted with shinyapps.io
https://USERNAME.shinyapps.io/APP_NAME/

While it sounds as simple as clicking a button, publishing to shinyapps.io is unfortunately one of the “pain points” in working with Shiny. Things can unexpectedly go wrong, and it’s even more difficult to determine the problem than with local Shiny apps! Here are some useful tips for successfully publishing an application:

  1. Always test and debug your app locally (e.g., on your own computer, by running the app through RStudio). It’s easier to find and fix errors locally; make sure the app works on your machine before you even try to put it online.

  2. You can view the error logs for your deployed app by either using the “Logs” tab in the application view or calling the showLogs() function (part of the rsconnect package). These logs will show print() statements and often list the errors that explain the problem that occurred when deploying your app.

  3. Use correct folder structures and relative paths. All of your app files should reside in a single folder (usually named after the project). Make sure any .csv or .R files referenced are inside the app folder, and that you use relative paths to refer to them in your code. Do not ever include any setwd() statements in your code; only set the working directory through RStudio (because shinyapps.io will have its own working directory).

  4. Make sure that any external packages you use are referenced with the library() function in your app.R file. The most common problem we’ve seen involves external packages not being available. See the documentation10 for an example and suggested solutions.

    10shinyapps.io build errors on deployment: http://docs.rstudio.com/shinyapps.io/Troubleshooting.html#build-errors-on-deployment

For more options and details, see the shinyapps.io user guide.11

11shinyapps.io user guide: http://docs.rstudio.com/shinyapps.io/index.html

19.5 Shiny in Action: Visualizing Fatal Police Shootings

This section demonstrates building a Shiny application to visualize a data set of people who were fatally shot by the police in the United States in the first half of 2018 (January through June). The data set was compiled by the Washington Post,12 which made the data available on GitHub.13 Latitude and longitude have been added to the data based on the city and state; the code for this data preparation is available alongside the full application code in the book code repository.14

12“Fatal Force”, Washington Post: https://www.washingtonpost.com/graphics/2018/national/police-shootings-2018/

13Fatal Shootings GitHub page: https://github.com/washingtonpost/data-police-shootings

14Shiny in Action: https://github.com/programming-for-data-science/in-action/tree/master/shiny

As of the time of writing, there were 506 fatalities captured in the data set during the time period, each one of which has 17 pieces of information about the incident, such as the name, age, and race of the victim (a subset of the data is shown in Figure 19.8). The purpose of the Shiny application is to understand the geographic distribution of where people have been killed by the police, and to provide summary information about the incidents, such as the total number of people killed broken down by race or gender. The final product (shown in Figure 19.10) allows users to select a variable in the data set—such as race or gender—through which to analyze the data. This choice will dictate the color encoding in the map as well as the level of aggregation in a summary table.

A screenshot depicts the subset of the police shooting data set. The data set of nine rows listed under headers date, manner_of_death, age, gender, race, city, and state with a spin box.
Figure 19.8 A subset of the police shootings data set, originally compiled by the Washington Post.

A main component of this application will be an interactive map displaying the location of each shooting. The color of each point will expresses additional information about that individual (such as race or gender). While the column used to dictate the color will eventually be dynamically selected by the user, you can start by creating a map with the column “hard-coded.” For example, you can use Leaflet (discussed in Section 17.3) to generate a map displaying the location of each shooting with points colored by race of the victim (shown in Figure 19.9):

# Create a map of fatal police shootings using leaflet

# Load the leaflet function for mapping
library("leaflet")

# Load the prepared data
shootings <- read.csv("police-shootings.csv", stringsAsFactors = FALSE)

# Construct a color palette (scale) based on the `race` column
# Using double-bracket notation will make it easier to adapt for use with Shiny
palette_fn <- colorFactor(palette = "Dark2", domain = shootings[["race"]])

# Create a map of the shootings using leaflet
# The `addCircleMarkers()` function will make circles with radii based on zoom
leaflet(data = shootings) %>%
  addProviderTiles("Stamen.TonerLite") %>% # add Stamen Map Tiles
  addCircleMarkers( # add markers for each shooting
    lat = ~lat,
    lng = ~long,
    label = ~paste0(name, ", ", age), # add a hover label: victim's name and age
    color = ~palette_fn(shootings[["race"]]), # color points by race
    fillOpacity = .7,
    radius = 4,
    stroke = FALSE
  ) %>%
  addLegend( # include a legend on the plot
    position = "bottomright",
    title = "race",
    pal = palette_fn, # the palette to label
    values = shootings[["race"]], # again, using double-bracket notation
    opacity = 1 # legend is opaque
  )
A screenshot depicts a map of each person killed by the police in 2018 by using leaflet.
Figure 19.9 A map of each person killed by the police in 2018, created using leaflet.

Tip

Because server inputs in a Shiny application are strings, it’s helpful to use R’s double-bracket notation to select data of interest (e.g., df[[input$some_key]]), rather than relying on dplyr functions such as select().

Tip

A great way to develop a Shiny application is to first build a static version of your content, then swap out static values (variable names) for dynamic ones (information stored in the input variable). Starting with a working version of your content will make debugging the application much easier.

While this map allows you to get an overall sense of the geographic distribution of the fatalities, supporting it with specific quantitative data—such as the total number of people killed by race—can provide more precise information. Such summary information can be calculated using the dplyr functions group_by() and count(). Note the use of double-bracket notation to pass in the column values directly (rather than referencing the column by name), which will allow you to more easily make the column name dynamic in the Shiny application.

# Calculate the number of fatalities by race
# Use double-bracket notation to support dynamic column choice in Shiny
table <- shootings %>%
  group_by(shootings[["race"]]) %>% # pass the column values directly
  count() %>%
  arrange(-n) # sort the table in decreasing order by number of fatalities

colnames(table) <- c("race", "Number of Victims") # Format column names

With these data representations established, you can begin implementing the Shiny application. For every Shiny app, you will need to create a UI and a server. It’s often useful to start with the UI to help provide structure to the application (and it’s easier to test that it works). To create the UI that will render these elements, you can use a structure similar to that described in Section 19.2.4 and declare a fluidPage() layout that has a sidebarPanel() to keep the control widgets (a “dropdown box” that lets the user select which column to analyze) along the side, and a mainPanel() in which to show the primary content (the leaflet map and the data table):

# Define the UI for the application that renders the map and table
my_ui <- fluidPage(
  # Application title
  titlePanel("Fatal Police Shootings"),

  # Sidebar with a selectInput for the variable for analysis
  sidebarLayout(
    sidebarPanel(
      selectInput(
        inputId = "analysis_var",
        label = "Level of Analysis",
        choices = c("gender", "race", "body_camera", "threat_level")
      )
    ),

    # Display the map and table in the main panel
    mainPanel(
      leafletOutput(outputId = "shooting_map"), # reactive output from leaflet
      tableOutput(outputId = "grouped_table")
    )
  )
)

You can check that the UI looks correct by providing an empty server function and calling the shinyApp() function to run the application. While the map and data won’t show up (they haven’t been defined), you can at least check the layout of your work.

# A temporarily empty server function
server <- function(input, output) {

}

# Start running the application
shinyApp(ui = my_ui, server = server)

Once the UI is complete, you can fill in the server. Since the UI renders two reactive outputs (a leafletOutput() and a tableOutput()), your server needs to provide corresponding render functions. These functions can return versions of the “hard-coded” map and data table defined previously, but using information taken from the UI’s input to select the appropriate column—in other words, replacing the "race" column with the column named by input$analysis_var.

Notice the use of input$analysis_var to dynamically set the color of each point, as well as the aggregation column for the data table.

# Define the server that renders a map and a table
my_server <- function(input, output) {

  # Define a map to render in the UI
  output$shooting_map <- renderLeaflet({

    # Construct a color palette (scale) based on chosen analysis variable
    palette_fn <- colorFactor(
      palette = "Dark2",
      domain = shootings[[input$analysis_var]]
    )

    # Create and return the map
    leaflet(data = shootings) %>%
      addProviderTiles("Stamen.TonerLite") %>% # add Stamen Map Tiles
      addCircleMarkers( # add markers for each shooting
        lat = ~lat,
        lng = ~long,
        label = ~paste0(name, ", ", age), # add a label: name and age
        color = ~palette_fn(shootings[[input$analysis_var]]), # set color w/ input
        fillOpacity = .7,
        radius = 4,
        stroke = FALSE
      ) %>%
      addLegend( # include a legend on the plot
        "bottomright",
        title = input$analysis_var,
        pal = palette_fn, # the palette to label
        values = shootings[[input$analysis_var]], # again, double-bracket notation
        opacity = 1 # legend is opaque
      )
  })

  # Define a table to render in the UI
  output$grouped_table <- renderTable({
    table <- shootings %>%
      group_by(shootings[[input$analysis_var]]) %>%
      count() %>%
      arrange(-n)

    colnames(table) <- c(input$analysis_var, "Number of Victims") # format columns
    table # return the table
  })
}

As this example shows, in a little less than 80 lines of well-commented code, you can build an interactive application for exploring fatal police shootings. The final application is shown in Figure 19.10, and the full code appears below.

A screenshot shows the Shiny application exploring fatal police shooting in the year 2018.
Figure 19.10 A Shiny application exploring fatal police shootings in 2018. A dropdown menu allows users to select the feature that dictates the color on the map, as well as the level of aggregation for the summary table.
# An interactive exploration of police shootings in 2018
# Data compiled by the Washington Post

# Load libraries
library(shiny)
library(dplyr)
library(leaflet)

# Load the prepared data
shootings <- read.csv("police-shootings.csv", stringsAsFactors = FALSE)

# Define UI for application that renders the map and table
my_ui <- fluidPage(
  # Application title
  titlePanel("Fatal Police Shootings"),

  # Sidebar with a selectInput for the variable for analysis
  sidebarLayout(
    sidebarPanel(
      selectInput(
        inputId = "analysis_var",
        label = "Level of Analysis",
        choices = c("gender", "race", "body_camera", "threat_level")
      )
    ),

    # Display the map and table in the main panel
    mainPanel(
      leafletOutput("shooting_map"), # reactive output provided by leaflet
      tableOutput("grouped_table")
    )
  )
)

# Define server that renders a map and a table
my_server <- function(input, output) {

  # Define a map to render in the UI
  output$shooting_map <- renderLeaflet({

    # Construct a color palette (scale) based on chosen analysis variable
    palette_fn <- colorFactor(
      palette = "Dark2",
      domain = shootings[[input$analysis_var]]
    )

    # Create and return the map
    leaflet(data = shootings) %>%
      addProviderTiles("Stamen.TonerLite") %>% # add Stamen Map Tiles
      addCircleMarkers( # add markers for each shooting
        lat = ~lat,
        lng = ~long,
        label = ~paste0(name, ", ", age), # add a label: name and age
        color = ~palette_fn(shootings[[input$analysis_var]]), # set color w/ input
        fillOpacity = .7,
        radius = 4,
        stroke = FALSE
      ) %>%
      addLegend( # include a legend on the plot
        "bottomright",
        title = "race",
        pal = palette_fn, # the palette to label
        values = shootings[[input$analysis_var]], # double-bracket notation
        opacity = 1 # legend is opaque
      )
  })

  # Define a table to render in the UI
  output$grouped_table <- renderTable({
    table <- shootings %>%
      group_by(shootings[[input$analysis_var]]) %>%
      count() %>%
      arrange(-n)

    colnames(table) <- c(input$analysis_var, "Number of Victims") # format column names
    table # return the table
  })
}

# Start running the application
shinyApp(ui = my_ui, server = my_server)

By creating interactive user interfaces for exploring your data, you can empower others to discover relationships in the data, regardless of their technical skills. This will help bolster their understanding of your data set and eliminate requests for you to perform different analyses (others can do it themselves!).

Tip

Shiny is a very complex framework and system, so RStudio provides a large number of resources to help you learn to use it. In addition to providing a cheatsheet available through the RStudio menu (Help > Cheatsheets), RStudio has compiled a detailed and effective set of video and written tutorials.a

ahttp://shiny.rstudio.com/tutorial/

For practice building Shiny applications, see the set of accompanying book exercises.15

15Shiny exercises: https://github.com/programming-for-data-science/chapter-19-exercises

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

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