Hi @JoeMark! Welcome!
Thanks very much for writing up a well-put-together question with a great example .
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:
- It converts the string supplied by
input$choice
into a symbol using thesym()
function - It uses the
!!
(bang-bang) operator to tellgroup_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!
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]
)