Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dashboards & apps with Shiny

Dashboards & apps with Shiny

Presentation at R-Ladies Philly

Mine Cetinkaya-Rundel

October 10, 2018
Tweet

More Decks by Mine Cetinkaya-Rundel

Other Decks in Education

Transcript

  1. Mine Çetinkaya-Rundel @ mine-cetinkaya-rundel
    [email protected]
    @minebocek
    Dashboards Apps
    & with

    View Slide

  2. Workshop materials
    Cloud
    OPTION 1
    bit.ly/shiny-rlphl
    OPTION 2
    bit.ly/shiny-rlphl-git
    1. clone or download
    log in and sit tight
    2. launch rladies-phl-shiny.Rproj

    View Slide

  3. Meet & greet
    Mine Çetinkaya-Rundel
    Associate Professor, Duke Statistical Science
    Data Scientist & Professional Educator, RStudio

    View Slide

  4. Overview
    - 01 - Building dashboards with flexdashboard
    - 02 - Getting started with shiny
    - 03 - Understanding reactivity
    - Lots of info!
    - Lots of “your turn” breaks

    View Slide

  5. 01
    Building dashboards
    with flexdashboard

    View Slide

  6. DEMO
    gallery.shinyapps.io/un-women-dash

    View Slide

  7. Dashboards
    - Built in layouts and UI elements
    - Good venue for displaying automatically updating data
    - May or may not be interactive

    View Slide

  8. UI
    - Static:
    - R code runs once and generates an HTML page
    - Generation of this HTML can be scheduled
    - Dynamic:
    - Client web browser connects to an R session running on server
    - User input causes server to do things and send information back to client
    - Interactivity can be on client and server
    - Can update data in real time
    - User potentially can do anything that R can do

    View Slide

  9. Building a dashboard

    View Slide

  10. ---
    title: "UN Women Stats Explorer"
    output:
    flexdashboard::flex_dashboard:
    orientation: rows
    social: menu
    source_code: https://github.com/mine-cetinkaya-rundel/rladies-phl-shiny/blob/master/01-flexdash/un-
    women-dash.Rmd
    runtime: shiny
    ---
    1. Set up the YAML

    View Slide

  11. 2. Pick a layout

    View Slide

  12. 3. Use R Markdown and/or Shiny code to add components
    selectInput(inputId = "x", label = "X-axis",
    choices = c("Average number of hours spent on unpaid domestic
    and care work"
    = "hrs_unpaid_dom_care_work",
    "Average number of hours spent on paid and unpaid
    domestic and care work combined"
    = "hrs_dom_care_work",...),
    selected = "labor_force")
    renderPlot({
    ggplot(data = sel_data(),
    mapping = aes_string(x = input$x, y = input$y, color = "region")
    geom_point(size = 2, alpha = 0.8) +
    theme_minimal() +
    labs(x = xlab(), y = ylab(), color = "Region")
    })

    View Slide

  13. Your turn
    - Open un-women-dash.Rmd
    - Change the default selection of years to the min_year to 2014
    - Run the app
    - Select view mode in the drop down menu next to Run App to
    Preview in Viewer Pane
    - Rerun the app
    sliderInput(inputId = "year", label = "Year",
    min = min_year, max = max_year,
    value = c(2001, max_year), step = 1, sep = "")

    View Slide

  14. 02
    Getting started
    with shiny

    View Slide

  15. High level view

    View Slide

  16. Every Shiny app has a webpage that the user visits,
    and behind this webpage there is a computer
    that serves this webpage by running R.

    View Slide

  17. When running your app locally,
    the computer serving your app is your computer.

    View Slide

  18. When your app is deployed,
    the computer serving your app is a web server.

    View Slide

  19. User interface
    HTML
    Server instructions

    View Slide

  20. Anatomy of a
    Shiny app

    View Slide

  21. What’s in an app?
    library(shiny)
    ui <- fluidPage()
    server <- function(input, output) {}
    shinyApp(ui = ui, server = server)
    User interface
    controls the layout and
    appearance of app
    Server function
    contains instructions
    needed to build app

    View Slide

  22. NHANES::NHANES
    Data from the 2009 - 2010 and 2011 - 2012 surveys on
    10,000 participants and 76 variables collected on them

    View Slide

  23. View Slide

  24. App template
    library(shiny)
    library(tidyverse)
    library(NHANES)
    ui <- fluidPage()
    server <- function(input, output) {}
    shinyApp(ui = ui, server = server)

    View Slide

  25. User interface

    View Slide

  26. # Define UI
    ui <- fluidPage(
    # Sidebar layout with a input and output definitions
    sidebarLayout(
    # Inputs: Select variables to plot
    sidebarPanel(
    # Select variable for y-axis
    selectInput(inputId = "y", label = "Y-axis:",
    choices = c("Age", "Poverty", "Pulse", "AlcoholYear", "BPSysAve"),
    selected = "BPSysAve"),
    # Select variable for x-axis
    selectInput(inputId = "x", label = "X-axis:",
    choices = c("Age", "Poverty", "Pulse", "AlcoholYear", "BPSysAve"),
    selected = "BPDiaAve")
    ),
    # Output: Show scatterplot
    mainPanel(
    plotOutput(outputId = "scatterplot")
    )
    )

    View Slide

  27. # Define UI
    ui <- fluidPage(
    # Sidebar layout with a input and output definitions
    sidebarLayout(
    # Inputs: Select variables to plot
    sidebarPanel(
    # Select variable for y-axis
    selectInput(inputId = "y", label = "Y-axis:",
    choices = c("Age", "Poverty", "Pulse", "AlcoholYear", "BPSysAve"),
    selected = "BPSysAve"),
    # Select variable for x-axis
    selectInput(inputId = "x", label = "X-axis:",
    choices = c("Age", "Poverty", "Pulse", "AlcoholYear", "BPDiaAve"),
    selected = "BPDiaAve")
    ),
    # Output: Show scatterplot
    mainPanel(
    plotOutput(outputId = "scatterplot")
    )
    )
    )
    Create fluid page layout

    View Slide

  28. # Define UI
    ui <- fluidPage(
    # Sidebar layout with a input and output definitions
    sidebarLayout(
    # Inputs: Select variables to plot
    sidebarPanel(
    # Select variable for y-axis
    selectInput(inputId = "y", label = "Y-axis:",
    choices = c("Age", "Poverty", "Pulse", "AlcoholYear", "BPSysAve"),
    selected = "BPSysAve"),
    # Select variable for x-axis
    selectInput(inputId = "x", label = "X-axis:",
    choices = c("Age", "Poverty", "Pulse", "AlcoholYear", "BPDiaAve"),
    selected = "BPDiaAve")
    ),
    # Output: Show scatterplot
    mainPanel(
    plotOutput(outputId = "scatterplot")
    )
    )
    )
    Create a layout with a
    sidebar and main area

    View Slide

  29. # Define UI
    ui <- fluidPage(
    # Sidebar layout with a input and output definitions
    sidebarLayout(
    # Inputs: Select variables to plot
    sidebarPanel(
    # Select variable for y-axis
    selectInput(inputId = "y", label = "Y-axis:",
    choices = c("Age", "Poverty", "Pulse", "AlcoholYear", "BPSysAve"),
    selected = "BPSysAve"),
    # Select variable for x-axis
    selectInput(inputId = "x", label = "X-axis:",
    choices = c("Age", "Poverty", "Pulse", "AlcoholYear", "BPDiaAve"),
    selected = "BPDiaAve")
    ),
    # Output: Show scatterplot
    mainPanel(
    plotOutput(outputId = "scatterplot")
    )
    )
    )
    Create a sidebar panel containing
    input controls that can in turn be
    passed to sidebarLayout

    View Slide

  30. # Define UI
    ui <- fluidPage(
    # Sidebar layout with a input and output definitions
    sidebarLayout(
    # Inputs: Select variables to plot
    sidebarPanel(
    # Select variable for y-axis
    selectInput(inputId = "y", label = "Y-axis:",
    choices = c("Age", "Poverty", "Pulse", "AlcoholYear", "BPSysAve"),
    selected = "BPSysAve"),
    # Select variable for x-axis
    selectInput(inputId = "x", label = "X-axis:",
    choices = c("Age", "Poverty", "Pulse", "AlcoholYear", "BPDiaAve"),
    selected = "BPDiaAve")
    ),
    # Output: Show scatterplot
    mainPanel(
    plotOutput(outputId = "scatterplot")
    )
    )
    )

    View Slide

  31. # Define UI
    ui <- fluidPage(
    # Sidebar layout with a input and output definitions
    sidebarLayout(
    # Inputs: Select variables to plot
    sidebarPanel(
    # Select variable for y-axis
    selectInput(inputId = "y", label = "Y-axis:",
    choices = c("Age", "Poverty", "Pulse", "AlcoholYear", "BPSysAve"),
    selected = "BPSysAve"),
    # Select variable for x-axis
    selectInput(inputId = "x", label = "X-axis:",
    choices = c("Age", "Poverty", "Pulse", "AlcoholYear", "BPDiaAve"),
    selected = "BPDiaAve")
    ),
    # Output: Show scatterplot
    mainPanel(
    plotOutput(outputId = "scatterplot")
    )
    )
    )
    Create a main panel containing
    output elements that get created
    in the server function can in turn
    be passed to sidebarLayout

    View Slide

  32. Server

    View Slide

  33. # Define server function
    server <- function(input, output) {
    # Create the scatterplot object the plotOutput function is expecting
    output$scatterplot <- renderPlot({
    ggplot(data = NHANES, aes_string(x = input$x, y = input$y)) +
    geom_point()
    })
    }

    View Slide

  34. # Define server function
    server <- function(input, output) {
    # Create the scatterplot object the plotOutput function is expecting
    output$scatterplot <- renderPlot({
    ggplot(data = NHANES, aes_string(x = input$x, y = input$y)) +
    geom_point()
    })
    }
    Contains instructions
    needed to build app

    View Slide

  35. # Define server function
    server <- function(input, output) {
    # Create the scatterplot object the plotOutput function is expecting
    output$scatterplot <- renderPlot({
    ggplot(data = NHANES, aes_string(x = input$x, y = input$y)) +
    geom_point()
    })
    }
    Renders a reactive plot that is
    suitable for assigning to an
    output slot

    View Slide

  36. # Define server function
    server <- function(input, output) {
    # Create the scatterplot object the plotOutput function is expecting
    output$scatterplot <- renderPlot({
    ggplot(data = NHANES, aes_string(x = input$x, y = input$y)) +
    geom_point()
    })
    }
    Good ol’ ggplot2 code,
    with inputs from UI

    View Slide

  37. UI + Server

    View Slide

  38. # Create the Shiny app object
    shinyApp(ui = ui, server = server)

    View Slide

  39. DEMO
    nhanes-01.R
    Putting it all together…

    View Slide

  40. Your turn
    - Add new select menu to color the points by
    - inputId = "z"
    - label = "Color by:"
    - choices = c("Gender", "Depressed", "SleepTrouble",
    "SmokeNow", "Marijuana")
    - selected = "SleepTrouble"
    - Use this variable in the aesthetics of the ggplot function as the color argument
    to color the points by
    - Run the app in the Viewer Pane
    - Compare your code / output with the person sitting next to / nearby you

    View Slide

  41. SOLUTION
    Solution to the previous exercise
    nhanes-02.R

    View Slide

  42. Inputs

    View Slide

  43. Your turn
    - Add new input variable to control the alpha level of the points
    - This should be a sliderInput
    - See shiny.rstudio.com/reference/shiny/latest/ for help
    - Values should range from 0 to 1
    - Set a default value that looks good
    - Use this variable in the geom of the ggplot function as the alpha argument
    - Run the app in a new window
    - Compare your code / output with the person sitting next to / nearby you

    View Slide

  44. SOLUTION
    Solution to the previous exercise
    nhanes-03.R

    View Slide

  45. Outputs

    View Slide

  46. ?
    Which render* and *Output function duo is used to add
    this table to the app?

    View Slide

  47. library(shiny)
    library(tidyverse)
    library(NHANES)
    ui <- fluidPage(
    DT::dataTableOutput()
    )
    server <- function(input, output) {
    DT::renderDataTable()
    }
    shinyApp(ui = ui, server = server)

    View Slide

  48. Your turn
    - Create a new output item using DT::renderDataTable.
    - Show first seven columns of NHANES data, show 10 rows at a time, and hide row
    names, e.g.
    - data = NHANES[, 1:7]
    - options = list(pageLength = 10)
    - rownames = FALSE
    - Add a DT::dataTableOutput to the main panel
    - Run the app in a new Window
    - Compare your code / output with the person sitting next to / nearby you
    - Stretch goal: Make the number of columns visible in the table a user defined input

    View Slide

  49. SOLUTION
    Solution to the previous exercise
    nhanes-04.R

    View Slide

  50. Execution

    View Slide

  51. Where you place code in your app will determine how many times they
    are run (or re-run), which will in turn affect the performance of your app,
    since Shiny will run some sections your app script more often than others.
    library(shiny)
    library(tidyverse)
    library(NHANES)
    ui <- fluidPage(
    ...
    )
    server <- function(input, output) {
    output$x <- renderPlot({
    ...
    })
    }
    shinyApp(ui = ui, server = server)
    Run once
    when app is
    launched

    View Slide

  52. library(shiny)
    library(tidyverse)
    library(NHANES)
    ui <- fluidPage(
    ...
    )
    server <- function(input, output) {
    output$x <- renderPlot({
    ...
    })
    }
    shinyApp(ui = ui, server = server)
    Run once
    each time a user
    visits the app

    View Slide

  53. library(shiny)
    library(tidyverse)
    library(NHANES)
    ui <- fluidPage(
    ...
    )
    server <- function(input, output) {
    output$x <- renderPlot({
    ...
    })
    }
    shinyApp(ui = ui, server = server)
    Run once
    each time a user
    changes a widget that
    output$x depends on

    View Slide

  54. 03
    Understanding
    reactivity

    View Slide

  55. Reactivity 101

    View Slide

  56. Reactions
    The input$ list stores the current value of each input object under its name.
    # Set alpha level
    sliderInput(inputId = "alpha",
    label = "Alpha:",
    min = 0, max = 1,
    value = 0.5)
    input$alpha
    input$alpha = 0.2
    input$alpha = 0.5
    input$alpha = 0.8

    View Slide

  57. Reactivity 101
    Reactivity automatically occurs when an input value
    is used to render an output object
    # Define server function required to create the scatterplot
    server <- function(input, output) {
    # Create the scatterplot object the plotOutput function is expecting
    output$scatterplot <- renderPlot(
    ggplot(data = NHANES, aes_string(x = input$x, y = input$y,
    color = input$z)) +
    geom_point(alpha = input$alpha)
    )
    }

    View Slide

  58. Reactive flow

    View Slide

  59. DEMO
    Suppose you want the option to plot only
    certain education level(s) as well as report how
    many such participants are plotted:
    1. Add a UI element for the user to select which
    education level(s) they want to plot
    2. Filter for chosen education level(s) and save
    as a new (reactive) expression
    3. Use new data frame (which is reactive) for
    plotting
    4. Use new data frame (which is reactive) also
    for reporting number of observations

    View Slide

  60. # Select which education level(s) to plot
    checkboxGroupInput(inputId = "education",
    label = "Select education level(s):”,
    choices = levels(NHANES$Education),
    selected = "College Grad")
    1. Add a UI element for the user to select which
    education level(s) they want to plot

    View Slide

  61. # Server
    # Create a subset of data filtering for chosen education level(s)
    NHANES_subset <- reactive({
    req(input$education)
    filter(NHANES, title_type %in% input$education)
    })
    2. Filter for chosen education level(s) and save as a
    new (reactive) expression
    Creates a cached expression
    that knows it is out of date
    when input changes

    View Slide

  62. 3. Use new data frame (which is reactive) for plotting
    # Create the scatterplot object the plotOutput function is expecting
    output$scatterplot <- renderPlot({
    ggplot(data = NHANES_subset(), aes_string(x = input$x, y = input$y,
    color = input$z)) +
    geom_point(…) +

    })
    Cached - only re-run
    when inputs change

    View Slide

  63. 4. Use new data frame (which is reactive) also for
    printing number of observations
    # UI
    mainPanel(

    # Print number of obs plotted
    uiOutput(outputId = "n"),

    )
    # Server
    output$n <- renderUI({
    types <- NHANES_subset()$title_type %>%
    factor(levels = input$selected_type)
    counts <- table(types)
    HTML(paste("There are", counts, input$selected_type, “participants in this
    dataset.
    "))
    })

    View Slide

  64. DEMO
    Putting it altogether
    nhanes-05.R
    Also notice
    - HTML tags for visual separation
    - req()

    View Slide

  65. When to use reactives
    - By using a reactive expression for the subsetted data frame, we were able to get
    away with subsetting once and then using the result twice
    - In general, reactive conductors let you
    - not repeat yourself (i.e. avoid copy-and-paste code) which is a maintenance boon)
    - decompose large, complex (code-wise, not necessarily CPU-wise) calculations into
    smaller pieces to make them more understandable
    - These benefits are similar to what happens when you decompose a large complex R
    script into a series of small functions that build on each other

    View Slide

  66. ?
    Suppose we want to plot only a random sample of
    participants, of size determined by the user. What is
    wrong with the following?
    # Server
    # Create a new data frame that is a sample of n_samp
    # observations from NHANES
    NHANES_sample <- sample_n(NHANES_sample(), input$n_samp)
    # Plot the sampled participants
    output$scatterplot <- renderPlot({
    ggplot(data = NHANES_sample,
    aes_string(x = input$x, y = input$y,
    color = input$z)) +
    geom_point(…)
    })

    View Slide

  67. SOLUTION
    # Server
    # Create a new data frame that is a sample of n_samp
    # observations from NHANES
    NHANES_sample <- reactive({
    req(input$n_samp) # ensure availability of value
    sample_n(NHANES_sample(), input$n_samp)
    })
    # Plot the sampled participants
    output$scatterplot <- renderPlot({
    ggplot(data = NHANES_sample(),
    aes_string(x = input$x,
    y = input$y,
    color = input$z)) +
    geom_point(…)
    })

    View Slide

  68. Render functions

    View Slide

  69. Render functions
    - Provide a code chunk that describes how an output should be populated
    - The output will update in response to changes in any reactive values or reactive
    expressions that are used in the code chunk
    render*({ [code_chunk] })

    View Slide

  70. View Slide

  71. Recap
    - These functions make objects to display
    - Results should always be saved to output$
    - They make an observer object that has a block of code associated with it
    - The object will rerun the entire code block to update itself whenever it is
    invalidated
    render*({ [code_chunk] })

    View Slide

  72. Implementation

    View Slide

  73. Implementation of reactives
    - Reactive values – reactiveValues():
    - e.g. input: which looks like a list, and contains many individual reactive values that are set by
    input from the web browser
    - Reactive expressions – reactive(): they depend on reactive values and observers depend on
    them
    - Can access reactive values or other reactive expressions, and they return a value
    - Useful for caching the results of any procedure that happens in response to user input
    - e.g. reactive data frame subsets we created earlier
    - Observers – observe(): they depend on reactive expressions, but nothing else depends on them
    - Can access reactive sources and reactive expressions, but they don’t return a value; they are used
    for their side effects
    - e.g. output object is a reactive observer, which also looks like a list, and contains many individual
    reactive observers that are created by using reactive values and expressions in reactive functions

    View Slide

  74. Reactive expressions vs. observers
    - Similarities: Both store expressions that can be executed
    - Differences:
    - Reactive expressions return values, but observers don’t
    - Observers (and endpoints in general) eagerly respond to reactives, but
    reactive expressions (and conductors in general) do not
    - Reactive expressions must not have side effects, while observers are only
    useful for their side effects

    View Slide

  75. Your turn
    Debug the following app scripts:
    - review/whats-wrong.R
    - review/mult-3.R
    - review/add-2.R

    View Slide

  76. Where to go next?

    View Slide

  77. View Slide

  78. Mine Çetinkaya-Rundel
    mine-cetinkaya-rundel
    [email protected]
    @minebocek
    bit.ly/shiny-rlphl-git

    View Slide