Shiny

An introductory presentation and a short training

Stéphane Laurent
R user

What is Shiny ?

help(package = shiny)
Description:        Shiny makes it incredibly easy to build interactive web applications
                    with R. Automatic "reactive" binding between inputs and outputs and 
                    extensive pre-built widgets make it possible to build beautiful, 
                    responsive, and powerful applications with minimal effort.
  • Originally created by Joe Cheng
  • Delivered by RStudio company

alt text

Easy example: the standard Gauss distribution

  • Left side (sidebar panel) : inputs

  • Right side (main panel) : outputs

  • Engine : two R files, namely ui.R and server.R, generate the app, and reactivity is handled by Javascript

Goals of this presentation

  • \(\frac12\) presentation, \(\frac12\) training

  • Each participant uses its own computer with a recent version of R and peferably RStudio, as well as an appropriate browser: Google Chrome of Firefox

  • Intended public: non-proficient R users

  • Skills expected after the presentation :

    • Making an easy Shiny app (such as the Gauss app)
    • Understanding reactivity
    • Self-improvement of these skills by studying the Shiny tutorial


Warning The examples given in these slides are mainly conceived to illustrate some concepts, and don't necessarily represent best practices !

Shiny app template

ui.R :

shinyUI(pageWithSidebar(
  
  headerPanel("app title"),
  
  sidebarPanel(   
    ***SET INPUTS*** 
  ),
   
  mainPanel(    
    ***RENDER OUTPUTS***
  )
  
))
  • Inputs are elements of the input list

  • Outputs are elements of the output list

server.R :

shinyServer(function(input, output, session) {

  ***GET INPUTS AND MAKE OUTPUTS*** 
  
})
  • get input: input$title (or input[["title"]])

  • set input: not by doing input$title <- ... !!!

Inputs are set through the widgets in the interface, defined in ui.R:
textInput(inputId="title", label="Title")

  • make output: output$plot <- renderPlot({ ... })

  • render output: plotOutput("plot")

Available input widgets


  • sliderInput() doesn't work in these slides currently

  • downloadButton()






Making the app - step 1: without shiny

  • Write regular R code to plot the standard Gauss density:
curve(dnorm(x), from = -3, to = 3, 
    main = "standard Gaussian density", 
    col = "blue", lwd = 6, axes = FALSE, 
    xlab = NA, ylab = NA)
axis(1)

plot of chunk unnamed-chunk-6

NB: The curve() function is anti-pedagogical: usually dnorm(x) returns the evaluation of the standard Gauss density :

x <- c(0, 1, 2)
dnorm(x)
## [1] 0.39894 0.24197 0.05399

But dnorm(x) inside of curve() is an expression written as a function of the x variable.

Making the app - step 2: embed code in template

ui.R :

  sidebarPanel(
    textInput("title", "Title"), 
    radioButtons("color", "Color:", choices=c("blue","red","green")), 
    numericInput("linewidth", "Line width", value=6) 
  ),
  mainPanel(
    plotOutput("GAUSS")
  )

server.R :

output$GAUSS <- renderPlot({
    curve(dnorm(x), from = -3, to = 3, main = input$title, col = input$color, 
        lwd = input$linewidth, axes = FALSE, xlab = NA, ylab = NA)
    axis(1)
})

