R Shiny tableOutput() and renderTable() does not generate a summary table output

Hi @JoeMark! Welcome!

Thanks very much for writing up a well-put-together question with a great example :grin:.

So you've run smack into one of the trade-offs that comes with dplyr's use of non-standard evaluation. That's the trick that lets you write group_by(campus) in your pipeline without having to explain to R where in the world campus is supposed to come from — compare to other situations where you'd have to write info$campus. The trade-off is that group_by() expects to receive a bare symbol as an argument — something that's easy to supply by typing directly, but a little tricky to pass around inside other variables.

The other half of the problem is that input$choice supplies a character string, not a bare symbol. It's evaluating to "campus", not campus, and these turn out to be very different things from R's perspective. If you try running:

info %>% group_by("campus") %>% summarise(funded = n())

you'll see that you get the same unhelpful result as your shiny app is giving you. To understand why, look at the result before summarizing:

info %>% group_by("campus")
#> # A tibble: 10 x 6
#> # Groups:   "campus" [1]
#>    campus   block qualification                  type  employer `"campus"`
#>    <chr>    <chr> <chr>                          <chr> <chr>    <chr>     
#>  1 athlone  l0    "l2: automotive repair &\n  m… f     null     campus    
#>  2 athlone  l0    l2: automotive repair & maint… f     null     campus    
#>  3 athlone  l0    "l2: automotive\n  repair & m… f     null     campus    
#>  4 athlone  l0    l2: automotive repair & maint… f     null     campus    

Confronted with a character string, group_by() assumed you wanted to create and then group on a new variable named "campus" (quotes included!) whose values are all the same — therefore there's only one group.

Here's a version of your shiny app that works the way you want:

library(shiny)
library(tidyverse)

info <- structure(list(campus = c("athlone", "athlone", "athlone",
  "athlone","pinelands", "pinelands", "pinelands", "pinelands",
  "pinelands","pinelands"), block = c("l0", "l0", "l0", "l0", "l0", "l0",
  "l0","ys", "ys", "ys"), qualification = c("l2: automotive repair &
  maintenace nqf","l2: automotive repair & maintenace nqf", "l2: automotive
  repair & maintenace nqf","l2: automotive repair & maintenace nqf", "l2:
  electrical engineering nqf","l2: electrical engineering nqf", "l2:
  electrical engineering nqf","ncv3: electrical infrastructure cnstr l3",
  "ncv3: electrical infrastructure cnstr l3","ncv3: electrical infrastructure
  cnstr l3"), type = c("f", "f","f", "f", "f", "f", "f", "e", "e", "e"),
  employer = c("null","null", "null", "null", "null", "null", "null", "null",
  "null","null")), row.names = c(NA, 10L), class = "data.frame")

ui <- fluidPage(
  
  selectInput(inputId = "choice",
              label = "Select group",
              choices = names(info),
              selected = c("qualification")),
  
  tableOutput(outputId = "fund")
)

server <- function(input, output) {
  headcount <- reactive({
    req(input$choice)
    info %>% group_by(!! sym(input$choice)) %>% summarise(funded = n())
  })
  
  # Headcount
  output$fund <- renderTable({
    headcount()
  })
}

shinyApp(ui = ui, server = server)

(I simplified it a bit — the select() step was trying to select non-existent columns of the data frame)

This code does two things:

  1. It converts the string supplied by input$choice into a symbol using the sym() function
  2. It uses the !! (bang-bang) operator to tell group_by() "OK, now evaluate that expression I gave you" (sym(input$choice), which evaluates to the selected variable as a bare symbol).

Both steps are necessary:

info %>% group_by(sym(input$choice)) %>% summarise(funded = n())
#> Error in mutate_impl(.data, dots): Column `sym(input$choice)` is of unsupported type symbol

Just like when we passed in a string, group_by() is trying to make a new column and group on it, but this time it fails because dplyr doesn't support a column full of bare symbols.

The bigger picture

The system for working with non-standard evaluation that dplyr is using is called tidy evaluation. It's a relatively new thing that's gradually rolling out to the whole tidyverse, so it's worth wrapping your head around.

Some starting points:

The vignette on programming with dplyr.

A short list of places to get help with tidy eval in ggplot2 (videos!)

Good luck with your app! :grin:

An aside on testing shiny code...

You are definitely on the right track with the general pattern of working on your code logic outside of shiny! Here's a little trick that can help you catch problems like these sooner.

When you're testing code outside of shiny, try defining a list called input with elements that mimic the structure of input inside of shiny. Then pass input$thing_name into your code where the input values belong. This will make you more aware of when your code is expecting a directly-typed symbol and getting a string or a number from an input, instead.

For this example, the mocked-up input might look like:

input <- list(
  choice = names(info)[1]
)
3 Likes