--- title: "Filter Panel for Developers" author: "NEST CoreDev" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Filter Panel for Developers} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ## Filter Panel API ### Getting and setting filter states All filter panel classes have dedicated methods to set and get the current filter state. These methods include: - `get_filter_state` - returns current state of filters as `teal_slices` object - `set_filter_state` - adds or modifies filters based on `teal_slices` object - `remove_filter_state` - removes particular filter states based on `teal_slices` object - `clear_filter_states` - removes all filter states Setting and getting filter states are done through `teal_slices` object which is a collection of `teal_slice` objects. Think of a `teal_slice` as a quantum of information that fully describes the filter state of one variable. In order to tell `FilteredData` to set a filter for a specific variable, one must call the `set_filter_state` method with a `teal_slices` object containing a `teal_slice` that refers to the variable of interest. To remove a particular `FilterState` object, one must call the `remove_filter_state` method using a `teal_slices` containing a `teal_slice` that refers to the respective variable. Each `teal_slice` object contains all the information necessary to: 1. Determine the column in the data set on which to apply the filter expression: - `dataname` - name of the data set - `varname` - name of the column - `experiment` (only for `MultiAssayExperiment` objects) - name of the experiment - `arg` (only for `SummarizedExperiment` objects, e.g within a `MultiAssayExperiment`) - name of the argument in the call to `subset` (`subset` of `select`) 2. Express or store the current selection state: - `selected` - selected values or limits of the selected range - `keep_inf` - determines if `Inf` values should be dropped - `keep_na` - determines if `NA` values should be dropped - `expr` - explicit logical expression 3. Control the behavior and appearance of the `FilterState` object: - `choices` - determines set of values or range that can be selected from - `multiple` (only for `ChoiceFilterState`) - allows multiple values to be selected - `fixed` - forbids changing state of the `FilterState` - `anchored` - forbids removing the `FilterState` - `title` - displayed title of the filter card In addition, every `teal_slice` object has an `id`. It is impossible to create `FilteredData` with slices with duplicated `id`s. This is because filter states are both created and modified with the `set_filter_state` method so if two consecutive calls to `set_filter_state` are passed a `teal_slice` with the same id, the first call will instantiate a `FilterState`, and the second call will modify it. Creating `teal_slices` with slices with duplicated `id`s is forbidden and will raise an error. #### 1. Setting the filter state ```{r, message=FALSE, warning=FALSE} library(teal.slice) datasets <- init_filtered_data(list(iris = iris, mtcars = mtcars)) set_filter_state( datasets = datasets, filter = teal_slices( teal_slice(dataname = "iris", varname = "Species", selected = "virginica", keep_na = FALSE), teal_slice(dataname = "mtcars", id = "4 cyl", title = "4 Cylinders", expr = "cyl == 4"), teal_slice(dataname = "mtcars", varname = "mpg", selected = c(20.0, 25.0), keep_na = FALSE, keep_inf = FALSE), include_varnames = list(iris = c("Species", "Sepal.Length")), exclude_varnames = list(mtcars = "cyl") ) ) ``` #### 2. Updating filter states. *Works only in the `shiny` reactive context. ```{r, message=FALSE, warning=FALSE} set_filter_state( datasets = datasets, filter = teal_slices( teal_slice(dataname = "mtcars", varname = "mpg", selected = c(22.0, 25.0)) ) ) ``` #### 3. Getting the filter state ```{r, message=FALSE, warning=FALSE} get_filter_state(datasets) ``` #### 4. Removing filter states ```{r, message=FALSE, warning=FALSE} remove_filter_state( datasets = datasets, filter = teal_slices( teal_slice(dataname = "iris", varname = "Species") ) ) ``` #### 5. Clearing the filter state ```{r, message=FALSE, warning=FALSE} clear_filter_states(datasets) ``` ### Controlling settings of the filter panel In addition to controlling individual filter states through `set_filter_state`, one can also manage some general behaviors of the whole filter panel. This can be done with arguments of the `teal_slices` function: 1. `include_varnames` defines which columns in the used data sets are allowed to be filtered on. In the following example only two columns of `iris` and two columns of `mtcars` will be able to have filters set. ```{r} set_filter_state( datasets, teal_slices( include_varnames = list( iris = c("Species", "Sepal.Length"), mtcard = c("cyl", "mpg") ) ) ) ``` 2. `exclude_varnames` defines which columns in the used data sets are **not** allowed to be filtered on. In the following example all variables except the four will be available to choose from. ```{r} set_filter_state( datasets, teal_slices( exclude_varnames = list( iris = c("Species", "Sepal.Length"), mtcard = c("cyl", "mpg") ) ) ) ``` 3. `count_type` defines how observation counts are displayed in filter cards | "none" | "all" | |--------|-------| | Distribution in unfiltered data | Filtered vs. unfiltered distribution | | ![](./images/filter_panel/count_type_none.jpeg){width=300} | ![](./images/filter_panel/count_type_all.jpeg){width=300} | 4. `allow_add` determines whether the "Add Filter Variables" module will be displayed to allow the user to add new filters. ## Filter panel as a module All the instructions herein can be utilized to build a `shiny` app. ```{r} library(shiny) # initializing FilteredData datasets <- init_filtered_data(list(iris = iris, mtcars = mtcars)) # setting initial filters set_filter_state( datasets = datasets, filter = teal_slices( teal_slice(dataname = "iris", varname = "Species", selected = "virginica", keep_na = FALSE), teal_slice(dataname = "mtcars", id = "4 cyl", title = "4 Cylinders", expr = "cyl == 4"), teal_slice(dataname = "mtcars", varname = "mpg", selected = c(20.0, 25.0), keep_na = FALSE, keep_inf = FALSE), include_varnames = list(iris = c("Species", "Sepal.Length")), exclude_varnames = list(mtcars = "cyl"), count_type = "all", allow_add = TRUE ) ) ui <- fluidPage( shinyjs::useShinyjs(), fluidRow( column( width = 9, id = "teal_primary_col", tagList( actionButton("add_species_filter", "Set iris$Species filter"), actionButton("remove_species_filter", "Remove iris$Species filter"), actionButton("remove_all_filters", "Remove all filters"), verbatimTextOutput("rcode"), verbatimTextOutput("filter_state") ) ), column( width = 3, id = "teal_secondary_col", datasets$ui_filter_panel("filter_panel") ) ) ) server <- function(input, output, session) { # calling filter panel module datasets$srv_filter_panel("filter_panel") # displaying actual filter states output$filter_state <- renderPrint(print(get_filter_state(datasets), trim = FALSE)) # displaying reproducible filter call output$rcode <- renderText( paste( sapply(c("iris", "mtcars"), datasets$get_call), collapse = "\n" ) ) # programmatic interaction with FilteredData observeEvent(input$add_species_filter, { set_filter_state( datasets, teal_slices( teal_slice(dataname = "iris", varname = "Species", selected = c("setosa", "versicolor")) ) ) }) # programmatic removal of the FilterState observeEvent(input$remove_species_filter, { remove_filter_state( datasets, teal_slices( teal_slice(dataname = "iris", varname = "Species") ) ) }) observeEvent(input$remove_all_filters, clear_filter_states(datasets)) } if (interactive()) { shinyApp(ui, server) } ```