Introduction to R Shiny
2024-12-09
Chapter 1 Introduction
Do you want to provide interactive visualisation and data exploration features for users who do not have R and data science skills? Discover how easy it can be to use R and shiny to create your own apps and dashboards for exploring data without relying on web development or external BI tools. You will be shown various examples of input widgets and outputs to display tables and visualisations.
This book is designed to accompany the Introduction to R Shiny training that we run at DfT. To complete this course, you will need a Cloud R account.
Users of this book should feel comfortable with using R and basic graphics with ggplot2.
If you’re running through this book solo, it is recommended to clone the exercise repository, run through the book in order and try out all the of the exercises as you go through. Each exercise has its own folder with an exercise and solution script, which allows you to view prompts to help with the question and run the final solution.
1.1 Session aims
- understand what a Shiny application is
- understand the back-end of building an app
- build a basic user interface
1.2 What is a Shiny application?
Shiny is an R package (similar to dplyr
or data.table
), although it is often treated like it is a completely separate platform.
It provides functionality to produce Shiny apps; for the user, these are essentially web pages running in HTML/CSS/JS. However, it is coded within R, so there is no need to learn any of those languages.
Shiny apps link to a computer running R to do all of the data processing. This makes them very powerful, as it allows R to calculate values “on the fly” i.e. as the user makes requests.
It goes way beyond a dashboarding tool; you can use it to build complicated tools and apps for a range of purposes.
1.3 Why Shiny tools?
- beyond dashboarding: Shiny can be used to create dashboards, but it actually goes far beyond that to allow you to create a whole range of custom apps and tools using R code
- calculating values on the fly: Shiny is ideal for scenarios where you want greater user interactivity. Rather than just filtering data, new values can be calculated based on user inputs, and used or exported
- use of libraries: Shiny allows you to tap into the full range of libraries available in R, so you can build a Shiny app for anything there’s a library for
- no limits: In tools such as PowerBI, it’s relatively easy to hit the barrier of what the software can’t do. In Shiny, you’re generally only limited by what you can’t do
- low barrier to entry: Don’t need to learn HTML/JavaScript to use it
The DfT’s Road Safety Statistics: Collision Analysis Tool was built using R and R Shiny.
1.4 Getting started with a Shiny app
Once you have the shiny
library installed, you can generate a basic app from a template within R Studio.
From there, you can:
- provide a name for the app
- choose a location
- specify whether you want it over one file or two (in this course, you will need to choose two)
1.4.1 How to run a Shiny app
Shiny code does not run like normal R code, you can’t run it one line at a time. Instead, you run the code as a single chunk in an interactive environment. When you’re using a Shiny application, the normal “Run” button will change to a “Run App” button.
Click on the dropdown arrow next to the “Run App” to choose how you want to run the app. Some features will not work when running in window or viewer pane, therefore it is recommended to run your apps in an external browser. This is done by selecting the “Run External” option.
1.4.2 Exercise
10:00
- Make sure you have got
shiny
installed in R. (Hint: If not runinstall.packages("shiny")
). - Launch a new Shiny web app. Give the application the name “test_shiny” and make sure it is a multi-file app.
- Run the app in an external window; what does the app do?
- Look in the “test_shiny” folder that was produced when you created a new Shiny app. How many files are in there, and what are they called?
- Can you see the chart without opening the app?
- What do you notice when you try to run any other code while the Shiny app is running?
1.4.2 Solution
UI
#
# This is the user-interface definition of a Shiny web application. You can
# run the application by clicking 'Run App' above.
#
# Find out more about building applications with Shiny here:
#
# http://shiny.rstudio.com/
#
library(shiny)
# Define UI for application that draws a histogram
shinyUI(fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
)
)
)
Server
#
# This is the server logic of a Shiny web application. You can run the
# application by clicking 'Run App' above.
#
# Find out more about building applications with Shiny here:
#
# http://shiny.rstudio.com/
#
library(shiny)
# Define server logic required to draw a histogram
shinyServer(function(input, output) {
output$distPlot <- renderPlot({
# generate bins based on input$bins from ui.R
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
})
Training repository: Go to Chapter 1
> Exercise-1.4.2
> test_shiny
Run either the ui.R
or server.R
file to see the app in action.
1.5 How does the Shiny app work?
1.5.1 Structure of a Shiny app
A Shiny app has two main parts: the user interface (UI), which is what people see and interact with, and the server, which does the behind-the-scenes work. You can either keep the UI and server in separate files or combine them into one.
The UI and server can share information back and forth in a limited way, using something called “inputs” and “outputs” (we’ll cover this more later!). Besides that, you need to load libraries, data, and other resources separately.
A Shiny app is self-contained, meaning all the data and charts you want to use need to be created within the app itself—you can’t bring in things generated outside of it. However, it can still access objects in your current environment, including libraries (i.e. packages), so be careful with this when building the app.
1.5.2 Loading libraries and data
In this course, we’ll create Shiny apps using two files: one for the user interface (UI) and one for the server.
The UI and server files don’t share things like libraries and data between them. If you need a library in both files, load it separately in each one. This also applies to data, though you’ll rarely need to load data in the UI file.
It’s a good habit to always write the package name with the function, using double colons (like dplyr::filter()
instead of just filter()
). This is especially helpful when working with Shiny apps, as it helps when you’re developing and hosting the app on multiple servers.
1.6 The UI (User interface)
The UI is what the user sees; inputs, outputs, structures, tabs etc etc. Objects appear on screen in the order they are written in the UI. They are contained within functions within functions, therefore be sure to keep track of all your brackets!
# Load the Shiny package
library(shiny)
# Define the UI
shinyUI(
fluidPage(
# Application title
titlePanel("Hello Shiny!"),
# Sidebar layout with input and output definitions
sidebarLayout(
# Sidebar panel for inputs
sidebarPanel(
# Input: Slider for the number of bins
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
# if you want to add another input you will need to separate it with a comma
),
# Main panel for displaying outputs
mainPanel(
# Output: Histogram
plotOutput("distPlot")
)
)
))
- the whole UI is wrapped in a
shinyUI()
call (could also useui <-
) - the
fluidPage()
function defines the content of the page - the
titlePanel()
defines the title of the page - the
sidebarLayout()
creates a basic page layout with a sidebar and main panel - the
sidebarPanel()
contains inputs e.g. radio buttons or file inputs - the
mainPanel()
contains outputs e.g. charts or tables separated by commas
1.7 The server
The server is where all the data processing takes place. Nothing in the server is shown directly to the user; it must be sent to the UI to be displayed. All of the server’s code is kept inside one function.
#Define server Logic required to draw a histogram
shinyServer(
function(input, output) {
output$distPlot <- renderPlot({
# Generate bins based on input$bins from the slider
x <- faithful$waiting
bins <- seq(min(x), max(x), length.out = input$bins + 1)
# Draw the histogram
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
})
- the whole server is wrapped in a
shinyServer()
call (could also useserver <-
) - the
function()
takes the input and output objects as arguments renderPlot()
is part of the reactive environment which produces reactive objects and outputs. These do not need to be separated by commas
1.7.1 Input and Outputs
As mentioned before, the server and UI communicate with each other through lists of three types of objects:
- Input: Objects the user creates by interacting with widgets in the UI, like sliders or dropdowns. They’re often single values or lists of values
- Output: Objects created in the server that users can see, usually things like charts or tables
- Session: Not covered in this course, but it’s useful for more advanced apps
1.8 The structure of the UI
1.8.3 Tabs
tabsetPanel()
creates a set of tabs within a main panel of any layout (likefluidPage
,navbarPage
, etc.). It allows users to switch between different sections of content easily. Providing a tab ID is optional but can be useful for analytics or shortcuts to specific tabstabPanel()
defines each individual tab within thetabsetPanel()
. Each tab can contain various UI elements such as text, plots or tables. You must provide a tab name, followed by any UI elements or objects you want to include, separated by commas
Example image of tabs:
Example code of tabs:
# Create the main panel with a tabset panel
mainPanel(
# Create a tabset panel
tabsetPanel(
id = "tabs", # Optional: Provide an ID for the tabset
# First tab for a chart
tabPanel("Chart", plotOutput("irisPlot") # Output for the plot
),
# Second tab for a table
tabPanel("Table", tableOutput("irisTable") # Output for the table
),
)
)
1.9 Exercise
15:00
Open Chapter 1 > Exercise-1.9
in the training repository and complete the following tasks:
- Change the title of your app to something different
- Add some tabs to the main panel of your app! Keep the chart in one tab, and some other text to another tab.
- Format the text in your tab so you have different level headers, bold text and some horizontal rules
Bonus
- Can you work out how to specify which tab you want to be open when the app starts for the first time?
1.9 Solution
UI
library(shiny)
shinyUI(fluidPage(
### Question 1. Change the title of the app to something different
titlePanel("My Shiny App!"),
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
### Question 2. Create two tabs, one called Chart to keep the chart in it
### and one called Text to write some text in
mainPanel(
tabsetPanel(
# Chart tab for the plot
tabPanel("Chart",
plotOutput("distPlot")),
# Text tab with formatted text
tabPanel("Text",
h2("Welcome to My Shiny App"), # Header
p("This app demonstrates a simple histogram of waiting times for eruptions of the Old Faithful geyser."),
p(strong("Adjust the number of bins"), " using the slider to see the effect on the chart.")
),
### Bonus: Set the default tab to "Chart" when the app runs
id = "tabs", selected = "Chart"
)
)
)
))
Server
library(shiny)
# Define server logic required to draw a histogram
shinyServer(function(input, output) {
output$distPlot <- renderPlot({
# generate bins based on input$bins from ui.R
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
})
Training repository: Go to Chapter 1
> Exercise-1.9
> Solution
Run either the ui.R
or server.R
file to see the app in action.
This is a good opportunity to take a 10-minute break away from the computer to refresh your mind, stretch, and reset before continuing onto Chapter 2.