library(arframe)
library(pharmaverseadam)
library(dplyr, warn.conflicts = FALSE)
library(tidyr)
library(cards)
adsl_saf <- pharmaverseadam::adsl |>
blank_to_na() |>
filter(SAFFL == "Y", TRT01A != "Screen Failure")
# adcm: on-treatment concomitant medications (ANL01FL = "Y")
adcm_ont <- pharmaverseadam::adcm |>
blank_to_na() |>
filter(ONTRTFL == "Y", TRT01A != "Screen Failure") |>
mutate(
CMCLAS = if_else(is.na(CMCLAS), "UNCODED", toupper(CMCLAS)),
CMDECOD = if_else(is.na(CMDECOD), CMTRT, CMDECOD),
CMDECOD = toupper(CMDECOD)
)
arm_levels <- c("Placebo", "Xanomeline Low Dose", "Xanomeline High Dose")
arm_n <- adsl_saf |>
filter(TRT01A %in% arm_levels) |>
count(TRT01A) |>
pull(n, name = TRT01A)
arm_n <- arm_n[arm_levels]
N_total <- nrow(adsl_saf)Concomitant Medications
Summary of Concomitant Medications by ATC Class and Medication Name
Setup
See Prerequisites for installation instructions.
Data Preparation
n_pct <- function(n, denom) sprintf("%d (%.1f)", n, n / denom * 100)
# ── ATC class level ──
atc_arm <- adcm_ont |>
distinct(USUBJID, TRT01A, CMCLAS) |>
count(TRT01A, CMCLAS) |>
filter(TRT01A %in% arm_levels) |>
mutate(value = mapply(n_pct, n, arm_n[TRT01A])) |>
select(TRT01A, CMCLAS, value) |>
pivot_wider(names_from = TRT01A, values_from = value)
atc_total <- adcm_ont |>
distinct(USUBJID, CMCLAS) |>
count(CMCLAS) |>
mutate(Total = n_pct(n, N_total)) |>
select(CMCLAS, Total)
atc_wide <- left_join(atc_arm, atc_total, by = "CMCLAS") |>
mutate(
atc_class = CMCLAS,
med_name = CMCLAS,
row_type = "atc",
.before = 1
) |>
select(-CMCLAS)
# ── Medication name level ──
med_arm <- adcm_ont |>
distinct(USUBJID, TRT01A, CMCLAS, CMDECOD) |>
count(TRT01A, CMCLAS, CMDECOD) |>
filter(TRT01A %in% arm_levels) |>
mutate(value = mapply(n_pct, n, arm_n[TRT01A])) |>
select(TRT01A, CMCLAS, CMDECOD, value) |>
pivot_wider(names_from = TRT01A, values_from = value)
med_total <- adcm_ont |>
distinct(USUBJID, CMCLAS, CMDECOD) |>
count(CMCLAS, CMDECOD) |>
mutate(Total = n_pct(n, N_total)) |>
select(CMCLAS, CMDECOD, Total)
med_wide <- left_join(med_arm, med_total, by = c("CMCLAS", "CMDECOD")) |>
mutate(
atc_class = CMCLAS,
med_name = CMDECOD,
row_type = "med",
.before = 1
) |>
select(-CMCLAS, -CMDECOD)
# ── Sort: ATC class descending by subject count, meds descending within class ──
atc_order <- adcm_ont |>
distinct(USUBJID, CMCLAS) |>
count(CMCLAS, name = "n") |>
arrange(desc(n)) |>
pull(CMCLAS)
med_sorted <- adcm_ont |>
distinct(USUBJID, CMCLAS, CMDECOD) |>
count(CMCLAS, CMDECOD, name = "n") |>
arrange(match(CMCLAS, atc_order), desc(n)) |>
select(atc_class = CMCLAS, med_name = CMDECOD)
med_wide <- med_sorted |> left_join(med_wide, by = c("atc_class", "med_name"))
# ── Interleave ATC header rows + medication rows ──
cm_wide <- bind_rows(
lapply(atc_order, function(a) {
bind_rows(
filter(atc_wide, atc_class == a),
filter(med_wide, atc_class == a)
)
})
) |>
mutate(across(all_of(c(arm_levels, "Total")), ~ replace_na(.x, "0 (0.0)")))
n_vec <- c(arm_n, Total = N_total)cm_ard <- ard_stack_hierarchical(
data = adcm_ont |> filter(TRT01A %in% arm_levels),
variables = c(CMCLAS, CMDECOD),
by = TRT01A,
denominator = adsl_saf,
id = USUBJID,
overall = TRUE
) |>
sort_ard_hierarchical(sort = "descending")
# Note: cards output uses soc/pt column names vs dplyr's atc_class/med_name
cm_cards <- fr_wide_ard(
cm_ard,
statistic = "{n} ({p}%)",
decimals = c(p = 1),
label = c(
CMCLAS = "ATC Class",
CMDECOD = "Medication Name"
)
)arframe Pipeline
The rendered table below uses the dplyr data prep (cm_wide). The cards tab produces an equivalent cm_cards — swap it in to use the cards path instead.
cm_wide |>
fr_table() |>
fr_titles(
"Table 14.5.1",
"Summary of Concomitant Medications by ATC Class and Medication Name",
"Safety Population \u2014 On-Treatment Period"
) |>
fr_cols(
atc_class = fr_col(visible = FALSE),
med_name = fr_col("ATC Class\n Medication Name", width = 3.5),
row_type = fr_col(visible = FALSE),
!!!setNames(
lapply(arm_levels, function(a) fr_col(a, align = "decimal")),
arm_levels
),
Total = fr_col("Total", align = "decimal"),
.n = n_vec
) |>
fr_header(bold = TRUE, align = "center") |>
fr_rows(
group_by = "atc_class",
indent_by = "med_name"
) |>
fr_styles(
fr_row_style(rows = fr_rows_matches("row_type", value = "atc"), bold = TRUE)
) |>
fr_footnotes(
"Concomitant medications are those with ONTRTFL = 'Y'.",
"Subjects are counted once per ATC class and once per medication name.",
"Percentages based on N per treatment arm (Safety Population).",
"ATC class rows sorted by descending subject frequency; medications sorted within class by descending frequency.",
"Medication names from CMDECOD (WHO Drug coded); ATC class from CMCLAS."
)Rendered Table
Table 14.5.1
Summary of Concomitant Medications by ATC Class and Medication Name
Safety Population — On-Treatment Period
| ATC Class Medication Name | Placebo (N=86) | Xanomeline High Dose (N=72) | Xanomeline Low Dose (N=96) | Total (N=254) |
|---|---|---|---|---|
| UNCODED | 49 (57.0) | 46 (63.9) | 54 (56.2) | 149 (58.7) |
| UNCODED | 49 (57.0) | 46 (63.9) | 54 (56.2) | 149 (58.7) |
| SYSTEMIC HORMONAL PREPARATIONS, EXCL. | 2 ( 2.3) | 8 (11.1) | 13 (13.5) | 23 ( 9.1) |
| HYDROCORTISONE | 2 ( 2.3) | 8 (11.1) | 13 (13.5) | 23 ( 9.1) |
| CARDIOVASCULAR SYSTEM | 3 ( 3.5) | 3 ( 4.2) | 3 ( 3.1) | 9 ( 3.5) |
| AMLODIPINE | 3 ( 3.5) | 0 | 0 | 3 ( 1.2) |
| DOXAZOSIN MESILATE | 0 | 1 ( 1.4) | 1 ( 1.0) | 2 ( 0.8) |
| DIGOXIN | 0 | 0 | 1 ( 1.0) | 1 ( 0.4) |
| DILTIAZEM HYDROCHLORIDE | 0 | 1 ( 1.4) | 0 | 1 ( 0.4) |
| FUROSEMIDE | 0 | 1 ( 1.4) | 0 | 1 ( 0.4) |
| LOSARTAN POTASSIUM | 0 | 0 | 1 ( 1.0) | 1 ( 0.4) |
| ALIMENTARY TRACT AND METABOLISM | 2 ( 2.3) | 2 ( 2.8) | 2 ( 2.1) | 6 ( 2.4) |
| ALGELDRATE | 1 ( 1.2) | 2 ( 2.8) | 0 | 3 ( 1.2) |
| LOPERAMIDE HYDROCHLORIDE | 1 ( 1.2) | 0 | 1 ( 1.0) | 2 ( 0.8) |
| CIMETIDINE | 0 | 0 | 1 ( 1.0) | 1 ( 0.4) |
| NIZATIDINE | 0 | 0 | 1 ( 1.0) | 1 ( 0.4) |
| NERVOUS SYSTEM | 2 ( 2.3) | 1 ( 1.4) | 1 ( 1.0) | 4 ( 1.6) |
| ACETYLSALICYLIC ACID | 2 ( 2.3) | 1 ( 1.4) | 1 ( 1.0) | 4 ( 1.6) |
| RESPIRATORY SYSTEM | 1 ( 1.2) | 1 ( 1.4) | 1 ( 1.0) | 3 ( 1.2) |
| BUDESONIDE | 0 | 1 ( 1.4) | 0 | 1 ( 0.4) |
| GUAIFENESIN | 1 ( 1.2) | 0 | 0 | 1 ( 0.4) |
| SALBUTAMOL SULFATE | 0 | 0 | 1 ( 1.0) | 1 ( 0.4) |
| ANTINEOPLASTIC AND IMMUNOMODULATING AGENTS | 0 | 0 | 1 ( 1.0) | 1 ( 0.4) |
| LEUPRORELIN ACETATE | 0 | 0 | 1 ( 1.0) | 1 ( 0.4) |
| GENITO URINARY SYSTEM AND SEX HORMONES | 0 | 1 ( 1.4) | 0 | 1 ( 0.4) |
| ESTROGENS CONJUGATED | 0 | 1 ( 1.4) | 0 | 1 ( 0.4) |
Concomitant medications are those with ONTRTFL = 'Y'.
Subjects are counted once per ATC class and once per medication name.
Percentages based on N per treatment arm (Safety Population).
ATC class rows sorted by descending subject frequency; medications sorted within class by descending frequency.
Medication names from CMDECOD (WHO Drug coded); ATC class from CMCLAS.
/opt/quarto/share/rmd/rmd.R
01APR2026 09:52:38