arframe TFL Gallery
  1. Tables
  2. Laboratory Worst Toxicity Grade
  • Getting Started
    • Installation

  • Tables
    • Study Conduct
    • Enrollment by Country and Site

    • Study Population
    • Demographics Summary
    • Medical History
    • Prior Medication
    • Disposition Summary
    • Analysis Populations

    • Extent of Exposure
    • Concomitant Medications
    • Extent of Exposure

    • Safety
    • Adverse Events by System Organ Class and Preferred Term
    • AEs Related to Study Drug
    • Common Adverse Events
    • Adverse Events by Grade / Intensity
    • Overall Safety Summary
    • Adverse Events with Event Counts
    • Exposure-Adjusted Adverse Events
    • Adverse Events by Subgroup
    • Serious Adverse Events by SOC and PT
    • AEs Leading to Study Drug Discontinuation
    • Death Summary
    • Vital Signs
    • Laboratory Results - Chemistry
    • Laboratory Shift Table
    • Laboratory Worst Toxicity Grade
    • Laboratory Marked Abnormalities
    • Electrocardiogram Summary

    • Efficacy
    • Time to Event Summary
    • Best Overall Response

  • Listings
    • Adverse Event Listing
    • Demographic Characteristics Listing
    • Medical History Listing
    • Vital Signs Listing
    • Laboratory Test Results Listing
    • Concomitant Medications Listing

  • Figures
    • Kaplan-Meier Plot
    • Swimmer Plot
    • Waterfall Plot

On this page

  • Setup
  • Data Preparation
  • arframe Pipeline
  • Rendered Table
  1. Tables
  2. Laboratory Worst Toxicity Grade

Laboratory Worst Toxicity Grade

Worst Post-Baseline Toxicity Grade by Parameter

Setup

See Prerequisites for installation instructions.

library(arframe)
library(pharmaverseadam)
library(dplyr, warn.conflicts = FALSE)
library(tidyr)

# Chemistry panel — 6 key parameters
param_subset <- c("ALT", "AST", "BILI", "CREAT", "GLUC", "SODIUM")

adlb_saf <- pharmaverseadam::adlb |>
  blank_to_na() |>
  filter(
    SAFFL   == "Y",
    TRT01A  != "Screen Failure",
    PARCAT1 == "CHEMISTRY",
    PARAMCD %in% param_subset
  )

adsl_saf <- pharmaverseadam::adsl |>
  blank_to_na() |>
  filter(SAFFL == "Y", TRT01A != "Screen Failure")

arm_levels <- c("Placebo", "Xanomeline Low Dose", "Xanomeline High Dose")
arm_n      <- adsl_saf |> count(TRT01A) |> pull(n, name = TRT01A)
arm_n      <- arm_n[arm_levels]

Data Preparation

  • dplyr
  • cards
# ── Worst post-baseline toxicity grade per subject per PARAMCD ──
# ATOXGR uses signed values: positive = high direction, negative = low direction
# Worst = maximum absolute toxicity grade
worst_tox <- adlb_saf |>
  filter(is.na(ABLFL) | ABLFL != "Y", !is.na(ATOXGR)) |>
  mutate(abs_tox = abs(as.integer(ATOXGR))) |>
  group_by(USUBJID, PARAMCD, PARAM, TRT01A) |>
  slice_max(abs_tox, n = 1, with_ties = FALSE) |>
  ungroup() |>
  mutate(worst_grade = abs_tox)

# Subjects with no post-baseline data get Grade 0 (no abnormality)
all_subj_param <- adlb_saf |>
  filter(ABLFL == "Y") |>
  distinct(USUBJID, PARAMCD, PARAM, TRT01A)

# Fill Grade 0 for subjects with baseline but no post-baseline tox record
worst_tox_full <- all_subj_param |>
  left_join(
    worst_tox |> select(USUBJID, PARAMCD, worst_grade),
    by = c("USUBJID", "PARAMCD")
  ) |>
  mutate(
    worst_grade = replace_na(worst_grade, 0L),
    worst_grade = factor(worst_grade, levels = 0:3)
  )

