Reactive selectizeInput filter does not return correct number of records in datatable

I have an interesting problem with the selectizeInput()function. I am not sure if this is a bug or if it's a problem with how I am handling the dataset.

In the context of my reproducible code example provided below, I have this to test:

sum(cars$cyl==4 |  cars$cyl==6)

The total records displayed should = 18.

However, using the reproducible code provided below, if you were to use the selectizeInput()for the ID value of cars$cyl, the number of records displayed is not correct.

If you select "4" and the "6" (in that order), it returns 10 records. But if you select "6" and then "4" it returns 8 records. In neither case does it return the correct value of 18. (Yet 10+8 does indeed = 18).

What is going on? Why is it that when I select multiple values it does not retrieve the correct number of records associated with the cars$cylcategory?

Reproducible Code:

##  ----- LOAD REQUIRED PACKAGES -----
library(shiny)
library(DT)
library(ggplot2)
library(shinyjs)


## ------ LOAD EXAMPLE DATA ------

cars = mtcars

## ==== UI ====

ui <- fluidPage(
  useShinyjs(),
  titlePanel("A DataTable"),
  fluidRow(
    column(4,
           textInput("reportText", label="Report Name", value="Enter Report Name", width=NULL))
  ),
  
  # Create a new Row in the UI for selectInputs
  fluidRow(
    column(4,
           selectizeInput("cyl",
                       "Cylinders:",
                       c("All",
                         unique(as.character(cars$cyl))), multiple = TRUE)
    ),
    column(4,
           selectizeInput("gear",
                       "Gears:",
                       c("All",
                         unique(as.character(cars$gear))), multiple = TRUE)
    ),
    column(4,
           selectizeInput("carb",
                       "Carburators:",
                       c("All",
                         unique(as.character(cars$carb))), multiple = TRUE)
    )
  ),
  # Create a new row for the table.
  
  DT::dataTableOutput("dt"),
  
  # Create a new row for the download button
  
  downloadButton(outputId = "download_filtered",
                 label = "Download Filtered Data"),
  
  # Create a new row for the reset button
  actionButton("clear", label = "Clear all filters",  icon ("eraser"), class = "down")
)
## ==== END UI ====

## ==== SERVER ====

server = function(input, output, session){
  
  # ---Reset Filters Button -----
  
  observeEvent(input$clear, {
    
    updateTextInput(session, "reportText", label = "Enter Report Name", value = "Enter Report Name")
    
    updateSelectizeInput(session,"cyl", label = "Filter Cylinder:",
                         choices = c("All",
                                     unique(sort(as.character(cars$cyl)))))
    
    updateSelectizeInput(session,"gear",label = "Filter Gear",
                         choices = c("All",
                                     unique(as.character(cars$gear))))
    
    updateSelectizeInput(session,"carb", label = "Filter Carb",
                         choices = c("All",
                                     unique(as.character(cars$carb))))
  })
  
  ## Create a reactive DF function for filters:
  
  reactiveDF = reactive({
    cardata <- cars
    
    # conditionals 
    
    if (input$cyl != "All") {
      cardata <- cardata[cardata$cyl == input$cyl, ]
    }
    if (input$gear != "All") {
      cardata <- cardata[cardata$gear == input$gear, ]
    }
    if (input$carb != "All") {
      cardata <- cardata[cardata$carb == input$carb, ]
    }
    
    # display the filtered data
    cardata
    
  })
  
  output$filtered_row <- 
    renderPrint({
      input[["dt_rows_all"]]
    })
  
  ## Output Datatable -------
  output$dt <- DT::renderDataTable(datatable({
    reactiveDF()
  }))
  
  
  
  output$download_filtered <-
    downloadHandler(
      filename = function() {
        repname <- "filtered_report.csv"
        if (isTruthy(input$reportText)) {
          if (input$reportText != "Enter Report Name") {
            repname <- paste(input$reportText, ".csv", sep="")
          }
        }
        repname
      },
      content = function(file) {
        write.csv(reactiveDF(), file)
      }
    )
  
  # Reset button observe event 
  observeEvent(input$clear, {
    shinyjs::reset("filter-pane")
  })
}
  shinyApp(ui,server)

Make these changes (using %in%):

  reactiveDF = reactive({
    cardata <- cars
    
    # conditionals 
    
    if (input$cyl != "All") {
      cardata <- cardata[cardata$cyl %in% input$cyl, ]
    }
    if (input$gear != "All") {
      cardata <- cardata[cardata$gear %in% input$gear, ]
    }
    if (input$carb != "All") {
      cardata <- cardata[cardata$carb %in% input$carb, ]
    }
    
    # display the filtered data
    cardata
    
  })

1 Like

@williaml -
Thank you so much! So to summarize - I needed to "test" the conditional statement against whether or not the values (numbers in this case) belong to a vector or dataframe. I suspected it might have to do with the values and how it was being interpreted. Thanks for helping me piece it together.

For other newbies - From documentation about %in%

The %in% operator in R can be used to identify if an element (e.g., a number) belongs to a vector or dataframe. For example, it can be used the see if the number 1 is in the sequence of numbers 1 to 10.

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.