Generates readable, runnable rtables source for the display the shell describes: the population filter, the factor set-up that keeps every treatment column showing the same rows, the column layout including any spanning header, one layout step per shell row, and the build_table() call.

shell_code(shell)

Arguments

shell

A shell2tlf_shell from read_shell() or new_shell().

Value

A character vector of R source lines, one element per line, with class shell2tlf_code. print() renders it as a code block; writeLines() or cat() write it to a .R file.

Details

The output is meant to be read and edited. It is the deliverable a programmer would otherwise have hand-written from the shell document, and it is byte-for-byte what build_tlf() evaluates – so reviewing the code is reviewing the table.

A shift row emits a split_rows_by() on the baseline category with no drop_split_levels (the cross-tabulation must stay square), a summarize_row_groups() that prints the baseline denominator, and an analyze() whose afun computes the denominator named in the shell. The comment above denom in the emitted code states which population the percentages are over, so it travels with the program.

A listing shell emits no layout at all: filter, dplyr::arrange(), a data.frame() of the listed variables under their shell labels, and rtables::df_to_tt(), which turns one record into one row. There is no aggregation step to collapse duplicates, which is the point.

Examples

shell <- read_shell(system.file("extdata", "demographics.yaml",
                                package = "shell2tlf"))
code <- shell_code(shell)
code
#> ── shell2tlf generated rtables code ────────────────────────────────────────────
#> # --------------------------------------------------------------------------
#> # Table 14.1.1
#> # Summary of Demographic and Baseline Characteristics
#> # Safety Population
#> #
#> # Population: Safety Population
#> # Generated by shell2tlf from shell: demographics.yaml
#> # Edit the shell and regenerate; do not edit this file by hand.
#> # --------------------------------------------------------------------------
#> 
#> library(rtables)
#> 
#> # Population: Safety Population
#> adsl <- dplyr::filter(adsl, SAFFL == "Y")
#> 
#> # Fix the level sets so every column shows the same rows in the same order.
#> adsl <- dplyr::mutate(
#>   adsl,
#>   TRTGRP = factor(TRTGRP, levels = c("Placebo", "Xanomeline")),
#>   TRT01A = factor(TRT01A, levels = c("Placebo", "Xanomeline Low Dose", "Xanomeline High Dose")),
#>   AGEGR1 = factor(AGEGR1, levels = c("<65", "65-80", ">80")),
#>   SEX    = factor(SEX, levels = c("F", "M")),
#>   RACE   = factor(RACE, levels = c("AMERICAN INDIAN OR ALASKA NATIVE", "BLACK OR AFRICAN AMERICAN", "WHITE"))
#> )
#> 
#> lyt <- basic_table(show_colcounts = TRUE) |>
#>   split_cols_by("TRTGRP") |>
#>   split_cols_by("TRT01A", split_fun = drop_split_levels) |>
#>   add_overall_col("All Subjects") |>
#>   analyze("AGE", var_labels = "Age (years)", show_labels = "visible",
#>     afun = function(x, .N_col) {
#>       in_rows(
#>         "n"         = rcell(sum(!is.na(x)), format = "xx"),
#>         "Mean (SD)" = rcell(c(mean(x, na.rm = TRUE), stats::sd(x, na.rm = TRUE)), format = "xx.x (xx.xx)"),
#>         "Median"    = rcell(stats::median(x, na.rm = TRUE), format = "xx.x"),
#>         "Q1, Q3"    = rcell(unname(stats::quantile(x, c(0.25, 0.75), na.rm = TRUE)), format = "xx.x, xx.x"),
#>         "Min, Max"  = rcell(c(min(x, na.rm = TRUE), max(x, na.rm = TRUE)), format = "xx.x, xx.x")
#>       )
#>     }) |>
#>   analyze("AGEGR1", var_labels = "Age group (years)", show_labels = "visible",
#>     afun = function(x, .N_col) {
#>       counts <- table(x)
#>       in_rows(
#>         .list   = lapply(counts, function(k) rcell(c(k, k / .N_col), format = "xx (xx.x%)")),
#>         .labels = names(counts)
#>       )
#>     }) |>
#>   analyze("SEX", var_labels = "Sex", show_labels = "visible",
#>     afun = function(x, .N_col) {
#>       counts <- table(x)
#>       in_rows(
#>         .list   = lapply(counts, function(k) rcell(c(k, k / .N_col), format = "xx (xx.x%)")),
#>         .labels = names(counts)
#>       )
#>     }) |>
#>   analyze("RACE", var_labels = "Race", show_labels = "visible",
#>     afun = function(x, .N_col) {
#>       counts <- table(x)
#>       in_rows(
#>         .list   = lapply(counts, function(k) rcell(c(k, k / .N_col), format = "xx (xx.x%)")),
#>         .labels = names(counts)
#>       )
#>     }) |>
#>   analyze("BMIBL", var_labels = "Baseline BMI (kg/m2)", show_labels = "visible",
#>     afun = function(x, .N_col) {
#>       in_rows(
#>         "n"         = rcell(sum(!is.na(x)), format = "xx"),
#>         "Mean (SD)" = rcell(c(mean(x, na.rm = TRUE), stats::sd(x, na.rm = TRUE)), format = "xx.x (xx.xx)"),
#>         "Median"    = rcell(stats::median(x, na.rm = TRUE), format = "xx.x"),
#>         "Min, Max"  = rcell(c(min(x, na.rm = TRUE), max(x, na.rm = TRUE)), format = "xx.x, xx.x")
#>       )
#>     })
#> 
#> tbl <- build_table(lyt, adsl)
#> 
#> tbl
#> ────────────────────────────────────────────────────────────────────────────────