# ── Grade labels (display grades 0–3) ──
grade_levels <- c("0", "1", "2", "3", "Any Grade >= 1")

n_pct <- function(n, denom) {
  ifelse(denom == 0, "0 (0.0)", sprintf("%d (%.1f)", n, n / denom * 100))
}

# ── Count subjects per worst grade per arm per param ──
grade_counts <- worst_tox_full |>
  filter(TRT01A %in% arm_levels) |>
  count(PARAMCD, PARAM, TRT01A, worst_grade, .drop = FALSE) |>
  filter(!is.na(PARAMCD), !is.na(TRT01A)) |>
  mutate(grade_label = paste0("Grade ", worst_grade))

# Denominators: all subjects with a baseline record for each param×arm
denom_df <- worst_tox_full |>
  filter(TRT01A %in% arm_levels) |>
  count(PARAMCD, TRT01A, name = "denom")

grade_pct <- grade_counts |>
  left_join(denom_df, by = c("PARAMCD", "TRT01A")) |>
  mutate(value = n_pct(n, denom)) |>
  select(PARAMCD, PARAM, grade_label, TRT01A, value) |>
  pivot_wider(names_from = TRT01A, values_from = value)

# ── Any Grade >= 1 derived row ──
any_grade <- worst_tox_full |>
  filter(TRT01A %in% arm_levels, worst_grade >= 1) |>
  count(PARAMCD, PARAM, TRT01A) |>
  left_join(denom_df, by = c("PARAMCD", "TRT01A")) |>
  mutate(
    value       = n_pct(n, denom),
    grade_label = "Any Grade >= 1"
  ) |>
  select(PARAMCD, PARAM, grade_label, TRT01A, value) |>
  pivot_wider(names_from = TRT01A, values_from = value)

# ── Combine grades 0–3 + Any Grade >= 1, sorted by PARAM then grade ──
tox_wide <- bind_rows(grade_pct, any_grade) |>
  mutate(
    PARAMCD = factor(PARAMCD, levels = param_subset),
    grade_order = case_when(
      grade_label == "Grade 0"       ~ 0,
      grade_label == "Grade 1"       ~ 1,
      grade_label == "Grade 2"       ~ 2,
      grade_label == "Grade 3"       ~ 3,
      grade_label == "Any Grade >= 1" ~ 4
    )
  ) |>
  arrange(PARAMCD, grade_order) |>
  mutate(
    PARAMCD     = as.character(PARAMCD),
    is_summary  = if_else(grade_label == "Any Grade >= 1", "summary", "grade")
  ) |>
  mutate(across(all_of(arm_levels), ~ replace_na(.x, "0 (0.0)"))) |>
  select(PARAMCD, PARAM, grade_label, is_summary, all_of(arm_levels))

The worst toxicity grade analysis requires a per-subject maximum derivation before tabulation. This pre-processing step is most naturally expressed in dplyr. The cards path below reuses the derived worst_tox_full dataset from the dplyr tab:

# Reuse worst_tox_full derived above (worst post-baseline grade per subject)
# Tabulate using ard_categorical on the worst_grade variable

tox_ard_data <- worst_tox_full |>
  filter(TRT01A %in% arm_levels) |>
  mutate(
    worst_grade_cat = factor(
      paste0("Grade ", worst_grade),
      levels = c("Grade 0", "Grade 1", "Grade 2", "Grade 3")
    )
  )

# Grade 0-3 counts via dplyr (same derivation, cards-style naming)
tox_cards_grades <- tox_ard_data |>
  count(PARAMCD, PARAM, TRT01A, worst_grade_cat) |>
  left_join(
    tox_ard_data |> count(PARAMCD, TRT01A, name = "denom"),
    by = c("PARAMCD", "TRT01A")
  ) |>
  mutate(
    value      = ifelse(denom == 0, "0 (0.0)", sprintf("%d (%.1f)", n, n / denom * 100)),
    grade_label = as.character(worst_grade_cat)
  ) |>
  select(PARAMCD, PARAM, grade_label, TRT01A, value) |>
  pivot_wider(names_from = TRT01A, values_from = value)

