Home > Blockchain >  How to filter multiple variables in a data.frame/data.table in Shiny with various combinations
How to filter multiple variables in a data.frame/data.table in Shiny with various combinations

Time:11-15

If I have a data.frame/data.table with multiple columns needed to be filtered and, later passed to other calculations, how can I filter the data without creating multiple combinations of filtering conditions using if else.

For example, if I have a data with Age, Gender, Ethnicity, and created three selectInput().

What I would like to achieve is that,

  1. If I select Age: 10-19 from the drop down list, then this should be passed to the data and do DT[Age %in% "10-19"]
  2. Similary, if I select Age: 10-19 and Gender: Female, then these should be passed to the data as DT[Age %in% "10-19" & Gender %in% "Female"]
  3. If I deselect Age, then the data will return Gender: Female, such as DT[Gender %in% "Female"]

How can I capture those conditions, and pass to the data filter automatically without explicitly going through those combinations?

Here is a non-working testing example

df <- data.table(AgeGroup = sample(c("0-9", "10-19", "20-29"), 20, replace = TRUE), 
                 Sex = sample(c("Male", "Female"), 20, replace = TRUE))

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      selectInput("AgeGroup", "Age Group", choices = c("", unique(df$AgeGroup))), 
      selectInput("Sex", "Sex", choices = c("", unique(df$Sex)))
    ), 
    mainPanel(
      tableOutput("table")
    )
  )
)

server <- function(input, output, session) {
  
  # How to modify here so that we don't need to do
  # `if (input$AgeGroup) df[AgeGroup == input$AgeGroup]`
  # consider multiple filters, some filters are selected and some are not. 
  # For example, if there are 5 filters, there would be 2^5 combinations
  
df_out <- reactive(df)
  
  output$table <- renderTable(df_out())
}

shinyApp(ui, server)

CodePudding user response:

We can use | and & to build a filter statement. The trick is to say input$a is either "" (which means return all rows) or a is input$a. You can use %in% instead of == when using multiple input values.

library(shiny)
library(data.table)

df <- data.table(a = c("a", "b", "c"), 
                 b = 1:3)

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      selectInput("a", "Select A", choices = c("", c("a", "b", "c"))), 
      selectInput("b", "Select B", choices = c("", c(1, 2, 3)))
    ), 
    mainPanel(
      tableOutput("table")
    )
  )
)

server <- function(input, output, session) {
  
  df_out <- reactive(df[(input$a == "" | a == input$a) &
                        (input$b == "" | b == input$b),])
  
  output$table <- renderTable(df_out())
}

shinyApp(ui, server)

A more programmatic solution is to use vapply() and wrap the result in rowMeans():

library(shiny)
library(data.table)

df <- data.table(a = c("a", "b", "c"), 
                 b = 1:3)

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      selectInput("a", "Select A", choices = c("", c("a", "b", "c"))), 
      selectInput("b", "Select B", choices = c("", c(1, 2, 3)))
    ), 
    mainPanel(
      tableOutput("table")
    )
  )
)

server <- function(input, output, session) {
  
  
  
  df_out <- reactive({
    
    idx_vec <- vapply(c("a", "b"),
                              FUN.VALUE = logical(nrow(df)),
                              FUN = function(x) {
                                input[[x]] == "" | df[[x]] == input[[x]]
                                })
    
    df[rowMeans(idx_vec) >= 1,]
    
  }) 
    
  
  output$table <- renderTable(df_out())
}

shinyApp(ui, server)
  • Related