Skip to contents

This guide provides a detailed overview of the mod_Tplyr_table module and its features. It is meant to provide guidance to App Creators on creating Apps in DaVinci using the mod_Tplyr_table module.

Walk-throughs for sample app creation using the module are also included to demonstrate the various module specific features.

mod_Tplyr_table serves as a wrapper for displaying interactive summary tables generated with {Tplyr}. The module leverages Tplyr’s metadata extraction functionality to offer traceability from the summary tables back to the source listing. By clicking on a result cell in the table, the source listing will be generated. For displaying the listing the listings module of {dv.listings} is used, which means all functionality of that module can be leveraged.

The Tplyr table module allows multiple output tables created with Tplyr and standalone listings to be displayed under the same tab, using a drop-down selector.

Features

mod_Tplyr_table features the following outputs:

  • table created using Tplyr package
  • corresponding listing from the selected cell of the table.
  • standalone listing

dv.tables::mod_Tplyr_table() module uses several arguments with the following being mandatory and the rest optional. As part of app creation, the app creator should specify the values for these arguments as applicable.

Mandatory Arguments

  • module_id : A unique identifier of type character for the module

  • output_list: A named list defining the outputs to be generated. Each element of the list corresponds to a table or listing and must be a named list with one of the following structures:

    For summary tables:

    • tplyr_tab_fun A function that takes one or more datasets as input and returns a tplyr_table object.
    • build_fun A function that takes the tplyr_table object and returns a built table (typically using Tplyr::build()).
      Note: The metadata argument of Tplyr::build() needs to be set to TRUE, so that the corresponding listing can be shown.

    For standalone listings:

    • dataset_names A character vector of dataset names required to generate the listing. The names of the top-level list elements are used as identifiers for the outputs.
  • subjid_var Column corresponding to subject ID. Default value is ‘USUBJID’

(Optional) Arguments for listing customization

The module uses {dv.listings} for showing drill-down listing data. Therefore additional arguments can be provided for further customization.

default_vars, pagination, intended_use_label, receiver_id, review are all arguments that are passed through to {dv.listings}. more information can be found here

Example

To better understand the set up of the module you can follow the below example:

First load dummy data:

# load demo data
dm <- pharmaversesdtm::dm

Create a Tplyr table and wrap it into a function.

my_tplyr_fun <- function(dm) {
  tab <- Tplyr::tplyr_table(dm, ARM) |>
    Tplyr::add_layer(Tplyr::group_desc(AGE, by = "Age (years)")) |>
    Tplyr::add_layer(Tplyr::group_count(SEX, by = "Sex")) |>
    Tplyr::add_layer(Tplyr::group_count(RACE, by = "Race"))
  return(tab)
}

Create a build function for the tplyr table created in the first step

build_func <- function(tab) {
  Tplyr::build(tab, metadata = TRUE) |>
    dplyr::mutate(
      row_label2 = ifelse(row_label2 == row_label1, "Total (%)", row_label2)
      ) |>
    Tplyr::apply_row_masks(row_breaks = TRUE)
}

Create the output_list passed to dv.tables::mod_Tplyr_table

output_list <- list(
  "Demographic" = list(
    tplyr_tab_fun = my_tplyr_fun,
    build_fun = build_func
  )
)

Put everything together and start the app within {dv.manager}

module_list <- list(
  "Table" = dv.tables::mod_Tplyr_table(
    module_id = "tplyr_table",
    output_list = output_list
  )
)

dv.manager::run_app(
  data = list("My data" = list(dm = dm)),
  module_list = module_list,
  filter_data = "dm",
  filter_type = "datasets"
)

It’s also possible to add more than one table into the module.

# load additional demo data
dm <- pharmaversesdtm::dm 