tox_cards_any <- tox_ard_data |>
  filter(worst_grade >= 1) |>
  count(PARAMCD, PARAM, TRT01A) |>
  left_join(
    tox_ard_data |> count(PARAMCD, TRT01A, name = "denom"),
    by = c("PARAMCD", "TRT01A")
  ) |>
  mutate(
    value      = ifelse(denom == 0, "0 (0.0)", sprintf("%d (%.1f)", n, n / denom * 100)),
    grade_label = "Any Grade >= 1"
  ) |>
  select(PARAMCD, PARAM, grade_label, TRT01A, value) |>
  pivot_wider(names_from = TRT01A, values_from = value)

tox_cards <- bind_rows(tox_cards_grades, tox_cards_any) |>
  mutate(
    PARAMCD = factor(PARAMCD, levels = param_subset),
    grade_order = case_when(
      grade_label == "Grade 0"        ~ 0,
      grade_label == "Grade 1"        ~ 1,
      grade_label == "Grade 2"        ~ 2,
      grade_label == "Grade 3"        ~ 3,
      grade_label == "Any Grade >= 1" ~ 4
    )
  ) |>
  arrange(PARAMCD, grade_order) |>
  mutate(
    PARAMCD    = as.character(PARAMCD),
    is_summary = if_else(grade_label == "Any Grade >= 1", "summary", "grade")
  ) |>
  mutate(across(all_of(arm_levels), ~ replace_na(.x, "0 (0.0)"))) |>
  select(PARAMCD, PARAM, grade_label, is_summary, all_of(arm_levels))

arframe Pipeline

The rendered table below uses the dplyr data prep (tox_wide). The cards tab produces an equivalent tox_cards — swap it in to use the cards path instead.

Each row is a toxicity grade category. Columns are treatment arms. The “Any Grade >= 1” summary row is bolded. One page per parameter:

tox_wide |>
  fr_table() |>
  fr_titles(
    "Table 14.3.7",
    "Laboratory Worst Post-Baseline Toxicity Grade by Parameter",
    "Safety Population"
  ) |>
  fr_cols(
    PARAMCD     = fr_col(visible = FALSE),
    PARAM       = fr_col(visible = FALSE),
    grade_label = fr_col("Toxicity Grade", width = 1.8),
    is_summary  = fr_col(visible = FALSE),
    !!!setNames(
      lapply(arm_levels, function(a) fr_col(a, align = "decimal")),
      arm_levels
    ),
    .n = arm_n
  ) |>
  fr_header(bold = TRUE, align = "center") |>
  fr_rows(page_by = "PARAM") |>
  fr_styles(
    fr_row_style(
      rows = fr_rows_matches("is_summary", value = "summary"),
      bold = TRUE
    )
  ) |>
  fr_footnotes(
    "Worst post-baseline toxicity grade is the maximum absolute toxicity grade (|ATOXGR|) recorded after baseline.",
    "Grade 0 = no abnormality or within normal limits; Grades 1-3 = increasing severity.",
    "Any Grade >= 1: subjects with at least one post-baseline toxicity grade of 1 or higher.",
    "Denominator for percentages is the number of subjects with a baseline value for each parameter.",
    "Safety Population: all randomised subjects who received at least one dose."
  )

Rendered Table

