library(arframe)
library(pharmaverseadam)
library(dplyr, warn.conflicts = FALSE)
library(tidyr)
# All enrolled subjects (no safety filter — enrollment includes screen failures)
adsl_all <- pharmaverseadam::adsl |>
blank_to_na() |>
filter(TRT01A != "Screen Failure")
arm_levels <- c("Placebo", "Xanomeline Low Dose", "Xanomeline High Dose")
arm_n <- adsl_all |>
filter(TRT01A %in% arm_levels) |>
count(TRT01A) |>
pull(n, name = TRT01A)
arm_n <- arm_n[arm_levels]
N_total <- sum(arm_n)
n_vec <- c(arm_n, Total = N_total)Enrollment by Country and Site
Summary of Enrollment by Country and Investigative Site
Setup
See Prerequisites for installation instructions.
Data Preparation
n_pct <- function(n, denom) sprintf("%d (%.1f)", n, n / denom * 100)
# Assign country (CDISCPILOT01 is all USA — use COUNTRY column)
# Group sites under their country for the hierarchical display
adsl_enroll <- adsl_all |>
filter(TRT01A %in% arm_levels) |>
mutate(
COUNTRY = if_else(is.na(COUNTRY) | COUNTRY == "", "USA", COUNTRY),
site_label = paste0("Site ", SITEID)
)
# ── Country-level counts ──
country_arm <- adsl_enroll |>
count(TRT01A, COUNTRY) |>
mutate(value = mapply(n_pct, n, arm_n[TRT01A])) |>
select(TRT01A, COUNTRY, value) |>
pivot_wider(names_from = TRT01A, values_from = value)
country_total <- adsl_enroll |>
count(COUNTRY) |>
mutate(Total = n_pct(n, N_total)) |>
select(COUNTRY, Total)
country_wide <- left_join(country_arm, country_total, by = "COUNTRY") |>
mutate(
country = COUNTRY,
site = COUNTRY,
row_type = "country",
.before = 1
) |>
select(-COUNTRY)
# ── Site-level counts ──
site_arm <- adsl_enroll |>
count(TRT01A, COUNTRY, site_label) |>
mutate(value = mapply(n_pct, n, arm_n[TRT01A])) |>
select(TRT01A, COUNTRY, site_label, value) |>
pivot_wider(names_from = TRT01A, values_from = value)
site_total <- adsl_enroll |>
count(COUNTRY, site_label) |>
mutate(Total = n_pct(n, N_total)) |>
select(COUNTRY, site_label, Total)
site_wide <- left_join(site_arm, site_total, by = c("COUNTRY", "site_label")) |>
mutate(
country = COUNTRY,
site = site_label,
row_type = "site",
.before = 1
) |>
select(-COUNTRY, -site_label)
# ── Sort: country alphabetical, sites by descending enrollment ──
site_order <- adsl_enroll |>
count(COUNTRY, site_label, name = "n") |>
arrange(COUNTRY, desc(n)) |>
select(country = COUNTRY, site = site_label)
site_wide <- site_order |>
left_join(site_wide, by = c("country", "site"))
# ── Interleave country + site rows ──
enroll_wide <- bind_rows(
lapply(sort(unique(adsl_enroll$COUNTRY)), function(cntry) {
bind_rows(
filter(country_wide, country == cntry),
filter(site_wide, country == cntry)
)
})
) |>
mutate(across(all_of(c(arm_levels, "Total")), ~ replace_na(.x, "0 (0.0)")))arframe Pipeline
The fr_table() pipeline:
enroll_wide |>
fr_table() |>
fr_titles(
"Table 14.1.2",
"Summary of Enrollment by Country and Investigative Site",
"Randomized Subjects"
) |>
fr_cols(
country = fr_col(visible = FALSE),
site = fr_col("Country\n Site", width = 2.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 = "country",
indent_by = "site"
) |>
fr_styles(
fr_row_style(rows = fr_rows_matches("row_type", value = "country"), bold = TRUE)
) |>
fr_footnotes(
"Percentages based on total number of randomized subjects.",
"Subjects who were screen failures are excluded.",
"Sites sorted by descending enrollment count within each country."
)Rendered Table
Table 14.1.2
Summary of Enrollment by Country and Investigative Site
Randomized Subjects
| Country Site | Placebo (N=86) | Xanomeline High Dose (N=72) | Xanomeline Low Dose (N=96) | Total (N=254) |
|---|---|---|---|---|
| USA | 86 (100.0) | 72 (100.0) | 96 (100.0) | 254 (100.0) |
| Site 701 | 14 ( 16.3) | 12 ( 16.7) | 15 ( 15.6) | 41 ( 16.1) |
| Site 710 | 11 ( 12.8) | 10 ( 13.9) | 10 ( 10.4) | 31 ( 12.2) |
| Site 704 | 9 ( 10.5) | 8 ( 11.1) | 8 ( 8.3) | 25 ( 9.8) |
| Site 708 | 9 ( 10.5) | 5 ( 6.9) | 11 ( 11.5) | 25 ( 9.8) |
| Site 716 | 8 ( 9.3) | 7 ( 9.7) | 9 ( 9.4) | 24 ( 9.4) |
| Site 709 | 7 ( 8.1) | 5 ( 6.9) | 9 ( 9.4) | 21 ( 8.3) |
| Site 703 | 6 ( 7.0) | 5 ( 6.9) | 7 ( 7.3) | 18 ( 7.1) |
| Site 705 | 5 ( 5.8) | 5 ( 6.9) | 6 ( 6.2) | 16 ( 6.3) |
| Site 718 | 4 ( 4.7) | 4 ( 5.6) | 5 ( 5.2) | 13 ( 5.1) |
| Site 713 | 3 ( 3.5) | 3 ( 4.2) | 3 ( 3.1) | 9 ( 3.5) |
| Site 715 | 3 ( 3.5) | 2 ( 2.8) | 3 ( 3.1) | 8 ( 3.1) |
| Site 717 | 2 ( 2.3) | 3 ( 4.2) | 2 ( 2.1) | 7 ( 2.8) |
| Site 714 | 2 ( 2.3) | 1 ( 1.4) | 3 ( 3.1) | 6 ( 2.4) |
| Site 711 | 1 ( 1.2) | 1 ( 1.4) | 2 ( 2.1) | 4 ( 1.6) |
| Site 706 | 1 ( 1.2) | 1 ( 1.4) | 1 ( 1.0) | 3 ( 1.2) |
| Site 707 | 1 ( 1.2) | 0 | 1 ( 1.0) | 2 ( 0.8) |
| Site 702 | 0 | 0 | 1 ( 1.0) | 1 ( 0.4) |
Percentages based on total number of randomized subjects.
Subjects who were screen failures are excluded.
Sites sorted by descending enrollment count within each country.
/opt/quarto/share/rmd/rmd.R
01APR2026 09:53:04