Introduction to Shiny

Laura Biggins

02/12/2020

R packages required

Shiny Cheatsheet

https://shiny.rstudio.com/images/shiny-cheatsheet.pdf

Example Shiny app

A live version of this app can be accessed here

Another example app

UI and Server

There are two parts to a Shiny application

Skeleton Shiny code



library(shiny)

ui <- fluidPage(
  
)

server <- function(input, output, session) {
  
}

shinyApp(ui, server)

Simple app code

library(shiny)

ui <- fluidPage(
  
  titlePanel("Simple app"),
  
  radioButtons(inputId = "letter_choice", 
              label    = "select a letter", 
              choices  = LETTERS[1:5]),
  numericInput(inputId = "number_choice",
               label   = "type in a number",
               value   = 4),
  textOutput(outputId = "message")
)

server <- function(input, output) {
  
  output$message <- renderText({
    paste0("You selected the letter ", input$letter_choice, 
          " and the number ", input$number_choice)
  })
}

shinyApp(ui, server)

UI code


ui <- fluidPage(
  
  titlePanel("Simple app"),
  
  radioButtons(inputId = "letter_choice", 
              label    = "select a letter", 
              choices  = LETTERS[1:5]),
  
  numericInput(inputId = "number_choice",
               label   = "type in a number",
               value   = 4),
  
  textOutput(outputId = "message")
)

Inputs

ui <- fluidPage(  
  radioButtons(inputId = "letter_choice", label = "choose a letter", ...),
  numericInput(inputId = "number_choice", label = "", ...)
)
server <- function(input, output) {
  ...
}  

Inputs

ui <- fluidPage(  
  radioButtons(inputId = "letter_choice", ...),
  numericInput(inputId = "number_choice", ...)
)
server <- function(input, output) {
  ...
}  

The input object is a special type of named list.
From within the server code, the values of the input object can be accessed in the same way as list values.

input$letter_choice
"B"


input[["letter_choice"]]
"B"
input$number_choice
4


input[["number_choice"]]
4

Server code

Accessing and using the input values

input$letter_choice
"B"
input$number_choice
4


