R shiny

OECD A/S seminar 2014

Bo Werth
Statistician STI/EAS

Motivation

+

  • access speed
  • routine operations
  • reproducibility
  • available methods
  • flexibility of presentation
  • user empowerment

-

  • development time
  • changing requirements
  • maintenance
  • transparency

tableau logo

What is Shiny?

  • Interactive web applications around your R analysis
  • Zero HTML/CSS/JavaScript knowledge is required...
  • fully customisable and extensible with HTML/CSS/JavaScript
  • Designed to integrate with existing JavaScript libraries (for visualisation)
  • Uses a reactive programming model which allows dramatically simpler code than traditional UI or web programming
  • Shiny applications are automatically "live" in the same way that spreadsheets are live. Outputs change instantly as users modify inputs, without requiring a reload of the browser

Shiny comes in two parts:

  1. the shiny R package for developing Shiny apps
  2. Shiny server for deploying them

Use Cases

  • Display data: show more data than can be displayed in one chart
  • Explore data: change mapping of variables to visual elements
  • Calculation: derive new indicators from variables in data set
  • Robustness: test sensitivity of results to changes in model parameters

shiny gallery

http://shiny.rstudio.com/gallery/

Structure of a Shiny App

Shiny apps have two components:

a user-interface script

The user-interface script ui.R controls the layout and appearance of your app. It contains:

  • layout information, e.g. default layouts, bootstrap theme, custom grid layout
  • controls for inputs into the application, e.g. selectInput, sliderInput, numericInput
  • appearance of outputs, e.g. text boxes, tables and plot size

a server script

The server-side script server.R contains the instructions that are required to build the application. It can:

  • use inputs from the ui script, e.g. for subsetting data or changing parameters
  • create reactive elements that refresh when inputs change
  • create outputs that are returned to the user interface

Reactive Sources and Endpoints

The simplest structure of a reactive program involves just a source and an endpoint:

source and endpoint

source

the source typically is user input through a browser interface (item selection, type text string, button click etc.). These actions will set values that are reactive sources. Reactive sources can signal objects downstream that they need to re-execute.

endpoint

reactive endpoints are accessible through the output object (in most cases). A reactive endpoint is usually something that appears in the user's browser window, such as a plot or a table of values. Reactive endpoints can be told to re-execute by the reactive environment, and can request upstream objects to execute.

Example App

rCharts Polyplot

rChartsPolyplot app

rCharts Polyplot

  ## ui.R
  selectInput(inputId = "country")
  ## server.R
  output$chart2 <- renderChart({
    data.country = subset(dat2m, country == input$country)
    p2 <- rPlot(value ~ year, color = 'gender', type = 'line', data = data.country)
    return(p2)
})

Reactive programming model

Reactive expressions as conductors

The amount of computation can be reduced by adding a reactive expression in between the source and endpoints:

reactive conductor

Reactive expressions are placed somewhere in between sources and endpoints on the reactive graph. They are typically used for encapsulating slow operations. Try to prevent unnecessary work, especially if the app needs to do a lot of slow computation.

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

Example App

histogram

simple histogram with slider to change the binwidth

  • data: Waiting time between eruptions and the duration of the eruption for the Old Faithful geyser in Yellowstone National Park, Wyoming, USA.
  • source: Azzalini, A. and Bowman, A. W. (1990). A look at some data on the Old Faithful geyser. Applied Statistics 39, 357–365.
  • function: seq to create histogram breaks
  • reference: http://shiny.rstudio.com/tutorial/lesson1/

histogram

histogram app

histogram

  ## ui.R
  sliderInput("bins", "Number of bins:", min = 1, max = 50, value = 30)

  plotOutput("distPlot")
  ## server.R
  bins <- reactive({
    seq(min(x), max(x), length.out = input$bins + 1)
  })

  output$distPlot <- renderPlot({
    hist(x, breaks = bins(), col = 'darkgray', border = 'white')
  })

histogram

Reactive Evaluation

When you change “Number of bins", input$bins will change and renderPlot will re-execute.

  1. renderPlot will call bins()
  2. bins will check that the bins widget has changed
  3. bins will return its new value
  4. renderPlot will re-draw the chart with the correct number of bins.

Input Controls