Table 14.3.7
Laboratory Worst Post-Baseline Toxicity Grade by Parameter
Safety Population
Alanine Aminotransferase (U/L)
Toxicity GradePlacebo
(N=86)
Xanomeline Low Dose
(N=96)
Xanomeline High Dose
(N=72)
Grade 077 (89.5)84 (87.5)61 (84.7)
Grade 1 7 ( 8.1)12 (12.5)10 (13.9)
Grade 2 2 ( 2.3) 0 1 ( 1.4)
Grade 3 0 0 0
Worst post-baseline toxicity grade is the maximum absolute toxicity grade (|ATOXGR|) recorded after baseline.
Grade 0 = no abnormality or within normal limits; Grades 1-3 = increasing severity.
Any Grade >= 1: subjects with at least one post-baseline toxicity grade of 1 or higher.
Denominator for percentages is the number of subjects with a baseline value for each parameter.
Safety Population: all randomised subjects who received at least one dose.
/opt/quarto/share/rmd/rmd.R 01APR2026 09:54:11
Table 14.3.7
Laboratory Worst Post-Baseline Toxicity Grade by Parameter
Safety Population
Aspartate Aminotransferase (U/L)
Toxicity GradePlacebo
(N=86)
Xanomeline Low Dose
(N=96)
Xanomeline High Dose
(N=72)
Grade 074 (86.0)82 (85.4)65 (90.3)
Grade 110 (11.6)13 (13.5) 6 ( 8.3)
Grade 2 2 ( 2.3) 1 ( 1.0) 1 ( 1.4)
Grade 3 0 0 0
Worst post-baseline toxicity grade is the maximum absolute toxicity grade (|ATOXGR|) recorded after baseline.
Grade 0 = no abnormality or within normal limits; Grades 1-3 = increasing severity.
Any Grade >= 1: subjects with at least one post-baseline toxicity grade of 1 or higher.
Denominator for percentages is the number of subjects with a baseline value for each parameter.
Safety Population: all randomised subjects who received at least one dose.
/opt/quarto/share/rmd/rmd.R 01APR2026 09:54:11
Table 14.3.7
Laboratory Worst Post-Baseline Toxicity Grade by Parameter
Safety Population
Bilirubin (umol/L)
Toxicity GradePlacebo
(N=86)
Xanomeline Low Dose
(N=96)
Xanomeline High Dose
(N=72)
Grade 080 (93.0)94 (97.9)66 (91.7)
Grade 1 5 ( 5.8) 1 ( 1.0) 3 ( 4.2)
Grade 2 0 1 ( 1.0) 3 ( 4.2)
Grade 3 1 ( 1.2) 0 0
Worst post-baseline toxicity grade is the maximum absolute toxicity grade (|ATOXGR|) recorded after baseline.
Grade 0 = no abnormality or within normal limits; Grades 1-3 = increasing severity.
Any Grade >= 1: subjects with at least one post-baseline toxicity grade of 1 or higher.
Denominator for percentages is the number of subjects with a baseline value for each parameter.
Safety Population: all randomised subjects who received at least one dose.
/opt/quarto/share/rmd/rmd.R 01APR2026 09:54:11
Table 14.3.7
Laboratory Worst Post-Baseline Toxicity Grade by Parameter
Safety Population
Creatinine (umol/L)
Toxicity GradePlacebo
(N=86)
Xanomeline Low Dose
(N=96)
Xanomeline High Dose
(N=72)
Grade 025 (29.1)30 (31.2)14 (19.4)
Grade 161 (70.9)66 (68.8)58 (80.6)
Grade 2 0 0 0
Grade 3 0 0 0
Worst post-baseline toxicity grade is the maximum absolute toxicity grade (|ATOXGR|) recorded after baseline.
Grade 0 = no abnormality or within normal limits; Grades 1-3 = increasing severity.
Any Grade >= 1: subjects with at least one post-baseline toxicity grade of 1 or higher.
Denominator for percentages is the number of subjects with a baseline value for each parameter.
Safety Population: all randomised subjects who received at least one dose.
/opt/quarto/share/rmd/rmd.R 01APR2026 09:54:11
Table 14.3.7
Laboratory Worst Post-Baseline Toxicity Grade by Parameter
Safety Population
Glucose (mmol/L)
Toxicity GradePlacebo
(N=86)
Xanomeline Low Dose
(N=96)
Xanomeline High Dose
(N=72)
Grade 083 (96.5)93 (96.9)66 (91.7)
Grade 1 0 0 0
Grade 2 1 ( 1.2) 2 ( 2.1) 0
Grade 3 2 ( 2.3) 1 ( 1.0) 6 ( 8.3)
Worst post-baseline toxicity grade is the maximum absolute toxicity grade (|ATOXGR|) recorded after baseline.
Grade 0 = no abnormality or within normal limits; Grades 1-3 = increasing severity.
Any Grade >= 1: subjects with at least one post-baseline toxicity grade of 1 or higher.
Denominator for percentages is the number of subjects with a baseline value for each parameter.
Safety Population: all randomised subjects who received at least one dose.
/opt/quarto/share/rmd/rmd.R 01APR2026 09:54:11
Table 14.3.7
Laboratory Worst Post-Baseline Toxicity Grade by Parameter
Safety Population
Sodium (mmol/L)
Toxicity GradePlacebo
(N=86)
Xanomeline Low Dose
(N=96)
Xanomeline High Dose
(N=72)
Grade 069 (80.2)83 (86.5)53 (73.6)
Grade 116 (18.6)13 (13.5)17 (23.6)
Grade 2 0 0 2 ( 2.8)
Grade 3 1 ( 1.2) 0 0
Worst post-baseline toxicity grade is the maximum absolute toxicity grade (|ATOXGR|) recorded after baseline.
Grade 0 = no abnormality or within normal limits; Grades 1-3 = increasing severity.
Any Grade >= 1: subjects with at least one post-baseline toxicity grade of 1 or higher.
Denominator for percentages is the number of subjects with a baseline value for each parameter.
Safety Population: all randomised subjects who received at least one dose.
/opt/quarto/share/rmd/rmd.R 01APR2026 09:54:11
Source Code
---
title: "Laboratory Worst Toxicity Grade"
subtitle: "Worst Post-Baseline Toxicity Grade by Parameter"
execute:
  echo: true
  eval: true