# create second tplyr table
my_tplyr_fun2 <- function(dm, ae) {
  dm_arm <- dm |> dplyr::select(USUBJID, ARM)
  ae_arm <- ae |> dplyr::inner_join(dm_arm, by = "USUBJID")
  
  tab <- Tplyr::tplyr_table(ae_arm, ARM) |>
    Tplyr::set_pop_data(dm) |>
    Tplyr::set_pop_treat_var(ARM) |>
    Tplyr::add_layer(
      Tplyr::group_count("All subjects") |>
        Tplyr::set_distinct_by(USUBJID) |>
        Tplyr::set_format_strings(Tplyr::f_str("xx", distinct_total))
    ) |>
    Tplyr::add_layer(
      Tplyr::group_count("Subjects with adverse events") |>
        Tplyr::set_distinct_by(USUBJID) |>
        Tplyr::set_format_strings(Tplyr::f_str("xx (xx %)", distinct_n, distinct_pct))
    ) |>
    Tplyr::add_layer(
      Tplyr::group_count(AESEV, by = "Adverse event severity") |>
        Tplyr::set_distinct_by(USUBJID) |>
        Tplyr::set_format_strings(Tplyr::f_str("xx (xx %)", distinct_n, distinct_pct))
    ) |>
    Tplyr::add_layer(
      Tplyr::group_count("Subjects with serious AE", where = AESER == "Y") |>
        Tplyr::set_distinct_by(USUBJID) |>
        Tplyr::set_format_strings(Tplyr::f_str("xx (xx %)", distinct_n, distinct_pct))
    )
  return(tab)
}

# create secod build function 
build_func2 <- function(tab) {
  Tplyr::build(tab, metadata = TRUE) |>
    Tplyr::apply_row_masks(row_breaks = TRUE)
}

# put second table into the output_list
output_list <- list(
  "Demographic" = list(
    tplyr_tab_fun = my_tplyr_fun,
    build_fun = build_func
  ),
  "Adverse Events" = list(
    tplyr_tab_fun = my_tplyr_fun2,
    build_fun = build_func
  )
)

# put everything together 
module_list <- list(
  "Table" = dv.tables::mod_Tplyr_table(
    module_id = "tplyr_table",
    output_list = output_list
  )
)
# start app
dv.manager::run_app(
  data = list("My data" = list(ae = ae, dm = dm)),
  module_list = module_list,
  filter_data = "dm",
  filter_type = "datasets"
)

As a last step you can also add a standalone listing to the module

# Add additional entry for standalone listings
output_list <- list(
  "Demographic" = list(
    tplyr_tab_fun = my_tplyr_fun,
    build_fun = build_func
  ),
  "Adverse Events" = list(
    tplyr_tab_fun = my_tplyr_fun2,
    build_fun = build_func
  ),
  "Listing" = list(
    dataset_names = c("dm", "ae")
  )
)
# put everything together 
module_list <- list(
  "Table" = dv.tables::mod_Tplyr_table(
    module_id = "tplyr_table",
    output_list = output_list
  )
)
# start app
dv.manager::run_app(
  data = list("My data" = list(ae = ae, dm = dm)),
  module_list = module_list,
  filter_data = "dm",
  filter_type = "datasets"
)

In mod_tplyr_table function, grouping variable levels with no data may be omitted as unused factor levels are dropped by default. To ensure all levels appear in the output, even those with zero counts, explicitly define the levels of the grouping variable as factors.

Here’s an example where we ensure all levels of the ARM variable (including an “empty level”) are retained in the table:

# Create table function
my_tplyr_fun <- function(dm) {
  dm <- dm |>
    # to preserve all levels
    dplyr::mutate(
      ARM = factor(ARM, levels = c("Placebo", "Xanomeline High Dose", 
                                   "Xanomeline Low Dose", "Screen Failure", 
                                   "empty level"))
    )
  
  tab <- Tplyr::tplyr_table(dm, ARM) |>
    Tplyr::add_layer(Tplyr::group_desc(AGE, by = "Age (years)")) |>
    Tplyr::add_layer(Tplyr::group_count(SEX, by = "Sex")) |>
    Tplyr::add_layer(Tplyr::group_count(RACE, by = "Race"))
  return(tab)
}

# Building the table
build_func <- function(tab) {
  Tplyr::build(tab, metadata = TRUE)
}