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,
- If I select Age:
10-19
from the drop down list, then this should be passed to the data and doDT[Age %in% "10-19"]
- Similary, if I select Age:
10-19
and Gender:Female
, then these should be passed to the data asDT[Age %in% "10-19" & Gender %in% "Female"]
- If I deselect
Age
, then the data will return Gender:Female
, such asDT[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)