Shiny Applications

Creating interactive Web applications with R


Welcome back everyone! Today we will have a look at

  1. How to create Shiny Apps in R
  2. What to look out for when creating Dashboards

Shiny Applications in R 💎

What is Shiny?

Shiny is an R package that makes it easy to build interactive web applications (apps) straight from R. You need to install the package shiny:

#install.packages("shiny")
library(shiny)

For a quick illustration of what you can do with Shiny head over to the interactive visualization tool for the Freedom of Press Index

Throughout the lab we will use a running example to illustrate the different aspects of creating a shiny app. You can find it in the folder our_app. It is an interactive plot that displays all movies on Rotten Tomatoes and includes several filtering options.


Structure of a Shiny App

Shiny apps are contained in a single script called app.R. The script app.R lives in a directory (for example, newdir/) and the app can be run with shiny::runApp("newdir").

The app.R file has three core components:

  1. A user interface object: The (UI) object controls the layout and appearance of your app.

  2. A server function: The server function contains the instructions that your computer needs to build your app.

  3. A call to the shiny::shinyApp() function: The shiny::shinyApp() function creates Shiny app objects from an explicit UI/server pair.

# illustrated workflow 

ui <- shiny::fluidPage()

server <- function(input, output) {}

shiny::shinyApp(ui = ui, server = server)

Running a Shiny App

To create a Shiny app you need to create a new directory and save an app.R file inside of it. This app.R script needs to contain the three components outlined above. Further, it is recommended that each app should have its own unique directory.

You can run a Shiny app by giving the name of this directory to the function shiny::runApp(). For example, if your Shiny app is in a directory called my_shiny_app, run it with the following code:

shiny::runApp("my_shiny_app")

Layout functions / UI 🗾

Shiny uses layout functions to provide the high-level visual structure of an app. For example, shiny::fluidPage() creates a display that automatically adjusts to the dimensions of the user’s browser window. You can lay out the user interface of your app by placing elements in the shiny::fluidPage() function. Alternatives to shiny::fluidPage() exist (i.e. shiny::fixedPage(), shiny::bootstrapPage()), which are useful in specific situations.

Layouts are created by a hierarchy of function calls, where the hierarchy in R matches the generated HTML. This means that you can get an intuitive idea of the app structure by assessing the layout.

fluidPage(
  titlePanel("Hello Shiny!"),
  sidebarLayout(
    sidebarPanel(
      sliderInput("obs", "Observations:", min = 0, max = 1000, value = 500)
    ),
    mainPanel(
      plotOutput("distPlot")
    )
  )
)

Focus on the hierarchy of the function calls:

fluidPage(
  titlePanel(),
  sidebarLayout(
    sidebarPanel(),
    mainPanel()
  )
)

We can guess what is going on by reading their names. We might imagine that this code will generate a classic app design: a title bar at top, followed by a sidebar (containing a slider) and main panel (containing a plot).

📝 Note

The ability to easily see hierarchy through indentation is one of the reasons it is a good idea to use a consistent style when building these apps.


While titlePanel() and sidebarLayout() create a basic layout for your Shiny app, you can also create more advanced layouts. For example you can use navbarPage() to give your app a multi-page user interface that includes a navigation bar. Or you can use fluidRow() and column() to build your layout up from a grid system. If you’d like to learn more about these advanced options, have a look at the Shiny Application Layout Guide.

Within the different layout functions you can also customize your app further by italisizing or making your font bold and even including html elements:

To include images you need to use the img function.

img(src = "my_image.png", height = 72, width = 72)

# "src =" needs to be typed out since the html expects it. 

To learn more about how you can customise your UI see here and here.


Widgets 🐭

A widget is a web element that your users can interact with. In other words to make your app interactive, you need widgets. These provide a way for your users to send messages to the Shiny app, meaning that when a user changes the input in a widget, the value that is shown will change as well.

Shiny comes with a family of pre-built widgets, each created with a transparently named R function. For example, Shiny provides a function named actionButton() that creates an Action Button and a function named sliderInput() that creates a slider bar.

Check out the gallery for an overview of standard widgets:

To add a widget to your app, you need to include a widget function in sidebarPanel() or mainPanel() in your ui object.

Each widget function requires several arguments. The first two arguments for each widget are:

  1. A name for the widget: The user will not see this name, but you can use it to access the widget’s value. The name should be a character string.

  2. A label: This label will appear with the widget in your app. It should be a character string, but it can be an empty string "".

The remaining arguments vary from widget to widget, depending on what the widget needs to do its job. They include things like initial values, ranges, and increments. You can find the exact arguments needed by a widget on the widget function’s help page, (e.g., ?selectInput).

Relevant chunk from our running example:

fluidPage(
  titlePanel("Movie explorer"),
  fluidRow(
    column(3, ... ,
        sliderInput(name = "reviews", label = "Minimum number of reviews on Rotten Tomatoes",
          10, 300, 80, step = 10),
        ... ,
        textInput("director", "Director name contains (e.g., Miyazaki)"),
    ),
    ...,
    selectInput("xvar", "X-axis variable", axis_vars, selected = "Meter")
    )
  )