A range of default controls are available:

  • actionButton: Action Button
  • checkboxInput: A single check box
  • fileInput: A file upload control wizard
  • helpText: Help text that can be added to an input form
  • numericInput: A field to enter numbers
  • radioButtons: A set of radio buttons
  • selectInput: A box with choices to select from
  • sliderInput: A slider bar
  • textInput: A field to enter text

http://shiny.rstudio.com/tutorial/lesson3/

Input Controls 1

shiny widgets 1

Input Controls 2

shiny widgets 2

Example App

k-means

k-means clustering, "X" marking the center of the cluster, points coloured according to clusters

  • data: measurements in centimeters of the variables sepal length and width and petal length and width, respectively, for 50 flowers from each of 3 species of iris (Iris setosa, versicolor, and virginica).
  • source: Anderson, Edgar (1935). The irises of the Gaspe Peninsula, Bulletin of the American Iris Society, 59, 2–5.
  • function: clustering of iris data with kmeans
  • reference: Joe Cheng on RStudio Shiny http://vimeo.com/94184686

k-means

k-means app

k-means

  ## ui.R
  selectInput('xcol', 'X Variable', names(iris)),
  selectInput('ycol', 'Y Variable', names(iris), selected=names(iris)[2]),
  numericInput('clusters', 'Cluster count', 3)
  ## server.R
  selectedData <- reactive({ iris[, c(input$xcol, input$ycol)] })
  clusters <- reactive({ kmeans(selectedData(), input$clusters) })
  output$plot1 <- renderPlot({
    par(mar = c(5.1, 4.1, 0, 2.1))
    plot(selectedData(), col=clusters()$cluster)
    points(clusters()$centers, pch=4, ces=2)
  })

k-means

Reactive Evaluation

When you change “Cluster count", input$clusters will change and renderPlot will re-execute.

  1. renderPlot will call clusters()
  2. clusters will check that the clusters widget has changed
  3. clusters will return its new value
  4. renderPlot will re-draw the chart with the correct number of clusters.

Display Reactive Output

Each entry to output should contain the output of one of Shiny’s render* functions

  • renderPrint: print summary statistics etc.
  • renderPlot: base plot or lattice graphics
  • renderTable: basic table output
  • renderChart: interactive rCharts using d3.js (js)
  • renderDataTable: table with embedded controls (js)
  • renderUI a tag object or HTML
  • downloadHandler: trigger file download

The expression can be a simple statment or a complicated function call. It will re-run the expression everytime an upstream object changes. The expression must return an object (text, plot etc.) that is supported by the respective render* function.

Source: http://shiny.rstudio.com/tutorial/lesson4/

renderPrint and renderPlot 1: ICIO Barplot by Industry

industry app

renderPrint and renderPlot 1: ICIO Barplot by Industry

  ## renderPlot
  barplot(data.plot$value,
    beside = TRUE,
    names = data.plot$names, las = 2, cex.names = .8,
    ylab = unit.string,
    col = "#4F81BD")
  par(op)
  mtext(title.string, line = 2, font = 2, cex = 1.2)
  op <- par(usr=c(0,0.1,0,1.05), xpd=NA)
  legend(0, 1.1, legend=data.sort, cex=0.8, box.col = NA, horiz = TRUE)

renderPrint and renderPlot 1: ICIO Barplot by Industry

  ## renderPrint
  blurb.string <- paste0(
    as.character(blurb$couS), ' ', tolower(blurb$coef), ', in source industry, \n',
    'generated by ', blurb$couD, ' ', tolower(blurb$demand), ' of \n',
    blurb$couX1, ' exports of ', blurb$indX, ', \n',
    blurb$year, '.')
  blurb.string.template <- paste0('<couS><coef>, in source industry,\n',
    'generated by <couD><demand> of \n',
    '<couX> exports of <indX>, \n',
    '<year>.')

renderPrint and renderPlot 2: ICIO Selection Matrix

industry app

renderPrint and renderPlot 2: ICIO Selection Matrix

  ## renderPlot
  x <- data.conv1
  x <- x[, rev(seq_len(ncol(x)))]
  xLabels <- rownames(x)
  yLabels <- colnames(x)
  i <- image(1:length(xLabels), 1:length(yLabels), x,
   col = c("white", "green"),
   xlab = "Export Country + Demand or Final Expenditure Industry (couX + indX)",
   ylab = "Demand Country (couD)",
   axes = FALSE)
  return(i)

