Previous chapter
Productionizing ShinyDebugging Shiny
Next chapter

General Coding Style

Writing Robust Code

  • Complexity is the problem; abstraction is the solution
  • Software programs are far too large to reason about in their entirety
  • Good programs are broken into fragments that you can reason about locally, and compose reliably
  • In other words, we break the program into simple fragments, and if we verify that each fragment is correct, then the whole program is correct
  • Are our fragments simple enough to understand?
  • Do they compose reliably?

Understandable Fragments

  • Indent your code! (Ctrl+I/Cmd+I)
  • Extract out complicated processing logic (as opposed to UI logic) into top-level functions so you can test them separately
  • Each function, reactive, observer, or module should be small, and do one thing
  • Function/reactive/observer bodies that don’t fit on a single screen is a bad code smell
  • If you’re having trouble giving something a meaningful name, maybe it’s doing too much
  • When you encounter unavoidable complexity, at least try to firewall the complexity behind as simple/straightforward an API as possible
  • Even if it’s hard to verify if the scary piece itself is correct, it’s still easy to verify that its callers are correct

Reliable Composition

  • Prefer “pure functions”—functions without side effects. Much less likely to surprise you.
  • When you do need side effects, don’t put them in surprising places. Consider following command-query separation—“asking a question should not change the answer”
  • Reactive expressions must not have side effects
  • Avoid observers and reactive values, where possible; use reactive expressions if you can help it
  • Don’t pass around environments and reactive values objects; this is similar to sharing global variables, it introduces hidden coupling
  • For ease of reasoning, prefer: pure functional > reactive > imperative (observers)

Debugging tools

Standard R Debugging Tools

  • Tracing
  • print()/cat()/str()
  • renderPrint() eats messages, must use cat(file = stderr(), ...)
  • Also consider shinyjs package’s logjs, which puts messages in the browser’s JavaScript
  • Debugger
  • Set breakpoints in RStudio
  • browser()
  • Conditionals: if (!is.null(input$x)) browser()

Shiny Debugging Tools

Symptom: Outputs or observers don’t execute when expected, or execute too often

  • Reactlog
  • Restart R process
  • Set options(shiny.reactlog = TRUE)
  • In the browser, Ctrl+F3 (or Cmd+F3

Symptom: Red error messages in the UI or session abruptly terminates

  • This means an R error has occured
  • Look in R console for stack traces
  • By default, Shiny hides “internal” stack traces. Use options(shiny.fullstacktrace = TRUE) if necessary to show.
  • Newer versions of Shiny/Shiny Server “sanitize” errors, for security reasons (every error message is displayed as “An error has occurred”)
  • See sanitizing errors article for more details, including how to view the real errors

Symptom: Server logic seems OK, but unexpected/broken/missing results in browser

  • Check browser’s JavaScript console for errors
  • Listen in on conversation between client and server
  • options(shiny.trace=TRUE) logs messages in the R console
  • Use Chrome’s Network tab to show individual websocket messages

Exercise: Broken 1

  • Run the code in the app below. You can also download the code here and the dataset here.
  • It is broken in a not-very-subtle way.
  • See if you can find and fix the bug.
library(shiny)
library(ggplot2)
library(DT)
library(stringr)
library(dplyr)
library(tools)
load("movies.Rdata")

# Define UI for application that plots features of movies -----------
ui <- fluidPage(

  # Application title -----------------------------------------------
  titlePanel("Movie browser - without modules"),

  # 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("IMDB rating" = "imdb_rating",
                              "IMDB number of votes" = "imdb_num_votes",
                              "Critics Score" = "critics_score",
                              "Audience Score" = "audience_score",
                              "Runtime" = "runtime"),
                  selected = "audience_score")

      # Select variable for x-axis ----------------------------------
      selectInput(inputId = "x",
                  label = "X-axis:",
                  choices = c("IMDB rating" = "imdb_rating",
                              "IMDB number of votes" = "imdb_num_votes",
                              "Critics Score" = "critics_score",
                              "Audience Score" = "audience_score",
                              "Runtime" = "runtime"),
                  selected = "critics_score")

      # Select variable for color -----------------------------------
      selectInput(inputId = "z",
                  label = "Color by:",
                  choices = c("Genre" = "genre",
                              "MPAA Rating" = "mpaa_rating",
                              "Critics Rating" = "critics_rating",
                              "Audience Rating" = "audience_rating"),
                  selected = "mpaa_rating")

      # Set alpha level ---------------------------------------------
      sliderInput(inputId = "alpha",
                  label = "Alpha:",
                  min = 0, max = 1,
                  value = 0.5)

      # Set point size ----------------------------------------------
      sliderInput(inputId = "size",
                  label = "Size:",
                  min = 0, max = 5,
                  value = 2)

      # Show data table ---------------------------------------------
      checkboxInput(inputId = "show_data",
                    label = "Show data table",
                    value = TRUE)

    ),

    # Output: -------------------------------------------------------
    mainPanel(

      # Show scatterplot --------------------------------------------
      tabsetPanel(id = "movies",
                  tabPanel("Documentaries",
                           plotOutput("scatterplot_doc"),
                           DT::dataTableOutput("moviestable_doc")),
                  tabPanel("Feature Films",
                           plotOutput("scatterplot_feature"),
                           DT::dataTableOutput("moviestable_feature")),
                  tabPanel("TV Movies",
                           plotOutput("scatterplot_tv"),
                           DT::dataTableOutput("moviestable_tv"))
      )
    )
  )
)