---


```{r}
#| label: prereqs
#| include: false
library(arframe)
fr_theme(hlines = "header", font_family = "Courier New")
blank_to_na <- function(df) {
  df[] <- lapply(df, function(x) {
    if (is.character(x)) x[x == ""] <- NA_character_
    x
  })
  df
}
```

## Setup

See [Prerequisites](../install.qmd) for installation instructions.

```{r}
#| label: setup
library(arframe)
library(pharmaverseadam)
library(dplyr, warn.conflicts = FALSE)
library(tidyr)

# Chemistry panel — 6 key parameters
param_subset <- c("ALT", "AST", "BILI", "CREAT", "GLUC", "SODIUM")

adlb_saf <- pharmaverseadam::adlb |>
  blank_to_na() |>
  filter(
    SAFFL   == "Y",
    TRT01A  != "Screen Failure",
    PARCAT1 == "CHEMISTRY",
    PARAMCD %in% param_subset
  )

adsl_saf <- pharmaverseadam::adsl |>
  blank_to_na() |>
  filter(SAFFL == "Y", TRT01A != "Screen Failure")

arm_levels <- c("Placebo", "Xanomeline Low Dose", "Xanomeline High Dose")
arm_n      <- adsl_saf |> count(TRT01A) |> pull(n, name = TRT01A)
arm_n      <- arm_n[arm_levels]
```


## Data Preparation

::: {.panel-tabset}

### dplyr

