5

I have a shiny app that takes a dataframe, and applies group_by from dplyr. I can make it accept a single group, but I would like the selectInput to accept multiple grouping variables.

I can get around this problem by adding another selectInput, and then passing that to the group_by statement, but I'd like this to extend to an arbitrary number of variables. Therefore I need the single selectInput to accept multiple arguments.

Just adding multiple = TRUE does not pass the variables in a way that group_by understands, and this answer I was unable to adapt now that group_by_ is deprecated

Note,

The full version of this app uses fileInput, rather than a hardcoded dataset, hence the calls to renderUI, and reactive

library(shiny)
library(DT)
library(dplyr)

ui <- fluidPage(

  titlePanel("app"),

  sidebarLayout(
    sidebarPanel(

      uiOutput("groups")

    ),


    mainPanel(

      DT::dataTableOutput("summary")
    )
  )
)


server <- function(input, output) {
  mydata <- reactive({structure(list(School = structure(c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 
                                                          2L, 1L, 1L, 2L, 2L), .Label = c("School1", "School2"), class = "factor"), 
                                     Town = structure(c(1L, 1L, 2L, 2L, 1L, 1L, 2L, 2L, 2L, 1L, 
                                                        2L, 1L), .Label = c("Levin", "Wellington"), class = "factor"), 
                                     Income = c(6314L, 3546L, 3541L, 846684L, 231123L, 564564L, 
                                                545L, 1325L, 484L, 51353L, 465546L, 564546L)), .Names = c("School", 
                                                                                                          "Town", "Income"), class = "data.frame", row.names = c(NA, -12L
                                                                                                          ))})



  output$groups <- renderUI({
    df <- mydata()
    selectInput(inputId = "grouper", label = "Group variable", choices = names(df), multiple = TRUE)
  })




  summary_data <- reactive({
    req(input$grouper)
    mydata() %>%
      dplyr::group_by(!!rlang::sym(input$grouper)) %>%
      dplyr::summarise(mean_income = mean(Income), na.rm = TRUE)
  })

  output$summary <- DT::renderDataTable({
    DT::datatable(summary_data())
  })



}

shinyApp(ui, server)
Conor Neilson
  • 1,026
  • 1
  • 11
  • 27
  • I don't know much about shiny, but I know you can pass multiple variables to `group_by` like this: `library(rlang); df %>% group_by(!!!syms(c('a', 'b', 'c')))` – IceCreamToucan Apr 29 '18 at 23:51
  • 1
    You can also just do `df %>% group_by_at(c('a', 'b', 'c'))` – IceCreamToucan Apr 29 '18 at 23:55
  • @Renu That first option for `!!!` and `syms` worked great! Are you able to point me towards any docs for either of those functions? I'm having trouble finding much about them, and I feel this is probably a common question – Conor Neilson Apr 30 '18 at 00:08
  • 1
    The best I know if is https://rpubs.com/lionel-/programming-draft or http://dplyr.tidyverse.org/articles/programming.html. Unfortunately neither of those mention `syms`, but they are a good resource for understanding how to program with `dplyr` functions. – IceCreamToucan Apr 30 '18 at 00:18

1 Answers1

5

The answer for this, as Renu pointed out in the comments, was to replace !! with !!!, and sym with syms (this allows group_by to accept a list of variables, rather than a single variable.

library(shiny)
library(DT)
library(dplyr)

ui <- fluidPage(

  titlePanel("app"),

  sidebarLayout(
    sidebarPanel(

      uiOutput("groups")

    ),


    mainPanel(

      DT::dataTableOutput("summary")
    )
  )
)


server <- function(input, output) {
  mydata <- reactive({structure(list(School = structure(c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 
                                                          2L, 1L, 1L, 2L, 2L), .Label = c("School1", "School2"), class = "factor"), 
                                     Town = structure(c(1L, 1L, 2L, 2L, 1L, 1L, 2L, 2L, 2L, 1L, 
                                                        2L, 1L), .Label = c("Levin", "Wellington"), class = "factor"), 
                                     Income = c(6314L, 3546L, 3541L, 846684L, 231123L, 564564L, 
                                                545L, 1325L, 484L, 51353L, 465546L, 564546L)), .Names = c("School", 
                                                                                                          "Town", "Income"), class = "data.frame", row.names = c(NA, -12L
                                                                                                          ))})



  output$groups <- renderUI({
    df <- mydata()
    selectInput(inputId = "grouper", label = "Group variable", choices = names(df), multiple = TRUE)
  })




  summary_data <- reactive({
    req(input$grouper)
    mydata() %>%
      dplyr::group_by(!!!rlang::syms(input$grouper)) %>%
      dplyr::summarise(mean_income = mean(Income), na.rm = TRUE)
  })

  output$summary <- DT::renderDataTable({
    DT::datatable(summary_data())
  })



}

shinyApp(ui, server)
Conor Neilson
  • 1,026
  • 1
  • 11
  • 27