Display Reactive Output

To give your Shiny App a feeling of live interactiveness you need to include a way to display reactive output. Reactive output automatically responds when your user selects an option in a widget.

There are two steps involved in making your widgets reactive:

  1. Add the relevant output function to your ui function.
  2. Provide R code to build the object in your server function.

Step 1

Shiny provides a family of functions that turn R objects into output for your user interface. Each function creates a specific type of output.

Some examples are: plotOutput() for reactive plots, tableOutput() for reactive tables.

In our running example we used textOutput() to give the number of movies displayed:

ui <- fluidPage(
  titlePanel("Movie explorer"),
  fluidRow(
    column( ... ),
    column(9,
           plotlyOutput("plot1"),
           wellPanel(
             span("Number of movies selected:",
                  textOutput("n_movies")
                  )
             )
           )

Step 2

Placing a function in ui tells Shiny where to display your object. Next, you need to tell Shiny how to build the object.

We do this by providing the R code that builds the object in the server function. You do this by defining a new element for output within the server function. The element name should match the name of the reactive element that you created in the ui.

If you created a textOutput("selected_var") in your ui, output$selected_var in the server function matches this.

To illustrate this, let’s look at our example, where we used textOutput("n_movies"), in the server function we state:

server <- function(input, output, session) {
  ... ,
  
  output$n_movies <- renderText({ nrow(movies()) })
}

# Note that movies is a df that we create within our server function

Every output* function in ui, has a corresponding render* function in server for building reactive widgets.

Each render* function takes a single argument: an R expression surrounded by braces, {}. The expression can be one simple line of code, or it can involve many lines of code.


Exercises

Now it is your turn, adapt the Shiny app by:

  1. Give the scatter plot a Title that updates based on the selected x and y variables.
    • Hint: Start by just giving it a title, then work on making it dynamic.
  2. Turn our scatter plot into a bubble plot, where the size of the bubbles is determined by the revenues generated at the Box-Office.
  3. Adding a widget that let’s you subset by genre of film:
    • Think about where you specify this?
    • Focus on filtering for only one genre at a time.
    • Make sure to specify what the default option should be and how we should handle it.
    • Genre Categories in the data include: Action, Adventure, Animation, Biography, Comedy, Crime, Documentary, Drama, Family, Fantasy, History, Horror, Music, Musical, Mystery, Romance, Sci-Fi, Short, Sport, Thriller, War, Western.

More complicated examples ⚙️

You can create more complicated Shiny apps by loading R Scripts, packages, and data sets.

What you need to know is that:

  • The directory that app.R appears in will become the working directory of the Shiny app
  • Shiny will run code placed at the start of app.R, before the server function, only once during the life of the app.
  • Shiny will run code placed inside server function multiple times, which can slow down the app.

In our example, this is used to first create and join the necessary tables for the entirety of the app. This only needs to be done once. Within the server function we then filter and select based on certain inputs; obviously this needs to be repeated every time the input changes. This is why we placed that code within the server function.


Share your Apps 🧺

You can now build a useful Shiny app, but can you share it with others? This lesson will show you several ways to share your Shiny apps.

When it comes to sharing Shiny apps, you have two basic options:

  1. Share your Shiny app as R scripts. This is the simplest way to share an app, but it only works if your users have R on their own computer (and know how to use it). Users can use these scripts to launch the app from their own R session, just like you’ve been launching the apps so far in this tutorial. All they need to do this a copy of your app directory including the app.R script.

  2. Share your Shiny app as a web page. This is definitely the most user-friendly way to share a Shiny app. Your users can navigate to your app through the internet with a web browser. They will find your app fully rendered, up to date, and ready to go.

If you prefer the second option, I propose that you have a look at shinyapps.io. It’s RStudio’s hosting service for Shiny apps. shinyapps.io lets you upload your app straight from your R session to a server hosted by RStudio. You have complete control over your app including server administration tools. You can find out more about shinyapps.io by visiting shinyapps.io.

The setup and installation for doing this is relatively straightforward, but requires all the standard boring steps, such as setting up an account, password and getting familiar with the website UI. Therefore we won’t do this here. However, if you have questions you can find a detailed guide here.


Guidelines for Dashboards 🎨

Now that you have the tools to create interactive dashboards, you might be eager to use these skills liberally. And so you should. But, dashboards are not always the best way to visualize data. Prior to developing a dashboard you should make sure to ask yourself a few questions.


Checklist before you start ✅

  1. Are you tackling a monitoring task that needs your data/metrics to be updated frequently?
  2. Who will use the dashboard and to what end? What questions will they use it to answer? What actions will they take in response to these answers?
  3. What specific information should be displayed, and is it meaningful without much context?
  4. What could lead to the metrics being wrong/misleading?

If a dashboard still sounds like the best approach after considering these questions, go for it!


Design advice

Even then, there are a number of design guidelines that you should follow. Here is a non-exhaustive list:

  • Minimize distractions.
  • Focus on meaningful quantities of interest, not the ones that look cool.
  • Don’t overload with information.
  • Apply all rules of good data visualization.
  • Use interactive figures with care (e.g., to make optional content conditionally visible)
  • Try not to exceed the boundaries of a single screen.
  • Ensure desktop/mobile screen responsiveness (fluidPage(), will achieve this).

Dashboards with flexdashboard

The goal of flexdashboard is to make it easy to create interactive dashboards for R, using R Markdown. A flexdashboard can either be static (a standard web page) or dynamic (a Shiny interactive document). A wide variety of components can be included in flexdashboard layouts, for a full list see the documentation.

As with Shiny apps, we are going to be working with the example in the folder /flexdash. We will use a slightly adapted version of this flexdashboard created by Philipp Ottolinger.


Flexdashboard in practice 🏀

The big difference to Shiny apps is that the flexdashboard Rmd file will take over the function used to define the layout of the dashboard/app. In other words, the entire Rmd, now plays the role of fluidPage().

So let’s have a closer look at how you can structure a flexdashboard.


Columns and rows

Dashboards are divided into columns and rows, with output components delineated using level 3 markdown headers (###). By default, dashboards are laid out within a single column, with charts stacked vertically within a column and sized to fill available browser height.

If you want to change the dashboard to include multiple columns you can introduce the following (--------------).

If you want to change the dashboard to include multiple tabs you can introduce the following (=============).

In the YAML tab of the Rmd you can specify a number of additional properties:

  • vertical_layout: scroll
  • orientation: rows

Feeling overwhelmed? No need to come up with a fancy design all on your own. You can find a long list of templates here.


A note on shiny dashboards

To add Shiny components to a flexdashboard there are a few things that you need to remember.

  1. you need to add runtime: shiny to the options declared at the top of the document (YAML front matter).

  2. Add the {.sidebar} attribute to the first column of the dashboard to make it a host for Shiny input controls (note this step isn’t strictly required, but many Shiny based dashboards will want to do this).

Overall, however, the same rules apply:

  • You should perform any expensive loading of data within the global chunk.
  • Input elements will go into the {.sidebar} column.
  • Outputs are represented within flexdashboard content panes

Now you are more or less ready to go!


Display all your new skills? 😁

Thinking a bit ahead (e.g. data science job market), one way to go would be to create a personalized website as a central source to everything you would like to show.

What to include, of course, highly depends on your profile, your discipline, and the job market you are interested in.

For an academic webpage that could for example be:

  • about statement
  • CV
  • list of publications/working papers
  • list of course materials for courses that you’ve taught
  • example of data science project / app / package you’re proud of
  • contact + links to Github, Google Scholar, Twitter, etc.

Here is a recommendation by Elsevier on how to create effective academic personal website.


For a non-academic webpage (e.g. focus on industry jobs) that could be:

  • about statement
  • CV (?)
  • EXAMPLES of data science projects
  • contact + links to Github, LinkedIn, etc.

Usually, a good first step is to look at webpages of other people with similar profiles you find inspiring (e.g. people that already have the job you’d like to have). 👀


Tools to build personal webpages

There is a plethora of possibilities to build your personal webpage, from very basic to very very complicated.

Very simple point-and-click ways to create websites are Google Sites, WordPress, Wix, etc. However, the free versions often come with ads and other downsides.


GitHub Pages 🐙

One mid-range way that we can recommend (it requires some coding experience, but you have that!) is to use GitHub Pages because

  • it’s free and free of ads
  • the page is directly hosted from your GitHub repository
  • you can choose from a huge variety of Jekyll themes
  • it compiles HTML from Markdown - something you’ve been doing all the time in this course
  • and there are even more advantages

For example Rob Williams created a great tutorial on how to build an academic websites with Github; and the academicpages template is a ready-to-fork GitHub Pages template and is easily customizable. If you like, you can also configure custom URLs.

Here is a possibly even more accessible, multifaceted, and less complicated Quick Start tutorial on GitHub Pages.


Blogdown

Blogdown is an R package based on Hugo, another static site generator.

  • it is free
  • you have great online support because you work with R and GitHub
  • and you can choose from a wide range of Hugo themes

Here is a tutorial by Andrew Hetherington on how to build a data-science personal website in R using blogdown. His personal webpage is also created with blogdown. You can deploy and host your website on Github or Netlify.


Acknowledgements

The Shiny tutorial leans heavily on RStudio’s series of written shiny tutorials. The running example of the shiny.app was adapted from: Movies-Explorer Interactive App, while the running example of the flexdashboard was adapted from Waste Lands - America’s forgotten nuclear legacy.

This script was drafted by Tom Arendt and Lisa Oswald, with contributions by Steve Kerr, Hiba Ahmad, Carmen Garro, Sebastian Ramirez-Ruiz, Killian Conyngham and Carol Sobral.