Skip to contents

A listing is two sheets keyed by output_id: listings, one row a listing (its dataset, the records it keeps, the order, rows a page), and listing_cols, one row a printed column. tfl_listing_code() writes the program – read the data, keep, sort, lay out – and tfl_listing() makes the pages from data in hand. The layout is rtfreporter’s listing_spec() / as_rtftables().

The sheets

Sheet Columns
listings output_id, type (an rtfreporter listing type; blank: multiline), dataset, where (R), sort (- for descending), max_rows, blank_row, wrap
listing_cols output_id, vars (| stacks variables in one column), label, width (characters), sep, align, collapse_repeats
spec <- tfl_listing_spec(
  listings = data.frame(output_id = "L-16-2-7", dataset = "ADAE",
                        where = 'AESER == "Y"',
                        sort = "USUBJID | ASTDY", max_rows = "15"),
  listing_cols = data.frame(
    output_id = "L-16-2-7",
    vars  = c("USUBJID", "TRT01A", "AEBODSYS | AEDECOD", "ASTDY", "AESEV"),
    label = c("Subject", "Treatment", "System organ class /\nPreferred term",
              "Study day", "Severity"),
    width = c("14", "22", "40", "8", "10"),
    collapse_repeats = c("TRUE", "TRUE", NA, NA, NA)))

A workbook is read with tfl_read_listing_spec() and written with tfl_write_listing_spec(); tfl_write_listing_spec(tfl_listing_spec(), path) gives an empty one to fill in.

The program

The data catalog – the datasets sheet of the ARD spec: each dataset’s path and derived columns – says how the data is read, so the listing’s program reads it the same way the ARD program does:

catalog <- data.frame(dataset = "ADAE", path = "data/adae.rds")
cat(tfl_listing_code(spec, "L-16-2-7", datasets = catalog), sep = "\n")
# the data: ADAE (data catalog)
adae <- readRDS("data/adae.rds")
# its records, in its order; dates as they print
data <- adae |>
  dplyr::filter(AESER == "Y") |>
  dplyr::arrange(USUBJID, ASTDY) |>
  dplyr::mutate(
    dplyr::across(dplyr::where(~ inherits(.x, c("Date", "POSIXt"))), format)
  )

lst <- listing_spec(
  list(
    listing_col("USUBJID", width = 14, label = "Subject", collapse_repeats = TRUE),
    listing_col(
      "TRT01A",
      width = 22,
      label = "Treatment",
      collapse_repeats = TRUE
    ),
    listing_col(
      c("AEBODSYS", "AEDECOD"),
      width = 40,
      label = "System organ class /\nPreferred term"
    ),
    listing_col("ASTDY", width = 8, label = "Study day"),
    listing_col("AESEV", width = 10, label = "Severity")
  ),
  type = "multiline"
)
content <- as_rtftables(data, listing = lst, max_rows = 15)

What the sheets cannot say about the data – a derived column, a join – is rework: code run on data before it is sorted, written into the program by the spec, not added afterwards by hand:

cat(tfl_listing_code(spec, "L-16-2-7", datasets = catalog,
                     rework = "data$AESEV <- tools::toTitleCase(tolower(data$AESEV))"),
    sep = "\n")
# the data: ADAE (data catalog)
adae <- readRDS("data/adae.rds")
data <- dplyr::filter(adae, AESER == "Y")

# rework
data$AESEV <- tools::toTitleCase(tolower(data$AESEV))

# in its order; dates as they print
data <- data |>
  dplyr::arrange(USUBJID, ASTDY) |>
  dplyr::mutate(
    dplyr::across(dplyr::where(~ inherits(.x, c("Date", "POSIXt"))), format)
  )

lst <- listing_spec(
  list(
    listing_col("USUBJID", width = 14, label = "Subject", collapse_repeats = TRUE),
    listing_col(
      "TRT01A",
      width = 22,
      label = "Treatment",
      collapse_repeats = TRUE
    ),
    listing_col(
      c("AEBODSYS", "AEDECOD"),
      width = 40,
      label = "System organ class /\nPreferred term"
    ),
    listing_col("ASTDY", width = 8, label = "Study day"),
    listing_col("AESEV", width = 10, label = "Severity")
  ),
  type = "multiline"
)
content <- as_rtftables(data, listing = lst, max_rows = 15)

The report around it is the report spec’s, as for a table: tfl_report_code(report_spec, "L-16-2-7", content = "content"). In a study whose report programs source the setup script (tfl_report_setup_code(): the company’s font, the study’s tokens, the running header and footer), the listing’s program is written with setup = TRUE and says only its own values, as a table’s does (see Reports).

The pages

tfl_listing() does what the program does, on data in hand. The data comes first, so it pipes:

pages <- adae |> tfl_listing(spec, "L-16-2-7")   # the sample study's ADAE
length(pages)
#> [1] 16
class(pages[[1]])
#> [1] "rtftable"

The pages go to tfl_report(report_spec, content = pages) or rtf_tables().

A listing written in code, back to a spec

tfl_as_listing_spec() writes a listing coded with rtfreporter (listing_spec(), or a plan_listing() plan) back as a listing spec, listing what the sheets cannot carry (attr(, "not_converted")):

lst <- listing_spec(list(
  listing_col("USUBJID", label = "Subject", width = 14),
  listing_col(c("AEDECOD", "AESEV"), label = "Term / Severity", width = 30)))
back <- suppressMessages(tfl_as_listing_spec(lst, "L-16-2-8", dataset = "ADAE"))
back$listing_cols[, c("vars", "label", "width")]
#>              vars           label width
#> 1         USUBJID         Subject    14
#> 2 AEDECOD | AESEV Term / Severity    30