renderPrint and renderPlot 3: STAN R&D

industry app

renderTable and renderChart: LFS Share

industry app

industry app

renderDataTable: STAN Indicators

industry app

downloadHandler: SDMX Browser

industry app

industry app

downloadHandler: STAN R&D

industry app

R Calculations

  • shiny requires a running R session
  • creation of self-service tools for statistical analysis
  • straight-forward use of functions from existing packages
  • beneficial to create own package(s) from scripts early
  • installation on external server from source code platforms

function binding

Example App

STAN Indicators

  • define formula: IPYE = VALK / EMPN / (VALK_2005 / EMPN_2005)
  • parse formula: remove non-alphanumeric characters: [^a-zA-Z0-9]
  • subset data to variables: subset(data,var%in%c("VALU","EMPN","VALK"))
  • pivot with denominators: VALU|EMPN|VALK|VALU_2005|EMPN_2005|VALK_2005
  • calculate IPYE according to formula

STAN Indicators

industry app

Example App

ICIO Foreign Demand Domestic Value Added

  • select dimensions
  • subset data from multidimensional arrays
  • perform calculations:
  data.couX.indX <- data.conv1 * data.demand
  aaa <- xB %*% data.couX.indX
  aaa <- apply(aaa, 1, sum)
  • aggregate and display results (table, barchart, map)

ICIO Foreign Demand Domestic: couD, couX, indX

industry app

ICIO Foreign Demand Domestic Value Added: calculation + aggregation

industry app

ICIO Foreign Demand Domestic Value Added: aceEditor

industry app

Integrating Applications

Radiant

Perform various tasks on the same dataset

  • customize UI to make best use of a single R function
  • optimized outputs are returned from function calls

“There are only two hard things in Computer Science: cache invalidation and naming things.” Phil Karlton

Requires systematic structure:

  • naming conflicts: prepend objects and functions with application id
  • common data across applications: reduce memory requirements
  • generalize processing and and exporting of results
  • flexibly expand and shrink functionality (testing, production)
  ## radiant.R
  statPanel <- function(fun_name, rfun_label, fun_label, fun_tabs) {
    sum_name <- paste0("summary_", fun_label)
    plot_name <- paste0("plots_", fun_label)
    output[[sum_name]] <- renderPrint({
      result <- get(rfun_label)()
      get(sum_name)()
    })
    output[[plot_name]] <- renderPlot({
      result <- get(rfun_label)()
      get(plot_name)()
    })
    string.tabPanel <- paste0('tabPanel("Plots", plotOutput(plot_name, height = "100%"),\n',
      'verbatimTextOutput(sum_name))')
    string.tabsetPanel <- paste0(string.tabsetPanel, string.tabPanel, sep = ',\n')
    return(eval(parse(text=paste0('tabsetPanel(id="tabs_',fun_label,'",\n',string.tabsetPanel,')'))))}

Radiant: Data

industry app

Radiant: Visualize

industry app

Radiant: Report

industry app

  rmd_example <- "## Sample report in Markdown
  This is an example report in markdown. You can embed R code to be executed.
  '''{r}
  rnorm(5)
  x <- 14*3
  '''
  ### Inline code
  Inline R code, e.g., the value of x is 'r x'.

  ### Math
  LaTeX math:  $f(\\\\alpha, \\\\beta) \\\\propto x^{\\\\alpha-1}(1-x)^{\\\\beta-1}$.

  ### Figures
  '''{r}
  hist(rnorm(100))
  '''
  "
  ## Radiant report UI
  output$report <- renderUI({
    div(class="row-fluid", 
      div(class="span6",
        aceEditor("rmd_report", mode="markdown", value=state_init("rmd_report",rmd_example)),
        ## vimKeyBinding=vimKeyBinding),
        actionButton("evalRmd", "Update"),
        downloadButton('saveHTML', 'Save HTML'),
        downloadButton('saveRmd', 'Save Rmd'), tags$br(), tags$br(),
        fileInput('loadRmd', 'Load Rmd', multiple=TRUE)),

      div(class="span6",
        htmlOutput("rmd_knitDoc"))
      )
  })
  ## Radiant report functions
  output$rmd_knitDoc <- renderUI({
    if(is.null(input$evalRmd) || input$evalRmd == 0) return()
      isolate({
        if(running_local && input$rmd_report != "") {
          return(HTML(paste(knit2html(text = input$rmd_report, fragment.only = TRUE, quiet = TRUE), 
            '<script>', 'MathJax.Hub.Typeset();', '</script>', 
            sep = '\n')))
        }})
  })

  output$saveHTML <- downloadHandler(
    filename = function() {"report.html"},
    content = function(file) {
     isolate({
      html <- knit2html(text = input$rmd_report, quiet = TRUE, 
        options=c("mathjax", "base64_images"))
      cat(html, file = file, sep = '\n')
    })})

