Introduction

The chevron R package has a collection of functions that create standard tables, listings, and graphs (TLGs). These functions are referred in chevron as tlg-functions. A subset of these tlg-functions create tables. Tables are created using the rtables package. rtables has the context of table layouts, i.e. pre-data table cell-derivation instructions. For example:

library(rtables)
#> Loading required package: magrittr
#> Loading required package: formatters

lyt <- basic_table() %>%
  split_cols_by("ARM") %>%
  analyze("AGE", mean)

lyt
#> A Pre-data Table Layout
#> 
#> Column-Split Structure:
#> ARM (lvls) 
#> 
#> Row-Split Structure:
#> AGE (** analysis **)

These layout contain all the cell-value derivation information and can be used and reused for different data

adsl <- data.frame(
  ARM = factor(sample(c("ARM A", "ARM B"), 100, TRUE)),
  AGE = runif(100, 20, 90),
  COUNTRY = factor(sample(c("CHN", "USA", "BRA", "RUS"), 100, TRUE))
)

build_table(lyt, adsl)
#>             ARM A              ARM B     
#> —————————————————————————————————————————
#> mean   53.8902372463296   54.329995988443

build_table(lyt, adsl[adsl$COUNTRY == "RUS", ])
#>             ARM A              ARM B      
#> ——————————————————————————————————————————
#> mean   52.2339463816024   55.9801052469993

Hence these layouts define a table pre-data. That is, the layout functions in chevron do never take any data as an argument. Note that pruning and sorting are currently not performed in the layout space in rtables but rather on the actual table objects.

Also, layouts allow for certain queries such as which variables are required to build the table using the given layout:

rtables::vars_in_layout(lyt)
#> [1] "ARM" "AGE"

So in chevron when creating tables we always do the explicit layout creation in the *_lyt functions. For example for aet02_1 we have the layout function:

library(chevron)
#> Registered S3 method overwritten by 'tern':
#>   method   from 
#>   tidy.glm broom
aet02_1_lyt
#> function (armvar = .study$actualarm, lbl_overall = .study$lbl_overall, 
#>     lbl_aebodsys = "Body System or Organ Class", lbl_aedecod = "Dictionary-Derived Term", 
#>     deco = std_deco("AET02"), .study = list(actualarm = "ACTARM", 
#>         lbl_overall = NULL)) 
#> {
#>     basic_table_deco(deco) %>% split_cols_by(var = armvar) %>% 
#>         add_colcounts() %>% ifneeded_add_overall_col(lbl_overall) %>% 
#>         summarize_num_patients(var = "USUBJID", .stats = c("unique", 
#>             "nonunique"), .labels = c(unique = "Total number of patients with at least one adverse event", 
#>             nonunique = "Overall total number of events")) %>% 
#>         split_rows_by("AEBODSYS", child_labels = "visible", labels_var = "AEBODSYS", 
#>             nested = FALSE, indent_mod = -1L, split_fun = drop_split_levels, 
#>             label_pos = "topleft", split_label = lbl_aebodsys) %>% 
#>         summarize_num_patients(var = "USUBJID", .stats = c("unique", 
#>             "nonunique"), .labels = c(unique = "Total number of patients with at least one adverse event", 
#>             nonunique = "Total number of events")) %>% count_occurrences(vars = "AEDECOD", 
#>         .indent_mods = -1L) %>% append_topleft(paste0("  ", lbl_aedecod))
#> }
#> <bytecode: 0x55a9cae690b8>
#> <environment: namespace:chevron>

Note that the aet02_1_lyt function returns a concise description on how tern and rtables can be used to create the cell values of the tables. The table creation function then does then use the *_lyt function and builds the table using build_table and the data given in adam_db as shown next:

aet02_1
#> function (adam_db, armvar = .study$actualarm, lbl_overall = .study$lbl_overall, 
#>     prune_0 = TRUE, deco = std_deco("AET02"), .study = list(actualarm = "ACTARM", 
#>         lbl_overall = NULL)) 
#> {
#>     dbsel <- get_db_data(adam_db, "adsl", "adae")
#>     lyt <- aet02_1_lyt(armvar = armvar, lbl_overall = lbl_overall, 
#>         lbl_aebodsys = var_labels_for(dbsel$adae, "AEBODSYS"), 
#>         lbl_aedecod = var_labels_for(dbsel$adae, "AEDECOD"), 
#>         deco = deco)
#>     tbl <- build_table(lyt, dbsel$adae, alt_counts_df = dbsel$adsl)
#>     if (prune_0) {
#>         tbl <- prune_table(tbl)
#>     }
#>     tbl_sorted <- tbl %>% sort_at_path(path = c("AEBODSYS"), 
#>         scorefun = cont_n_onecol(ncol(tbl))) %>% sort_at_path(path = c("AEBODSYS", 
#>         "*", "AEDECOD"), scorefun = score_occurrences)
#>     tbl_sorted
#> }
#> <bytecode: 0x55a9bdad82a8>
#> <environment: namespace:chevron>

Summary

  • Layouts specify a tabulation an cell-values derivation pre-data
  • chevron exports the layout functions for each table creating tlg-function
  • these layouts are useful to understand how table is created and what variables are required in the data to create a table using the layout
  • the table creation functions focus the main part of their code on building, sorting and pruning the table
  • Headers and footers are taken by default from the standards. They can be specified with the deco argument. Note that we currently do not have all of the data base for standard headers and footers imported in chevron.