# keep it as a program
writeLines(code, file.path(tempdir(), "t_14_1_1.R"))

# a shift table: the denominator is named in the emitted code
shift <- read_shell(system.file("extdata", "lab_shift.yaml",
                                package = "shell2tlf"))
grep("Denominator", shell_code(shift), value = TRUE)
#> [1] "      # Denominator: distinct USUBJID in this baseline category and this"

# a listing: no layout, one record per row
shell_code(read_shell(system.file("extdata", "ae_listing.yaml",
                                  package = "shell2tlf")))
#> ── shell2tlf generated rtables code ────────────────────────────────────────────
#> # --------------------------------------------------------------------------
#> # Listing 16.2.7.1
#> # Listing of Treatment-Emergent Adverse Events
#> # Safety Population
#> #
#> # Population: Treatment-Emergent Adverse Events
#> # Generated by shell2tlf from shell: ae_listing.yaml
#> # Edit the shell and regenerate; do not edit this file by hand.
#> # --------------------------------------------------------------------------
#> 
#> library(rtables)
#> 
#> # Population: Treatment-Emergent Adverse Events
#> adae <- dplyr::filter(adae, SAFFL == "Y" & TRTEMFL == "Y")
#> 
#> # Order the records. Sorting a listing never combines records:
#> # a subject with three events keeps three rows.
#> adae <- dplyr::arrange(adae, USUBJID, AEBODSYS, AEDECOD)
#> 
#> # One column per listed variable, carrying the label from the shell.
#> # as.character() reproduces the stored value: rounding and formatting of
#> # derived variables belong in the ADaM step, not in the display.
#> lst <- data.frame(
#>   "Subject"            = as.character(adae[["USUBJID"]]),
#>   "Treatment"          = as.character(adae[["TRT01A"]]),
#>   "System Organ Class" = as.character(adae[["AEBODSYS"]]),
#>   "Preferred Term"     = as.character(adae[["AEDECOD"]]),
#>   "Severity"           = as.character(adae[["AESEV"]]),
#>   check.names = FALSE,
#>   stringsAsFactors = FALSE
#> )
#> 
#> # Row labels are the record number, so the listing shows on its own face
#> # that nrow(lst) records became nrow(lst) rows -- no de-duplication.
#> tbl <- df_to_tt(lst)
#> 
#> tbl
#> ────────────────────────────────────────────────────────────────────────────────