server <- function(input, output {
  
  output$message <- renderText({
    
    paste0("You selected the letter ", input$letter_choice, 
          " and the number ", input$number_choice)
  })
}

Outputs

ui <- fluidPage(  
  ...,
  textOutput(outputId = "message")
)

Output in the simple app

Output function in UI code pairs with a render function in the server code.
Within the render function are instructions telling Shiny what to show the user.

ui <- fluidPage(...,
  textOutput(outputId = "message")
)
server <- function(input, output, session) {
  output$message <- renderText({
    paste0("You selected the letter ", input$letter_choice, 
           " and the number ", input$number_choice)
  })
}  

Recap

library(shiny)
ui <- fluidPage(
  
  titlePanel("Simple app"),
  radioButtons(inputId = "letter_choice", 
              label    = "select a letter", 
              choices  = LETTERS[1:5]),
  numericInput(inputId = "number_choice",
               label   = "type in a number",
               value   = 4),
  textOutput(outputId = "message")
)

server <- function(input, output, session) {
  
  output$message <- renderText({
    
    paste0("You selected the letter ", input$letter_choice, 
          " and the number ", input$number_choice)
  })
}
shinyApp(ui, server)

Running a shiny application


Inserting code snippet



Save file as app.R within a folder named as the application name.
Each of these folders contains an app.R file.

Exercise 1

1.1 Modifying an existing application

a. Open the app.R file for Exercise 1.1 in RStudio and run the application.
b. Complete the tasks set out in the comments at the top of the script. These include setting default text, adding a label and changing the step size on the slider.

1.2 Creating a new Shiny app

Create a Shiny application from scratch that looks (and functions) like the one below.
To get started use the shortcut provided by RStudio - start typing shinyapp to autofill a shiny code snippet.

  • There should be 2 textInput fields for entering user names, and one numericInput for entering height
  • The message at the bottom should display the first and last names entered by the user, along with their height

[Optional]
  • Add some radio buttons to allow users to select age brackets
  • Change the height selector to a slider
  • Feel free to add extra info fields if you have time

Inputs

A live version of this app, alongside the code used to create it can be accessed here

Outputs

A live version of this app, alongside the code used to create it can be accessed here

Layouts

ui <- fluidPage(
  
  titlePanel("Simple app"),
  
  radioButtons(inputId = "letter_choice", 
              label    = "select a letter", 
              choices  = LETTERS[1:5]),
  
  numericInput(inputId = "number_choice",
               label   = "type in a number",
               value   = 4),
  
  textOutput(outputId = "message")
)

Layout examples

A live version of this app can be accessed here

Exercises 2 and 3

For each exercise open the app.R file and follow the instructions set out in the comments at the top of the script.

2 Adding a plot and using layouts

3 Modifying inputs and outputs

Importing datasets - fileInput()

Unlike other inputs, fileInput() returns a data frame with 4 columns

ui <- fluidPage(
  
  titlePanel("Uploading files"),
  fileInput(inputId = "upload", 
            label = "Upload...",),
  tableOutput("files")
)
server <- function(input, output) {
  
  output$files <- renderTable(input$upload)
}


default max file size is 5MB

# allow file up to 10MB to be uploaded
options(shiny.maxRequestSize = 10 * 1024^2) 

Importing datasets - fileInput()


library(shiny)
library(readr)

options(shiny.maxRequestSize = 10 * 1024^2) 

ui <- fluidPage(
  titlePanel("Uploading files"),
  fileInput(inputId = "upload", 
            label = "Upload...",),
  tableOutput("files")
)

server <- function(input, output) {
  
  output$files <- renderTable({
    req(input$upload)
    dataset <- read_csv(input$upload$datapath)
    head(dataset)
  })
}

shinyApp(ui = ui, server = server)

fileInput() summary

fileInput() returns a data frame of 4 columns

input$id$datapath 

Access the filepath.

options(shiny.maxRequestSize = 10 * 1024^2) 

Increase max upload size (to 10MB).

req(input$id) 

Check a file has been chosen.

File validity

fileInput(inputId, label, 
          accept = ".csv")

Use the accept parameter in fileInput() to limit files shown in browser.

req(input$file)
ext <- tools::file_ext(input$file$name)
validate(need(ext == "csv", 
              "Please upload a csv file"))
read_csv(input$file$datapath)

The accept argument is only a suggestion to the file browser.
Use further checks to validate file type.

Pre-processing datasets

Data can be imported, manipulated if necessary and saved as R data objects (.Rds or .RData). This can be done in a pre-processing script to allow the data to simply be loaded within the Shiny app.

Saving and loading one file

# in pre-processing script
tree_data <- read_csv("tree_measurements.csv")
saveRDS(tree_data, file = "data/tree_data.Rds")
# in app.R script
tree_data <- readRDS("data/tree_data.Rds")

Saving a named list

tree_list  <- list(
  "beech" = beech_trees,
  "oak" =  oak_trees)
saveRDS(tree_list, file = "data/tree_list.Rds")
# in app.R script
tree_data <- readRDS("data/tree_list.Rdata") 
tree_data$beech # to access the beech dataset
tree_data$oak   # to access the oak dataset

Saving more than one dataset in .Rdata

# in pre-processing script
beech_trees <- read_csv("beech_tree_measurements.csv")
oak_trees <- read_csv("oak_tree_measurements.csv")
save(beech_trees, oak_trees, file = "data/tree_data.Rdata")
# in app.R script
load("data/tree_data.Rdata") # loads the 2 datasets beech_trees and oak_trees

Debugging

showcase mode

# to run the app in showcase mode which 
# displays code alongside the app
runApp(…, display.mode= “showcase”)

browser

# in the UI code
actionButton("browser", "browser")

# in the server code
 observeEvent(input$browser, browser())

Exercise 4

Fixing bugs and making modifications

Open the app.R file for Exercise 4 and follow the instructions set out in the comments at the top of the script.
The app will not work initially as there are some bugs in the code.
The first task will be to debug the script to enable the app to work, before making some further modifications.

Exercise 5

Uploading a file.

Create an app that looks like this one. It should:
- allow a csv file to be chosen and uploaded
- display the first few rows of data in a table
- have an option to change the number of rows displayed

Tidyverse syntax

head(iris)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
        5.1         3.5          1.4         0.2  setosa
        4.9         3.0          1.4         0.2  setosa
        4.7         3.2          1.3         0.2  setosa
        4.6         3.1          1.5         0.2  setosa
        5.0         3.6          1.4         0.2  setosa
        5.4         3.9          1.7         0.4  setosa

# base R
iris[iris$Sepal.Length > 7 & iris$Petal.Width < 2, ]

# tidyverse
iris %>%
  filter(Sepal.Length > 7 & Petal.Width < 2)

# data masking -> minimises repetition

Tidyverse with Shiny

var1 <- "Sepal.Length"
var2 <- "Petal.Width"
head(iris, n = 3)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
        5.1         3.5          1.4         0.2  setosa
        4.9         3.0          1.4         0.2  setosa
        4.7         3.2          1.3         0.2  setosa

base R

tidyverse

iris[iris$var1 > 7 & iris$var2 < 2, ]
iris %>%
  filter(var1 > 7 & var2 < 2)

Tidyverse with Shiny

var1 <- "Sepal.Length"
var2 <- "Petal.Width"
head(iris, n = 3)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
        5.1         3.5          1.4         0.2  setosa
        4.9         3.0          1.4         0.2  setosa
        4.7         3.2          1.3         0.2  setosa

base R

tidyverse

X

iris[iris$var1 > 7 & iris$var2 < 2, ]
iris %>%
  filter(var1 > 7 & var2 < 2)

iris[iris[[var1]] > 7 & iris[[var2]] < 2, ]
iris %>%
  filter(.data[[var1]] > 7 & .data[[var2]] < 2)

Shiny with base R

Shiny with tidyverse

plot(x = iris[[input$x_attribute]],
     y = iris[[input$y_attribute]])
iris %>%
    ggplot(aes(x = .data[[input$x_attribute]],
               y = .data[[input$y_attribute]])) +
    geom_point()

Tidyverse with Shiny


.data[[input$var]]


An example of an app using tidyverse and base R can be found here

Customising the view of an app

UI code in a Shiny app consists of functions to create html.

HTML

titlePanel("Choosing letters")
<h2>Choosing letters</h2>

HTML

radioButtons(inputId = "letter_choice", 
             label    = "select a letter", 
             choices  = LETTERS[1:2])
<div id="letter_choice" class="form-group shiny-input-radiogroup shiny-input-container">
  <label class="control-label" for="letter_choice">select a letter</label>
  <div class="shiny-options-group">
  <div class="radio">
  <label>
  <input type="radio" name="letter_choice" value="A" checked="checked"/>
  <span>A</span>
  </label>
  </div>
  <div class="radio">
  <label>
  <input type="radio" name="letter_choice" value="B"/>
  <span>B</span>
  </label>
  </div>
  </div>
  </div>
textOutput(outputId = "message")
<div id="message" class="shiny-text-output"></div>

Static HTML elements

HTML elements usually consist of a start and end tag, with the content inserted between the two tags. Shiny provides helper functions for creating the most common HTML elements

Wrapper functions for HTML tags

Header functions

Some of the helper functions such as the header tags are very simple.

h1(), h2(), h3(), h4(), h5(), h6() 

are functions for turning a piece of text into a title or header, h1 being the top level heading, h2 a subheader, h3 the next level subheader etc.

h1()

is a wrapper function for generating the <h1> html tag

h1("Simple app")

produces the following html:

<h1>Simple app</h1>

The other header tags (h2 - h6) work in a similar way.

HTML

http://www.bioinformatics.babraham.ac.uk/shiny/Intro_to_Shiny_course/examples/07_static_html/

Shiny themes

The package shinythemes provides a range of themes to change the appearance of your Shiny app.

Exercise 6

http://www.bioinformatics.babraham.ac.uk/shiny/Intro_to_Shiny_course/exercises/06_iris_boxplot/