# Define server function required to create the scatterplot ---------
server <- function(input, output, session) {

  # Create subsets for various title types --------------------------
  docs <- reactive({
    filter(movies, title_type == "Documentary")
  })

  features <- reactive({
    filter(movies, title_type == "Feature Film")
  })

  tvs <- reactive({
    filter(movies, title_type == "TV Movie")
  })

  # Scatterplot for docs --------------------------------------------
  output$scatterplot_doc <- renderPlot({
    ggplot(data = docs(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })

  # Scatterplot for features ----------------------------------------
  output$scatterplot_feature <- renderPlot({
    ggplot(data = features(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })

  # Scatterplot for tvs ---------------------------------------------
  output$scatterplot_tv <- renderPlot({
    ggplot(data = tvs(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })

  # Table for docs --------------------------------------------------
  output$moviestable_doc <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = docs()[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )

  # Table for features ----------------------------------------------
  output$moviestable_feature <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = features()[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )

  # Table for tvs ---------------------------------------------------
  output$moviestable_tv <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = tvs()[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )

}

# Run the application -----------------------------------------------
shinyApp(ui = ui, server = server)

Next Steps

  1. Try to first run the exercise above based on the specifications ON YOUR OWN.
  2. Only as a last resort check out the solution in the next section.

Solution: Broken 1

Missing commas, as explained in the R console.

library(shiny)
library(ggplot2)
library(DT)
library(stringr)
library(dplyr)
library(tools)
load("movies.Rdata")

# Define UI for application that plots features of movies -----------
ui <- fluidPage(

  # Application title -----------------------------------------------
  titlePanel("Movie browser - without modules"),

  # 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("IMDB rating" = "imdb_rating",
                              "IMDB number of votes" = "imdb_num_votes",
                              "Critics Score" = "critics_score",
                              "Audience Score" = "audience_score",
                              "Runtime" = "runtime"),
                  selected = "audience_score"),

      # Select variable for x-axis ----------------------------------
      selectInput(inputId = "x",
                  label = "X-axis:",
                  choices = c("IMDB rating" = "imdb_rating",
                              "IMDB number of votes" = "imdb_num_votes",
                              "Critics Score" = "critics_score",
                              "Audience Score" = "audience_score",
                              "Runtime" = "runtime"),
                  selected = "critics_score"),

      # Select variable for color -----------------------------------
      selectInput(inputId = "z",
                  label = "Color by:",
                  choices = c("Genre" = "genre",
                              "MPAA Rating" = "mpaa_rating",
                              "Critics Rating" = "critics_rating",
                              "Audience Rating" = "audience_rating"),
                  selected = "mpaa_rating"),

      # Set alpha level ---------------------------------------------
      sliderInput(inputId = "alpha",
                  label = "Alpha:",
                  min = 0, max = 1,
                  value = 0.5),

      # Set point size ----------------------------------------------
      sliderInput(inputId = "size",
                  label = "Size:",
                  min = 0, max = 5,
                  value = 2),

      # Show data table ---------------------------------------------
      checkboxInput(inputId = "show_data",
                    label = "Show data table",
                    value = TRUE)

    ),

    # Output: -------------------------------------------------------
    mainPanel(

      # Show scatterplot --------------------------------------------
      tabsetPanel(id = "movies",
                  tabPanel("Documentaries",
                           plotOutput("scatterplot_doc"),
                           DT::dataTableOutput("moviestable_doc")),
                  tabPanel("Feature Films",
                           plotOutput("scatterplot_feature"),
                           DT::dataTableOutput("moviestable_feature")),
                  tabPanel("TV Movies",
                           plotOutput("scatterplot_tv"),
                           DT::dataTableOutput("moviestable_tv"))
      )
    )
  )
)

# Define server function required to create the scatterplot ---------
server <- function(input, output, session) {

  # Create subsets for various title types --------------------------
  docs <- reactive({
    filter(movies, title_type == "Documentary")
  })

  features <- reactive({
    filter(movies, title_type == "Feature Film")
  })

  tvs <- reactive({
    filter(movies, title_type == "TV Movie")
  })

  # Scatterplot for docs --------------------------------------------
  output$scatterplot_doc <- renderPlot({
    ggplot(data = docs(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })

  # Scatterplot for features ----------------------------------------
  output$scatterplot_feature <- renderPlot({
    ggplot(data = features(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })

  # Scatterplot for tvs ---------------------------------------------
  output$scatterplot_tv <- renderPlot({
    ggplot(data = tvs(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })

  # Table for docs --------------------------------------------------
  output$moviestable_doc <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = docs()[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )

  # Table for features ----------------------------------------------
  output$moviestable_feature <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = features()[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )

  # Table for tvs ---------------------------------------------------
  output$moviestable_tv <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = tvs()[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )

}

# Run the application -----------------------------------------------
shinyApp(ui = ui, server = server)

Exercise: Broken 2

  • Run the code in the app below. You can also download the code here and the dataset here.
  • It is broken in a not-very-subtle way.
  • See if you can find and fix the bug.
library(shiny)
library(ggplot2)
library(DT)
library(stringr)
library(dplyr)
library(tools)
load("movies.Rdata")

# Define UI for application that plots features of movies -----------
ui <- fluidPage(

  # Application title -----------------------------------------------
  titlePanel("Movie browser - without modules"),

  # 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("IMDB rating" = "imdb_rating",
                              "IMDB number of votes" = "imdb_num_votes",
                              "Critics Score" = "critics_score",
                              "Audience Score" = "audience_score",
                              "Runtime" = "runtime"),
                  selected = "audience_score"),

      # Select variable for x-axis ----------------------------------
      selectInput(inputId = "x",
                  label = "X-axis:",
                  choices = c("IMDB rating" = "imdb_rating",
                              "IMDB number of votes" = "imdb_num_votes",
                              "Critics Score" = "critics_score",
                              "Audience Score" = "audience_score",
                              "Runtime" = "runtime"),
                  selected = "critics_score"),

      # Select variable for color -----------------------------------
      selectInput(inputId = "z",
                  label = "Color by:",
                  choices = c("Genre" = "genre",
                              "MPAA Rating" = "mpaa_rating",
                              "Critics Rating" = "critics_rating",
                              "Audience Rating" = "audience_rating"),
                  selected = "mpaa_rating"),

      # Set alpha level ---------------------------------------------
      sliderInput(inputId = "alpha",
                  label = "Alpha:",
                  min = 0, max = 1,
                  value = 0.5),

      # Set point size ----------------------------------------------
      sliderInput(inputId = "size",
                  label = "Size:",
                  min = 0, max = 5,
                  value = 2),

      # Show data table ---------------------------------------------
      checkboxInput(inputId = "show_data",
                    label = "Show data table",
                    value = TRUE)

    ),

    # Output: -------------------------------------------------------
    mainPanel(

      # Show scatterplot --------------------------------------------
      tabsetPanel(id = "movies",
                  tabPanel("Documentaries",
                           plotOutput("scatterplot_doc"),
                           DT::dataTableOutput("moviestable_doc")),
                  tabPanel("Feature Films",
                           plotOutput("scatterplot_feature"),
                           DT::dataTableOutput("moviestable_feature")),
                  tabPanel("TV Movies",
                           plotOutput("scatterplot_tv"),
                           DT::dataTableOutput("moviestable_tv"))
      )
    )
  )
)

# Define server function required to create the scatterplot ---------
server <- function(input, output, session) {

  # Create subsets for various title types --------------------------
  docs <- reactive({
    filter(movies, title_type == "Documentary")
  })

  features <- reactive({
    filter(movies, title_type == "Feature Film")
  })

  tvs <- reactive({
    filter(movies, title_type == "TV Movie")
  })

  # Scatterplot for docs --------------------------------------------
  output$scatterplot_doc <- renderPlot({
    ggplot(data = docs(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })

  # Scatterplot for features ----------------------------------------
  output$scatterplot_feature <- renderPlot({
    ggplot(data = features(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })

  # Scatterplot for tvs ---------------------------------------------
  output$scatterplot_tv <- renderPlot({
    ggplot(data = tvs(), aes_string(x = input$x, y = input$y, color = input$z))
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })

  # Table for docs --------------------------------------------------
  output$moviestable_doc <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = docs()[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )

  # Table for features ----------------------------------------------
  output$moviestable_feature <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = features()[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )

  # Table for tvs ---------------------------------------------------
  output$moviestable_tv <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = tvs()[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )

}

# Run the application -----------------------------------------------
shinyApp(ui = ui, server = server)

Next Steps

  1. Try to first run the exercise above based on the specifications ON YOUR OWN.
  2. Only as a last resort check out the solution in the next section.

Solution: Broken 2

Hint: Check the tab TV Movies

Solution Broken 2: ggplot call was missing a “+”

library(shiny)
library(ggplot2)
library(DT)
library(stringr)
library(dplyr)
library(tools)
load("movies.Rdata")

# Define UI for application that plots features of movies -----------
ui <- fluidPage(

  # Application title -----------------------------------------------
  titlePanel("Movie browser - without modules"),

  # 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("IMDB rating" = "imdb_rating",
                              "IMDB number of votes" = "imdb_num_votes",
                              "Critics Score" = "critics_score",
                              "Audience Score" = "audience_score",
                              "Runtime" = "runtime"),
                  selected = "audience_score"),

      # Select variable for x-axis ----------------------------------
      selectInput(inputId = "x",
                  label = "X-axis:",
                  choices = c("IMDB rating" = "imdb_rating",
                              "IMDB number of votes" = "imdb_num_votes",
                              "Critics Score" = "critics_score",
                              "Audience Score" = "audience_score",
                              "Runtime" = "runtime"),
                  selected = "critics_score"),

      # Select variable for color -----------------------------------
      selectInput(inputId = "z",
                  label = "Color by:",
                  choices = c("Genre" = "genre",
                              "MPAA Rating" = "mpaa_rating",
                              "Critics Rating" = "critics_rating",
                              "Audience Rating" = "audience_rating"),
                  selected = "mpaa_rating"),

      # Set alpha level ---------------------------------------------
      sliderInput(inputId = "alpha",
                  label = "Alpha:",
                  min = 0, max = 1,
                  value = 0.5),

      # Set point size ----------------------------------------------
      sliderInput(inputId = "size",
                  label = "Size:",
                  min = 0, max = 5,
                  value = 2),

      # Show data table ---------------------------------------------
      checkboxInput(inputId = "show_data",
                    label = "Show data table",
                    value = TRUE)

    ),

    # Output: -------------------------------------------------------
    mainPanel(

      # Show scatterplot --------------------------------------------
      tabsetPanel(id = "movies",
                  tabPanel("Documentaries",
                           plotOutput("scatterplot_doc"),
                           DT::dataTableOutput("moviestable_doc")),
                  tabPanel("Feature Films",
                           plotOutput("scatterplot_feature"),
                           DT::dataTableOutput("moviestable_feature")),
                  tabPanel("TV Movies",
                           plotOutput("scatterplot_tv"),
                           DT::dataTableOutput("moviestable_tv"))
      )
    )
  )
)

# Define server function required to create the scatterplot ---------
server <- function(input, output, session) {

  # Create subsets for various title types --------------------------
  docs <- reactive({
    filter(movies, title_type == "Documentary")
  })

  features <- reactive({
    filter(movies, title_type == "Feature Film")
  })

  tvs <- reactive({
    filter(movies, title_type == "TV Movie")
  })

  # Scatterplot for docs --------------------------------------------
  output$scatterplot_doc <- renderPlot({
    ggplot(data = docs(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })

  # Scatterplot for features ----------------------------------------
  output$scatterplot_feature <- renderPlot({
    ggplot(data = features(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })

  # Scatterplot for tvs ---------------------------------------------
  output$scatterplot_tv <- renderPlot({
    ggplot(data = tvs(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })

  # Table for docs --------------------------------------------------
  output$moviestable_doc <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = docs()[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )

  # Table for features ----------------------------------------------
  output$moviestable_feature <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = features()[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )

  # Table for tvs ---------------------------------------------------
  output$moviestable_tv <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = tvs()[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )

}

# Run the application -----------------------------------------------
shinyApp(ui = ui, server = server)

Exercise: Broken 3

  • Run the code in the app below. You can also download the code here and the dataset here.
  • It is broken in a not-very-subtle way.
  • See if you can find and fix the bug.
library(shiny)
library(ggplot2)
library(DT)
library(stringr)
library(dplyr)
library(tools)
load("movies.Rdata")

# Define UI for application that plots features of movies -----------
ui <- fluidPage(

  # Application title -----------------------------------------------
  titlePanel("Movie browser - without modules"),

  # 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("IMDB rating" = "imdb_rating",
                              "IMDB number of votes" = "imdb_num_votes",
                              "Critics Score" = "critics_score",
                              "Audience Score" = "audience_score",
                              "Runtime" = "runtime"),
                  selected = "audience_score"),

      # Select variable for x-axis ----------------------------------
      selectInput(inputId = "x",
                  label = "X-axis:",
                  choices = c("IMDB rating" = "imdb_rating",
                              "IMDB number of votes" = "imdb_num_votes",
                              "Critics Score" = "critics_score",
                              "Audience Score" = "audience_score",
                              "Runtime" = "runtime"),
                  selected = "critics_score"),

      # Select variable for color -----------------------------------
      selectInput(inputId = "z",
                  label = "Color by:",
                  choices = c("Genre" = "genre",
                              "MPAA Rating" = "mpaa_rating",
                              "Critics Rating" = "critics_rating",
                              "Audience Rating" = "audience_rating"),
                  selected = "mpaa_rating"),

      # Set alpha level ---------------------------------------------
      sliderInput(inputId = "alpha",
                  label = "Alpha:",
                  min = 0, max = 1,
                  value = 0.5),

      # Set point size ----------------------------------------------
      sliderInput(inputId = "size",
                  label = "Size:",
                  min = 0, max = 5,
                  value = 2),

      # Show data table ---------------------------------------------
      checkboxInput(inputId = "show_data",
                    label = "Show data table",
                    value = TRUE)

    ),

    # Output: -------------------------------------------------------
    mainPanel(

      # Show scatterplot --------------------------------------------
      tabsetPanel(id = "movies",
                  tabPanel("Documentaries",
                           plotOutput("scatterplot_doc"),
                           DT::dataTableOutput("moviestable_doc")),
                  tabPanel("Feature Films",
                           plotOutput("scatterplot_feature"),
                           DT::dataTableOutput("moviestable_feature")),
                  tabPanel("TV Movies",
                           plotOutput("scatterplot_tv"),
                           DT::dataTableOutput("moviestable_tv"))
      )
    )
  )
)

# Define server function required to create the scatterplot ---------
server <- function(input, output, session) {

  # Create subsets for various title types --------------------------
  docs <- reactive({
    filter(movies, title_type == "Documentary")
  })

  features <- reactive({
    filter(movies, title_type == "Feature Film")
  })

  tvs <- reactive({
    filter(movies, title_type == "TV Movie")
  })

  # Scatterplot for docs --------------------------------------------
  output$scatterplot_doc <- renderPlot({
    ggplot(data = docs(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })

  # Scatterplot for features ----------------------------------------
  output$scatterplot_feature <- renderPlot({
    ggplot(data = features(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })

  # Scatterplot for tvs ---------------------------------------------
  output$scatterplot_tv <- renderPlot({
    ggplot(data = tvs(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })

  # Table for docs --------------------------------------------------
  output$moviestable_doc <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = docs[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )

  # Table for features ----------------------------------------------
  output$moviestable_feature <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = features[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )

  # Table for tvs ---------------------------------------------------
  output$moviestable_tv <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = tvs[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )

}

# Run the application -----------------------------------------------
shinyApp(ui = ui, server = server)

Next Steps

  1. Try to first run the exercise above based on the specifications ON YOUR OWN.
  2. Only as a last resort check out the solution in the next section.

Solution: Broken 3

  • Solution Broken 3: Reactive was not being called with ()
library(shiny)
library(ggplot2)
library(DT)
library(stringr)
library(dplyr)
library(tools)
load("movies.Rdata")

# Define UI for application that plots features of movies -----------
ui <- fluidPage(
  
  # Application title -----------------------------------------------
  titlePanel("Movie browser - without modules"),
  
  # 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("IMDB rating" = "imdb_rating",
                              "IMDB number of votes" = "imdb_num_votes",
                              "Critics Score" = "critics_score",
                              "Audience Score" = "audience_score",
                              "Runtime" = "runtime"),
                  selected = "audience_score"),
      
      # Select variable for x-axis ----------------------------------
      selectInput(inputId = "x",
                  label = "X-axis:",
                  choices = c("IMDB rating" = "imdb_rating",
                              "IMDB number of votes" = "imdb_num_votes",
                              "Critics Score" = "critics_score",
                              "Audience Score" = "audience_score",
                              "Runtime" = "runtime"),
                  selected = "critics_score"),
      
      # Select variable for color -----------------------------------
      selectInput(inputId = "z",
                  label = "Color by:",
                  choices = c("Genre" = "genre",
                              "MPAA Rating" = "mpaa_rating",
                              "Critics Rating" = "critics_rating",
                              "Audience Rating" = "audience_rating"),
                  selected = "mpaa_rating"),
      
      # Set alpha level ---------------------------------------------
      sliderInput(inputId = "alpha",
                  label = "Alpha:",
                  min = 0, max = 1,
                  value = 0.5),
      
      # Set point size ----------------------------------------------
      sliderInput(inputId = "size",
                  label = "Size:",
                  min = 0, max = 5,
                  value = 2),
      
      # Show data table ---------------------------------------------
      checkboxInput(inputId = "show_data",
                    label = "Show data table",
                    value = TRUE)
      
    ),
    
    # Output: -------------------------------------------------------
    mainPanel(
      
      # Show scatterplot --------------------------------------------
      tabsetPanel(id = "movies",
                  tabPanel("Documentaries",
                           plotOutput("scatterplot_doc"),
                           DT::dataTableOutput("moviestable_doc")),
                  tabPanel("Feature Films",
                           plotOutput("scatterplot_feature"),
                           DT::dataTableOutput("moviestable_feature")),
                  tabPanel("TV Movies",
                           plotOutput("scatterplot_tv"),
                           DT::dataTableOutput("moviestable_tv"))
      )
    )
  )
)

# Define server function required to create the scatterplot ---------
server <- function(input, output, session) {
  
  # Create subsets for various title types --------------------------
  docs <- reactive({
    filter(movies, title_type == "Documentary")
  })
  
  features <- reactive({
    filter(movies, title_type == "Feature Film")
  })
  
  tvs <- reactive({
    filter(movies, title_type == "TV Movie")
  })
  
  # Scatterplot for docs --------------------------------------------
  output$scatterplot_doc <- renderPlot({
    ggplot(data = docs(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })
  
  # Scatterplot for features ----------------------------------------
  output$scatterplot_feature <- renderPlot({
    ggplot(data = features(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })
  
  # Scatterplot for tvs ---------------------------------------------
  output$scatterplot_tv <- renderPlot({
    ggplot(data = tvs(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })
  
  # Table for docs --------------------------------------------------
  output$moviestable_doc <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = docs()[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )
  
  # Table for features ----------------------------------------------
  output$moviestable_feature <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = features()[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )
  
  # Table for tvs ---------------------------------------------------
  output$moviestable_tv <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = tvs()[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )
  
}

# Run the application -----------------------------------------------
shinyApp(ui = ui, server = server)

Exercise: Broken 4

  • Run the code in the app below. You can also download the code here and the dataset here.
  • It is broken in a not-very-subtle way.
  • See if you can find and fix the bug.
library(shiny)
library(ggplot2)
library(DT)
library(stringr)
library(dplyr)
load("movies.Rdata")

# Define UI for application that plots features of movies -----------
ui <- fluidPage(

  # Application title -----------------------------------------------
  titlePanel("Movie browser - without modules"),

  # 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("IMDB rating" = "imdb_rating",
                              "IMDB number of votes" = "imdb_num_votes",
                              "Critics Score" = "critics_score",
                              "Audience Score" = "audience_score",
                              "Runtime" = "runtime"),
                  selected = "audience_score"),

      # Select variable for x-axis ----------------------------------
      selectInput(inputId = "x",
                  label = "X-axis:",
                  choices = c("IMDB rating" = "imdb_rating",
                              "IMDB number of votes" = "imdb_num_votes",
                              "Critics Score" = "critics_score",
                              "Audience Score" = "audience_score",
                              "Runtime" = "runtime"),
                  selected = "critics_score"),

      # Select variable for color -----------------------------------
      selectInput(inputId = "z",
                  label = "Color by:",
                  choices = c("Genre" = "genre",
                              "MPAA Rating" = "mpaa_rating",
                              "Critics Rating" = "critics_rating",
                              "Audience Rating" = "audience_rating"),
                  selected = "mpaa_rating"),

      # Set alpha level ---------------------------------------------
      sliderInput(inputId = "alpha",
                  label = "Alpha:",
                  min = 0, max = 1,
                  value = 0.5),

      # Set point size ----------------------------------------------
      sliderInput(inputId = "size",
                  label = "Size:",
                  min = 0, max = 5,
                  value = 2),

      # Show data table ---------------------------------------------
      checkboxInput(inputId = "show_data",
                    label = "Show data table",
                    value = TRUE)

    ),

    # Output: -------------------------------------------------------
    mainPanel(

      # Show scatterplot --------------------------------------------
      tabsetPanel(id = "movies",
                  tabPanel("Documentaries",
                           plotOutput("scatterplot_doc"),
                           DT::dataTableOutput("moviestable_doc")),
                  tabPanel("Feature Films",
                           plotOutput("scatterplot_features"),
                           DT::dataTableOutput("moviestable_features")),
                  tabPanel("TV Movies",
                           plotOutput("scatterplot_tv"),
                           DT::dataTableOutput("moviestable_tv"))
      )
    )
  )
)

# Define server function required to create the scatterplot ---------
server <- function(input, output, session) {

  # Create subsets for various title types --------------------------
  docs <- reactive({
    filter(movies, title_type == "Documentary")
  })

  features <- reactive({
    filter(movies, title_type == "Feature Film")
  })

  tvs <- reactive({
    filter(movies, title_type == "TV Movie")
  })

  # Scatterplot for docs --------------------------------------------
  output$scatterplot_doc <- renderPlot({
    ggplot(data = docs(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })

  # Scatterplot for features ----------------------------------------
  output$scatterplot_feature <- renderPlot({
    ggplot(data = features(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })

  # Scatterplot for tvs ---------------------------------------------
  output$scatterplot_tv <- renderPlot({
    ggplot(data = tvs(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })

  # Table for docs --------------------------------------------------
  output$moviestable_doc <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = docs()[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )

  # Table for features ----------------------------------------------
  output$moviestable_feature <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = features()[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )

  # Table for tvs ---------------------------------------------------
  output$moviestable_tv <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = tvs()[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )

}

# Run the application -----------------------------------------------
shinyApp(ui = ui, server = server)

Next Steps

  1. Try to first run the exercise above based on the specifications ON YOUR OWN.
  2. Only as a last resort check out the solution in the next section.

Solution: Broken 4

  • Solution Broken 4: Output ID was not consistent between UI and server
library(shiny)
library(ggplot2)
library(DT)
library(stringr)
library(dplyr)
load("movies.Rdata")

# Define UI for application that plots features of movies -----------
ui <- fluidPage(
  
  # Application title -----------------------------------------------
  titlePanel("Movie browser - without modules"),
  
  # 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("IMDB rating" = "imdb_rating",
                              "IMDB number of votes" = "imdb_num_votes",
                              "Critics Score" = "critics_score",
                              "Audience Score" = "audience_score",
                              "Runtime" = "runtime"),
                  selected = "audience_score"),
      
      # Select variable for x-axis ----------------------------------
      selectInput(inputId = "x",
                  label = "X-axis:",
                  choices = c("IMDB rating" = "imdb_rating",
                              "IMDB number of votes" = "imdb_num_votes",
                              "Critics Score" = "critics_score",
                              "Audience Score" = "audience_score",
                              "Runtime" = "runtime"),
                  selected = "critics_score"),
      
      # Select variable for color -----------------------------------
      selectInput(inputId = "z",
                  label = "Color by:",
                  choices = c("Genre" = "genre",
                              "MPAA Rating" = "mpaa_rating",
                              "Critics Rating" = "critics_rating",
                              "Audience Rating" = "audience_rating"),
                  selected = "mpaa_rating"),
      
      # Set alpha level ---------------------------------------------
      sliderInput(inputId = "alpha",
                  label = "Alpha:",
                  min = 0, max = 1,
                  value = 0.5),
      
      # Set point size ----------------------------------------------
      sliderInput(inputId = "size",
                  label = "Size:",
                  min = 0, max = 5,
                  value = 2),
      
      # Show data table ---------------------------------------------
      checkboxInput(inputId = "show_data",
                    label = "Show data table",
                    value = TRUE)
      
    ),
    
    # Output: -------------------------------------------------------
    mainPanel(
      
      # Show scatterplot --------------------------------------------
      tabsetPanel(id = "movies",
                  tabPanel("Documentaries",
                           plotOutput("scatterplot_doc"),
                           DT::dataTableOutput("moviestable_doc")),
                  tabPanel("Feature Films",
                           plotOutput("scatterplot_features"),
                           DT::dataTableOutput("moviestable_features")),
                  tabPanel("TV Movies",
                           plotOutput("scatterplot_tv"),
                           DT::dataTableOutput("moviestable_tv"))
      )
    )
  )
)

# Define server function required to create the scatterplot ---------
server <- function(input, output, session) {
  
  # Create subsets for various title types --------------------------
  docs <- reactive({
    filter(movies, title_type == "Documentary")
  })
  
  features <- reactive({
    filter(movies, title_type == "Feature Film")
  })
  
  tvs <- reactive({
    filter(movies, title_type == "TV Movie")
  })
  
  # Scatterplot for docs --------------------------------------------
  output$scatterplot_doc <- renderPlot({
    ggplot(data = docs(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })
  
  # Scatterplot for features ----------------------------------------
  output$scatterplot_features <- renderPlot({
    ggplot(data = features(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })
  
  # Scatterplot for tvs ---------------------------------------------
  output$scatterplot_tv <- renderPlot({
    ggplot(data = tvs(), aes_string(x = input$x, y = input$y, color = input$z)) +
      geom_point(alpha = input$alpha, size = input$size) +
      labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
           y = toTitleCase(str_replace_all(input$y, "_", " ")),
           color = toTitleCase(str_replace_all(input$z, "_", " "))
      )
  })
  
  # Table for docs --------------------------------------------------
  output$moviestable_doc <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = docs()[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )
  
  # Table for features ----------------------------------------------
  output$moviestable_features <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = features()[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )
  
  # Table for tvs ---------------------------------------------------
  output$moviestable_tv <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = tvs()[, 1:7],
                    options = list(pageLength = 10),
                    rownames = FALSE)
    }
  )
  
}

# Run the application -----------------------------------------------
shinyApp(ui = ui, server = server)

Exercise: Broken 5

  • Run the code in the app below. You can also download the code here and the dataset here.
  • It is broken in a subtle way.
  • See if you can find and fix the bug.
  • Check the box for one other type of movie and see how the text about number of movies changes.
  • Choose a low sample size and get a new sample. Choose a high sample size and get a new sample.
library(shiny)
library(ggplot2)
library(DT)
library(stringr)
library(dplyr)
library(tools)
load("movies.Rdata")

# Define UI for application that plots features of movies -------------------
ui <- fluidPage(
  
  # Application title -------------------------------------------------------
  titlePanel("Movie browser"),
  
  # 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("IMDB rating" = "imdb_rating", 
                              "IMDB number of votes" = "imdb_num_votes", 
                              "Critics Score" = "critics_score", 
                              "Audience Score" = "audience_score", 
                              "Runtime" = "runtime"), 
                  selected = "audience_score"),
      
      # Select variable for x-axis ------------------------------------------
      selectInput(inputId = "x", 
                  label = "X-axis:",
                  choices = c("IMDB rating" = "imdb_rating", 
                              "IMDB number of votes" = "imdb_num_votes", 
                              "Critics Score" = "critics_score", 
                              "Audience Score" = "audience_score", 
                              "Runtime" = "runtime"), 
                  selected = "critics_score"),
      
      # Show data table -----------------------------------------------------
      checkboxInput(inputId = "show_data",
                    label = "Show data table",
                    value = TRUE),
      
      # Horizontal line for visual separation -------------------------------
      hr(),
      
      # Select which types of movies to plot --------------------------------
      checkboxGroupInput(inputId = "selected_type",
                         label = "Select movie type(s):",
                         choices = c("Documentary", "Feature Film", "TV Movie"),
                         selected = "Feature Film"),
      
      # Select sample size --------------------------------------------------
      numericInput(inputId = "n_samp", 
                   label = "Sample size:", 
                   min = 1, max = nrow(movies), 
                   value = 10),
      
      # Get a new sample ----------------------------------------------------
      actionButton(inputId = "get_new_sample", 
                   label = "Get new sample")
      
    ),
    
    # Output: ---------------------------------------------------------------
    mainPanel(
      
      # Show scatterplot ----------------------------------------------------
      plotOutput(outputId = "scatterplot"),
      HTML("<br>"),        # a little bit of visual separation
      
      # Print number of obs plotted -----------------------------------------
      textOutput(outputId = "n"),
      HTML("<br><br>"),    # a little bit of visual separation
      
      # Show data table -----------------------------------------------------
      dataTableOutput(outputId = "moviestable")
    )
  )
)

# Define server function required to create the scatterplot -----------------
server <- function(input, output, session) {
  
  # Create a subset of data filtering for selected title types --------------
  movies_subset <- reactive({
    movies %>%
      filter(title_type %in% input$selected_type)
  })
  
  # Update the maximum allowed n_samp for selected type movies --------------
  observe({
    updateNumericInput(session, 
                       inputId = "n_samp", 
                       max = nrow(movies_subset()),
                       value = input$n_samp
    )
  })
  
  # Get new sample ----------------------------------------------------------
  movies_sample <- eventReactive(eventExpr = input$get_new_sample,
                                 valueExpr = {
                                   movies_subset() %>%
                                     sample_n(input$n_samp)
                                 },
                                 ignoreNULL = FALSE
  )
  
  # Create the scatterplot object the plotOutput function is expecting ------
  output$scatterplot <- renderPlot({
    ggplot(data = movies_sample(), aes_string(x = input$x, y = input$y)) +
      geom_point() +
    labs(x = toTitleCase(str_replace_all(input$x, "_", " ")),
         y = toTitleCase(str_replace_all(input$y, "_", " ")),
         color = toTitleCase(str_replace_all(input$z, "_", " "))
    )
  })
  
  # Print number of movies plotted ------------------------------------------
  output$n <- renderText({
    counts <- movies_sample() %>%
      group_by(title_type) %>%
      summarise(count = n()) %>%
      select(count) %>%
      unlist()
    paste("There are", counts, input$selected_type, "movies in this dataset.")
  })
  
  # Print data table if checked ---------------------------------------------
  output$moviestable <- DT::renderDataTable(
    if(input$show_data){
      DT::datatable(data = movies_sample()[, 1:7], 
                    options = list(pageLength = 10), 
                    rownames = FALSE)
    }
  )
  
}

# Run the application -------------------------------------------------------
shinyApp(ui = ui, server = server)

Next Steps

  1. Try to first run the exercise above based on the specifications ON YOUR OWN.
  2. Only as a last resort check out the solution in the next section.

Solution: Broken 5

With a low sample size there are not necessarily at least one of each type of movie, hence the way the paste function is written you get length coercion.

uiOutput(outputId = "n"),
output$n <- renderUI({
  types <- movies_sample()$title_type %>%
  factor(levels = input$selected_type)
  counts <- table(types)
  HTML(paste("There are",
             counts, input$selected_type,
})