Author: NEST CoreDev

# Introduction to teal.slice

#### NEST CoreDev

## Introduction

The `teal.slice` package provides a filter panel for `shiny` applications. `teal.slice` creates a filter panel module that allows for interactive filtering of data stored in `data.frame` and `MultiAssayExperiment` objects. It also displays filtered and unfiltered observation counts.

## Information for users of the filter panel in applications

The filter panel contains three panels:

1. The top panel displays the number of records (observations) that remain after the filters have been applied. For relational data (where `join_keys` is specified), the number of unique subjects remaining in the filtered data set is also shown.
2. The middle panel displays the current active filters and allows the user to change their state.
3. The bottom panel allows to add new filters on columns of the data sets in the app.

Note that multiple filters for the same data set are combined with the `AND` operator and it is not possible to apply hierarchical filters.

An example of the filter panel in use is shown in the image below. The `iris` data set has the 50 `versicolor` records removed, leaving 100 observations. The `mtcars` data set has filtered out all records other than those with `(gear = 3 or gear = 5) and (disp in [205, 295])`.

Depending on the data type of the selected variable, different filters are shown, for example date pickers, range sliders, checkbox inputs.

For relational data (when `join_keys` are specified), filters specified on a parent data set (i.e. `ADSL`) will apply to all child data sets, whereas filters for a child data set can only be specified on columns not found in that data set’s parent. For general relational data the filter panel treats each data set independently, irrespective of relationships between data sets.

The filter panel also supports filtering `MultiAssayExperiment` objects, where filters can be applied to the subject data, which is stored in the `colData` slot and accessed with `colData(MAE)`, as well as to experiments, which are stored in the `ExperimentList` slot and accessed with `MAE[["experiment name"]]`.

## Information for R developers using the filter panel

The example app below shows how to embed the filter panel inside a `shiny` application.

The filter panel is entirely contained within a `FilteredData` object. `FilteredData` is an `R6` class that stores data sets, tracks filter states, constructs and applies filter calls, and can output filtered data. It also contains `shiny` modules (both UI and server functions) that make up the panel itself.

In a `teal` application the `FilteredData` object (`datasets`) is created automatically by `teal`’s `init` function and filter panel components are automatically placed on the right-hand side of the page.

Note that this example uses `shiny::dataTableOutput` and `shiny::renderDataTable`. This can lead to issues with Bootstrap, especially version 4. We recommend using `DT::dataTableOutput` and `DT::renderDataTable` if possible. See the `shiny` and `DT` documentation for more information.

To facilitate communication with `FilteredData`, we provide the filter panel API. The example below uses the `set_filter_state` function to set state and the state is specified with `teal_slices` and `teal_slice` functions. For details please see the Filter panel for developers vignette.

    library(shiny)
    library(teal.slice)

    # create a FilteredData object
    datasets <- init_filtered_data(list(iris = iris, mtcars = mtcars))

    # setting initial state
    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(
      fluidRow(
        column(
          width = 9,
          tabsetPanel(
            tabPanel(title = "iris", dataTableOutput("iris_table")),
            tabPanel(title = "mtcars", dataTableOutput("mtcars_table"))
          )
        ),
        # ui for the filter panel
        column(width = 3, datasets$ui_filter_panel("filter_panel"))
      )
    )

    ## `shiny::dataTableOutput()` is deprecated as of shiny 1.8.1.
    ## Please use `DT::DTOutput()` instead.
    ## Since you have a suitable version of DT (>= v0.32.1), shiny::dataTableOutput() will automatically use DT::DTOutput() under-the-hood.
    ## If this happens to break your app, set `options(shiny.legacy.datatable = TRUE)` to get the legacy datatable implementation (or `FALSE` to squelch this message).
    ## See <https://rstudio.github.io/DT/shiny.html> for more information.
    ## `shiny::dataTableOutput()` is deprecated as of shiny 1.8.1.
    ## Please use `DT::DTOutput()` instead.
    ## Since you have a suitable version of DT (>= v0.32.1), shiny::dataTableOutput() will automatically use DT::DTOutput() under-the-hood.
    ## If this happens to break your app, set `options(shiny.legacy.datatable = TRUE)` to get the legacy datatable implementation (or `FALSE` to squelch this message).
    ## See <https://rstudio.github.io/DT/shiny.html> for more information.

    server <- function(input, output, session) {
      # this is the shiny server function for the filter panel and the datasets
      # object can now be used inside the application
      datasets$srv_filter_panel("filter_panel")

      # get the filtered datasets and put them inside reactives for analysis
      iris_filtered_data <- reactive(datasets$get_data(dataname = "iris", filtered = TRUE))
      mtcars_filtered_data <- reactive(datasets$get_data(dataname = "mtcars", filtered = TRUE))

      output$iris_table <- renderDataTable(iris_filtered_data())
      output$mtcars_table <- renderDataTable(mtcars_filtered_data())
    }

    if (interactive()) {
      shinyApp(ui, server)
    }