Skip to contents

There are times when an app developer wants to showcase more than just one fixed slice of their dataset in their custom module. Relinquishing control of the application to a user demands the developer gives their users a degree of freedom. In case of analyzing data, teal allows app developers to open up their applications to users, letting them decide exactly what app data to analyze in the module.

A lot of teal modules use data_extract_spec objects and modules to tackle user input. You can find many examples in e.g. teal.modules.general and teal.modules.clinical.

data_extract_spec

data_extract_spec’s task is two-fold: create a UI component in a shiny application and pass the user input from the UI to the module itself. Having that formulated, let’s have a look at how it supports both its responsibilities.

Example module

In order to showcase different initialization options of data_extract_spec, first we define a shiny module which uses data_extract_ui and data_extract_srv designed to handle data_extract_spec objects. The module creates a UI component for single data_extract_spec and prints a list of values returned from data_extract_srv module. Please see package documentation for more information about data_extract_ui and data_extract_srv.

library(teal.transform)
#> Loading required package: magrittr
library(shiny)

extract_ui <- function(id, data_extract) {
  ns <- NS(id)
  teal.widgets::standard_layout(
    output = teal.widgets::white_small_well(verbatimTextOutput(ns("output"))),
    encoding = data_extract_ui(ns("data_extract"), label = "variable", data_extract)
  )
}

extract_srv <- function(id, datasets, data_extract) {
  moduleServer(id, function(input, output, session) {
    reactive_extract_input <- data_extract_srv("data_extract", datasets, data_extract)
    s <- reactive({
      format_data_extract(reactive_extract_input())
    })
    output$output <- renderPrint({
      cat(s())
    })
  })
}

Example data

teal.transform functions depend on a FilteredData object from the teal.slice package. Normally, FilteredData is created automatically by teal::init, but for example purposes we define a wrapper function to initialize the necessary object.

sample_filtered_data <- function() {
  # create TealData
  adsl <- teal.data::cdisc_dataset("ADSL", scda::synthetic_cdisc_data("latest")$adsl)
  adtte <- teal.data::cdisc_dataset("ADTTE", scda::synthetic_cdisc_data("latest")$adtte)
  data <- teal.data::cdisc_data(adsl, adtte)

  # covert TealData to FilteredData
  datasets <- teal.slice:::filtered_data_new(data)
  teal.slice:::filtered_data_set(data, datasets)
  datasets
}

datasets <- sample_filtered_data()

Consider the following example, where we create two UI elements, one to filter on a specific level from SEX variable, and a second one to select a variable from c("BMRKR1", "AGE"). data_extract_spec object is handed over to the shiny app and gives instructions to generate UI components.

simple_des <- data_extract_spec(
  dataname = "ADSL",
  filter = filter_spec(vars = "SEX", choices = c("F", "M")),
  select = select_spec(choices = c("BMRKR1", "AGE"))
)

Shiny app

Finally, we pass extract_ui to the ui of the shinyApp and we use extract_srv in the server function of the shinyApp:

shinyApp(
  ui = fluidPage(extract_ui("data_extract", simple_des)),
  server = function(input, output, session) {
    extract_srv("data_extract", datasets, simple_des)
  }
)