Advanced Example

SDMX Browser

  • connecting to Java library from R
  • obtain data flows from provider, its dimensions and select codes interacetively
  • send SDMX query to retrieve values
  • transform returned time series into table format, create basic plots and download information

SDMX logo

SDMX GitHub Repository

RJSDMX sdmxHelp()

industry app

  ## Provider
  output$uisB_provider <- renderUI({
    ui.sdmxbrowser_provider <- getProviders()
    selectInput("sdmxbrowser_provider", "Provider:", ui.sdmxbrowser_provider,
      selected = state_init_list("sdmxbrowser_provider","EUROSTAT", ui.sdmxbrowser_provider),
      multiple = FALSE)
  })
  ## Flow
  sdmxbrowser_provider <- input$sdmxbrowser_provider
  if (input$sdmxbrowser_flow_updateButton != 0) {
    load("data/data_init/sdmxBrowser.rda")
  }
  ui.sdmxbrowser_flow <- ui.sdmxBrowser.flows.list[[sdmxbrowser_provider]]
  selectInput("sdmxbrowser_flow", "Flow:", c("", ui.sdmxbrowser_flow),
    selected = "", multiple = FALSE)
  ## Dimensions
  .sdmxbrowser_dimensions_all <- reactive({
    sdmxbrowser_dimensions_all<- names(getDimensions(input$sdmxbrowser_provider,
      input$sdmxbrowser_flow))
    return(sdmxbrowser_dimensions_all)
  })
  output$uisB_dimensions <- renderUI({
    if (input$sdmxbrowser_flow=="") return()
      sdmxbrowser_dimensions_all <- .sdmxbrowser_dimensions_all()
    selectInput("sdmxbrowser_dimensions", "Filter Dimensions:", sdmxbrowser_dimensions_all,
      selected = sdmxbrowser_dimensions_all,
      multiple = TRUE, selectize = FALSE)
  })
  ## Dimension Codes
  .sdmxbrowser_dimensioncodes <- reactive({
    sdmxbrowser_dimensions <- input$sdmxbrowser_dimensions
    sdmxbrowser_dimensioncodes <- as.list(sdmxbrowser_dimensions)
    sdmxbrowser_dimensioncodes <- sapply(sdmxbrowser_dimensioncodes,function(x) NULL)
    names(sdmxbrowser_dimensioncodes) <- sdmxbrowser_dimensions
    return(sdmxbrowser_dimensioncodes)
  })
  output$uisB_dimensioncodes <- renderUI({
    for (d in seq(along = sdmxbrowser_dimensions)) {
      command <- paste0('selectInput("sdmxbrowser_dimensioncodes_', d,
        '", "Select ', sdmxbrowser_dimensions[d],
        ':", c("',
          gsub(', ', '", "', toString(sdmxbrowser_dimensioncodes[[d]])),
          '"), selected = "', sdmxbrowser_dimensioncodes[[d]][1], 
      '", multiple = TRUE, selectize = TRUE)')
    }
    command.all <- paste(command.all, command, sep = ",")
  })

SDMX Browser: DataTables

industry app

SDMX Browser: Plots

industry app

Going Public - Server Hosting

RStudio server: shinyapps.io

  • free beta
  • no maintenance
  • good performance
  • intuitive interface
  • professional subscriptions coming soon

Self-hosting on cloud server:

  • Microsoft Azure (or: Amazon EC2, Google...)
  • Spec: 4-core, 2GB RAM: 1300 Euros p.a.
  • RedHat Server license: 500 Euros p.a.

System Architecture

How to get started

Questions?