Making the app - step 3: run

  • Run by doing runApp("myappfolder") where myappfolder is the folder containing server.R and ui.R, and the current directory is the one containing myappfolder

  • If shiny is not loaded in your R session, type shiny::runApp("myappfolder")

  • In RStudio just type runApp(" and press Tab to select the folder

  • To prevent the app to be launched in the default browser, type

    runApp("myappfolder", launch.browser=FALSE)

    or, shorter, runApp("myappfolder", l=FALSE); then the app is accessible at http://localhost:xxxx where xxxx is the port given in the runApp() message:

runApp

  • Do not use an old version of Internet Explorer

What does runApp() do ?

  • The ui.R file generates the html code of the app

  • For instance, textInput("title","Title") generates the following html code:

<label for="title">Title</label>
<input id="title" type="text" value=""/>

(try textInput("title","Title") in the R console)

  • There are some html style files (css) in the shiny package folder, as well as Javascript libraries (js files)

  • Javascript handles the connection between R and the interface (the server and the client), namely reactivity; we will see in the next slides how reactivity works

  • Is it true that using Shiny does not require skills in html/Javascript ?

    • Yes, I am the proof that it's possible
    • But such skills are useful for customization

Incredible javascript visualizer: reactlog

  • Type options(shiny.reactlog=TRUE) at the beginning of server.R, or before running the app, and press Ctrl+F3 while the app is running

  • It shows the reactive objects constituting the app, and the connections between them

Reactive roles

  • There's no reactive conductor in the Gauss app, only direct connections:

Simple connections

Understanding reactivity (1/2)

  • The output object GAUSS is connected to each input object; then it is updated each time an input object is modified

  • Let us try to isolate an input:

  output$GAUSS <- renderPlot({
    curve(dnorm(x), from=-3, to=3, axes=FALSE, xlab=NA, ylab=NA, 
      main=input$title, col=isolate(input$color), lwd=input$linewidth)
    axis(1)
  })

Understanding reactivity (2/2)

What is happening with isolate(input$color) ?

  1. ${\tt input$color}\,\,\;$ is unmodifiable when we do ${\tt isolate(input$color)}\,\,\,\;$
  2. ${\tt output$GAUSS}\,\,\,\;$ is disconnected from ${\tt input$color}\,\,\,\;$
  3. ${\tt output$GAUSS}\,\,\,\;$ reads the value of ${\tt input$color}\,\,\;$ only when it reacts

Look at the reactlog !

See the Shiny tutorial !

Action button

  • We can isolate all input parameters of the plot: the user firstly selects the parameters and generates the plot by pressing (useful when modifying the inputs generates a time-consuming task)

ui.R

  actionButton(inputId="gobutton", label="Go!")

server.R

  output$GAUSS <- renderPlot({
    input$gobutton # create connection with the Go! button
    curve(dnorm(x), from=-3, to=3, axes=FALSE, xlab=NA, ylab=NA, 
      main=isolate(input$title), col=isolate(input$color), lwd=isolate(input$linewidth))
    axis(1)
  })
  • Here the content of input$gobutton has no importance: its role is to generate a value that changes at each press; actually input$gobutton initially is 0 and is incremented at each press

  • Exercise: modify the app in order that the plot first time appears only once the button has been pressed (see result in next slide)

Gauss app with an action button

Reactive conductors (1/4)

  • A reactive conductor is a reactive component lying between a source and an endpoint:

Reactive conductor

  • It is defined in server.R with the reactive({}) function

  • Reactive function is a bad synonym of reactive conductor: similarly to a regular R function, reactive({}) returns a value, but it does not work like a function

  • Illustration: consider an inefficient algorithm calculating the Fibonacci numbers:
# Calculate nth number in Fibonacci sequence
fib <- function(n) {
    ifelse(n < 3, 1, fib(n - 1) + fib(n - 2))
}

Example for \(n=26\,\):

fib(26)
## [1] 121393

the computation is slow:

system.time(fib(26))
##    user  system elapsed 
##    3.39    0.00    3.40

Reactive conductors (2/4)

Let's make an app returning the n-th Fibonacci number and its inverse:

server.R

# Calculate nth number in Fibonacci sequence
fib <- function(n) ifelse(n<3, 1, fib(n-1)+fib(n-2))

shinyServer(function(input, output) {
  output$nthValue    <- renderText({ fib(input$n) })
  output$nthValueInv <- renderText({ 1/fib(input$n) })
})

ui.R

sidebarPanel(
  numericInput("n", "Enter n:", value=6) 
),
mainPanel(
  h3("n-th Fibonacci number:"),
  textOutput("nthValue"),
  br(),
  h3("inverse n-th Fibonacci number:"),
  textOutput("nthValueInv")
)

n-th Fibonacci number:


inverse n-th Fibonacci number:

Reactive conductors (3/4)

  • The graph of the current app is:

Reactive conductor

  • The fib() function is run twice: each reactive endpoint executes fib() whenever it reacts

With a reactive conductor we can run fib() no more times than is absolutely necessary:

Reactive conductor

How ?

  • by executing fib(input$n) in the reactive conductor

  • and getting the reactive conductor output in the reactive endpoints

Reactive conductors (4/4)

old server.R :

# Calculate nth number in Fibonacci sequence
fib <- function(n) ifelse(n<3, 1, fib(n-1)+fib(n-2))

shinyServer(function(input, output) {
  output$nthValue    <- renderText({ fib(input$n) })
  output$nthValueInv <- renderText({ 1/fib(input$n) })
})


new server.R :

# Calculate nth number in Fibonacci sequence
fib <- function(n) ifelse(n<3, 1, fib(n-1)+fib(n-2))

shinyServer(function(input, output) {
  getFib <- reactive({ fib(input$n) })
  output$nthValue <- renderText({ getFib() })
  output$nthValueInv <- renderText({ 1 / getFib() })
})

Dynamic UI (1/3)

  • In the app below, user uploads a data file and select columns of the dataset

  • Thus the UI widgets (selectInput() and actionButton()) are dynamic, not static

Dynamic UI (2/3)

  • The dynamic UI is made as follows:

server.R :

output$column_selection <- renderUI({
  dat <- getFile()  ## to be explained below
  if(!is.null(dat)){
    list(
      selectInput("column_x", "Column x:", choices=names(dat)),      
      selectInput("column_y", "Column y:", choices=names(dat)),
      actionButton("gobutton", "Plot!")
    )
  }
})

ui.R :

uiOutput("column_selection")
  • File is got as a dataframe with my getFile() reactive conductor:

ui.R :

fileInput("userfile", "Upload a CSV file:")

server.R :

getFile <- reactive({
  if(!is.null(input$userfile)){
    read.csv(input$userfile$datapath)
  }
})

Dynamic UI (3/3)

And finally the plot:

server.R :

output$plot <- renderPlot({
  if(!is.null(input$gobutton)){
    dat <- getFile()
    if(input$gobutton==0) return(NULL)
    xname <- input$column_x
    yname <- input$column_y
    plot(x=dat[,xname], y=dat[,yname], xlab=xname, ylab=yname)
  }
})

ui.R :

plotOutput("plot")

Other important Shiny components

  • conditionalPanel() in ui.R: to hide/unhide something in the UI

  • observe({}): this is like reactive({}), but does not return anything

  • updateXXXInput() in server.R: to change the value of the input (updateNumericInput, updateTextInput, ...)

  • reactiveValues() in server.R: similar to a list, but to store reacting values

  • the Shiny tutorial on RStudio website (from which I plagiarized the Fibonacci example)

  • the Shiny Google group, to get some help

  • the shinyIncubator package: unofficial add-ons for Shiny

  • the shinyAce package: a cosy text area input/output

New in Shiny : Javascript tables (DataTables library)

Currently doesn't work within these slides

Javascript charts

Shiny allows to render the Javascript charts generated by the rCharts package

About these slides

  • This HTML5 slide deck has been created with Ramnath Vaidyanathan's slidify package

  • He is also the author or the rCharts package, R package to create interactive javascript visualizations from R, possibly jointly with Shiny

Acknowledgements

I am grateful to Ramnath and to the members of the Shiny Google group for their help