```{r}
#| label: dplyr-code

# ── Worst post-baseline toxicity grade per subject per PARAMCD ──
# ATOXGR uses signed values: positive = high direction, negative = low direction
# Worst = maximum absolute toxicity grade
worst_tox <- adlb_saf |>
  filter(is.na(ABLFL) | ABLFL != "Y", !is.na(ATOXGR)) |>
  mutate(abs_tox = abs(as.integer(ATOXGR))) |>
  group_by(USUBJID, PARAMCD, PARAM, TRT01A) |>
  slice_max(abs_tox, n = 1, with_ties = FALSE) |>
  ungroup() |>
  mutate(worst_grade = abs_tox)

# Subjects with no post-baseline data get Grade 0 (no abnormality)
all_subj_param <- adlb_saf |>
  filter(ABLFL == "Y") |>
  distinct(USUBJID, PARAMCD, PARAM, TRT01A)

# Fill Grade 0 for subjects with baseline but no post-baseline tox record
worst_tox_full <- all_subj_param |>
  left_join(
    worst_tox |> select(USUBJID, PARAMCD, worst_grade),
    by = c("USUBJID", "PARAMCD")
  ) |>
  mutate(
    worst_grade = replace_na(worst_grade, 0L),
    worst_grade = factor(worst_grade, levels = 0:3)
  )

# ── Grade labels (display grades 0–3) ──
grade_levels <- c("0", "1", "2", "3", "Any Grade >= 1")

n_pct <- function(n, denom) {
  ifelse(denom == 0, "0 (0.0)", sprintf("%d (%.1f)", n, n / denom * 100))
}

# ── Count subjects per worst grade per arm per param ──
grade_counts <- worst_tox_full |>
  filter(TRT01A %in% arm_levels) |>
  count(PARAMCD, PARAM, TRT01A, worst_grade, .drop = FALSE) |>
  filter(!is.na(PARAMCD), !is.na(TRT01A)) |>
  mutate(grade_label = paste0("Grade ", worst_grade))

# Denominators: all subjects with a baseline record for each param×arm
denom_df <- worst_tox_full |>
  filter(TRT01A %in% arm_levels) |>
  count(PARAMCD, TRT01A, name = "denom")

grade_pct <- grade_counts |>
  left_join(denom_df, by = c("PARAMCD", "TRT01A")) |>
  mutate(value = n_pct(n, denom)) |>
  select(PARAMCD, PARAM, grade_label, TRT01A, value) |>
  pivot_wider(names_from = TRT01A, values_from = value)

# ── Any Grade >= 1 derived row ──
any_grade <- worst_tox_full |>
  filter(TRT01A %in% arm_levels, worst_grade >= 1) |>
  count(PARAMCD, PARAM, TRT01A) |>
  left_join(denom_df, by = c("PARAMCD", "TRT01A")) |>
  mutate(
    value       = n_pct(n, denom),
    grade_label = "Any Grade >= 1"
  ) |>
  select(PARAMCD, PARAM, grade_label, TRT01A, value) |>
  pivot_wider(names_from = TRT01A, values_from = value)

# ── Combine grades 0–3 + Any Grade >= 1, sorted by PARAM then grade ──
tox_wide <- bind_rows(grade_pct, any_grade) |>
  mutate(
    PARAMCD = factor(PARAMCD, levels = param_subset),
    grade_order = case_when(
      grade_label == "Grade 0"       ~ 0,
      grade_label == "Grade 1"       ~ 1,
      grade_label == "Grade 2"       ~ 2,
      grade_label == "Grade 3"       ~ 3,
      grade_label == "Any Grade >= 1" ~ 4
    )
  ) |>
  arrange(PARAMCD, grade_order) |>
  mutate(
    PARAMCD     = as.character(PARAMCD),
    is_summary  = if_else(grade_label == "Any Grade >= 1", "summary", "grade")
  ) |>
  mutate(across(all_of(arm_levels), ~ replace_na(.x, "0 (0.0)"))) |>
  select(PARAMCD, PARAM, grade_label, is_summary, all_of(arm_levels))
```

### cards

The worst toxicity grade analysis requires a per-subject maximum derivation before tabulation. This pre-processing step is most naturally expressed in dplyr. The cards path below reuses the derived `worst_tox_full` dataset from the dplyr tab:

```{r}
#| label: cards-code

# Reuse worst_tox_full derived above (worst post-baseline grade per subject)
# Tabulate using ard_categorical on the worst_grade variable

tox_ard_data <- worst_tox_full |>
  filter(TRT01A %in% arm_levels) |>
  mutate(
    worst_grade_cat = factor(
      paste0("Grade ", worst_grade),
      levels = c("Grade 0", "Grade 1", "Grade 2", "Grade 3")
    )
  )

# Grade 0-3 counts via dplyr (same derivation, cards-style naming)
tox_cards_grades <- tox_ard_data |>
  count(PARAMCD, PARAM, TRT01A, worst_grade_cat) |>
  left_join(
    tox_ard_data |> count(PARAMCD, TRT01A, name = "denom"),
    by = c("PARAMCD", "TRT01A")
  ) |>
  mutate(
    value      = ifelse(denom == 0, "0 (0.0)", sprintf("%d (%.1f)", n, n / denom * 100)),
    grade_label = as.character(worst_grade_cat)
  ) |>
  select(PARAMCD, PARAM, grade_label, TRT01A, value) |>
  pivot_wider(names_from = TRT01A, values_from = value)

tox_cards_any <- tox_ard_data |>
  filter(worst_grade >= 1) |>
  count(PARAMCD, PARAM, TRT01A) |>
  left_join(
    tox_ard_data |> count(PARAMCD, TRT01A, name = "denom"),
    by = c("PARAMCD", "TRT01A")
  ) |>
  mutate(
    value      = ifelse(denom == 0, "0 (0.0)", sprintf("%d (%.1f)", n, n / denom * 100)),
    grade_label = "Any Grade >= 1"
  ) |>
  select(PARAMCD, PARAM, grade_label, TRT01A, value) |>
  pivot_wider(names_from = TRT01A, values_from = value)

tox_cards <- bind_rows(tox_cards_grades, tox_cards_any) |>
  mutate(
    PARAMCD = factor(PARAMCD, levels = param_subset),
    grade_order = case_when(
      grade_label == "Grade 0"        ~ 0,
      grade_label == "Grade 1"        ~ 1,
      grade_label == "Grade 2"        ~ 2,
      grade_label == "Grade 3"        ~ 3,
      grade_label == "Any Grade >= 1" ~ 4
    )
  ) |>
  arrange(PARAMCD, grade_order) |>
  mutate(
    PARAMCD    = as.character(PARAMCD),
    is_summary = if_else(grade_label == "Any Grade >= 1", "summary", "grade")
  ) |>
  mutate(across(all_of(arm_levels), ~ replace_na(.x, "0 (0.0)"))) |>
  select(PARAMCD, PARAM, grade_label, is_summary, all_of(arm_levels))
```

:::


## arframe Pipeline

The rendered table below uses the **dplyr** data prep (`tox_wide`). The cards tab produces an equivalent `tox_cards` — swap it in to use the cards path instead.

Each row is a toxicity grade category. Columns are treatment arms. The "Any Grade >= 1" summary row is bolded. One page per parameter:

```{r}
#| label: pipeline
#| eval: false
tox_wide |>
  fr_table() |>
  fr_titles(
    "Table 14.3.7",
    "Laboratory Worst Post-Baseline Toxicity Grade by Parameter",
    "Safety Population"
  ) |>
  fr_cols(
    PARAMCD     = fr_col(visible = FALSE),
    PARAM       = fr_col(visible = FALSE),
    grade_label = fr_col("Toxicity Grade", width = 1.8),
    is_summary  = fr_col(visible = FALSE),
    !!!setNames(
      lapply(arm_levels, function(a) fr_col(a, align = "decimal")),
      arm_levels
    ),
    .n = arm_n
  ) |>
  fr_header(bold = TRUE, align = "center") |>
  fr_rows(page_by = "PARAM") |>
  fr_styles(
    fr_row_style(
      rows = fr_rows_matches("is_summary", value = "summary"),
      bold = TRUE
    )
  ) |>
  fr_footnotes(
    "Worst post-baseline toxicity grade is the maximum absolute toxicity grade (|ATOXGR|) recorded after baseline.",
    "Grade 0 = no abnormality or within normal limits; Grades 1-3 = increasing severity.",
    "Any Grade >= 1: subjects with at least one post-baseline toxicity grade of 1 or higher.",
    "Denominator for percentages is the number of subjects with a baseline value for each parameter.",
    "Safety Population: all randomised subjects who received at least one dose."
  )
```


## Rendered Table

```{r}
#| label: rendered-table
#| echo: false
#| ref.label: pipeline
```

Open-source TFL reference collection

 

CDISC Pilot Study (CDISCPILOT01) • pharmaverseadam datasets