Worked example: one synthetic BE study
A synthetic bioequivalence study, from sample size to CSR text
Synthetic data only: simulated for this worked example; not real trial data and not a regulatory deliverable.
One synthetic early-phase bioequivalence study, taken from sample size to clinical study report text. Every number on this page and on the pages it links to comes from one script, study/run_all.R, run on synthetic data. The study builds on the sim_lifecycle/ sandbox (same priors and model, a 2x2 crossover, N = 40, seed 2026).
- Design40 enrolled; 38 evaluable for 90% power
- SAP and shells6 table shells
- ADaM24 of 24 metadata checks passed
- Analysis and TLFsCmax 101.07% (90% CI 98.89 to 103.30)
- QC40 of 40 checks (same-author)
- CSR text10 of 10 paragraphs number-checked
Stage 1
Design: sample size and randomization
What was executed
- PowerTOST 1.5-7, exact method: CV 25%, ratio 95%, limits 80.00% to 125.00%: 28 evaluable for 80% power, 38 for 90%; 5% allowance for non-evaluable participants gives 40 enrolled.
- Sensitivity grid of N and power over CV 15% to 35% and ratio 0.90 to 1.05; pa.ABE() limits of the assumptions.
- blockrand 1.5, seed 2026: permuted blocks of 4, sequences TR and RT 20 : 20; balance check passed (largest running imbalance 2).
- Protocol statistical-section excerpt with ICH M11 headings (estimand, analysis sets, model, missing data, sample size).
Artifacts
- protocol_statistics.htmlM11-headed excerpt, sensitivity tables, schedule check
- randomization_schedule.csv40 sequence assignments
- sensitivity_n.csvN over CV and ratio
- design_parameters.jsonmachine-readable design
Not covered: BOIN/CRM dose-escalation designs, adaptive or group-sequential designs.
Stage 2
Statistical analysis plan and mock shells
What was executed
- Estimand with the five ICH E9(R1) attributes; each intercurrent event mapped to a strategy, and why the other four strategies do not apply.
- Analysis sets with their ADaM flags; the ICH M13A model, the tests to report, the decision on unrounded limits.
- BLQ and missing-data rules per ICH M13A; the settings are written to analysis_parameters.json, which the NCA, exclusion and BE programs read.
- Mock shells for the six executed tables.
Artifacts
- sap_excerpt.htmlstatistical methods excerpt
- mock_shells.htmlsix table shells
- analysis_parameters.jsonsettings the code reads
Stage 3
SDTM-like inputs, ADaM, specification and XPT
What was executed
- Simulated DM, EX, PC, AE, VS and DS (1185 concentration records at actual times) with 6 documented scenarios: a withdrawal, emesis, a pre-dose carry-over value, a missed sample, a late sample and a BLQ result between quantifiable samples.
- ADSL, ADPC, ADPP and ADAE with admiral 1.5.0 following its ad_adpc.R and ad_adpp.R templates (NFRLT, AFRLT, NRRLT, ARRLT), adaptations listed.
- Specification in Pinnacle 21 layout (xlsx) and JSON, read by metacore::spec_to_metacore(); 24 of 24 metadata checks passed (metatools check_variables, check_ct_data, check_unique_keys, lengths, mandatory values, XPT read-back).
- SAS XPORT v5 with labels and lengths from xportr 0.6.0; Dataset-JSON v1.1 copies from datasetjson 0.4.1, which FDA has not accepted as of Oct 2026.
Artifacts
- ADaM pagedatasets, spec, checks, adaptations
- adam_spec.xlsxP21-layout specification
- adam_spec.jsonmachine-readable specification
- adpc.xptone of four XPT v5 files
Not covered: define.xml and the ADRG are not authored; no Pinnacle 21 or CDISC CORE run; the inputs are SDTM-like, not conformant SDTM.
Stage 4
NCA, bioequivalence analysis and TLFs
What was executed
- PKNCA 0.12.1 NCA on actual times with BLQ kept as zero in every position (ICH M13A): 79 profiles. For the profile with a BLQ value between quantifiable samples, AUC(0-t) is 1312.1 h*ng/mL against 1433.0 with the PKNCA default, which drops that value.
- SAP parameter rules through PKNCA exclude(): AUC(0-inf) flagged in 2 profiles (more than 20% extrapolated); the supportive AUC(0-inf) analysis uses 37 participants.
- ICH M13A GLM: Cmax ratio 101.07% (90% CI 98.89 to 103.30), AUC(0-t) ratio 103.99% (90% CI 98.52 to 109.75) in 38 participants; bioequivalence shown.
- Cmax at the first post-dose sample in 2 profiles (ICH M13A 2.1.8.1); a supplementary analysis without those participants.
- Six tables with rtables 0.6.17 and tern 0.9.13, each exported to RTF with formatters::export_as_rtf() and to HTML; three figures; a Quarto results report.
Artifacts
- Analysis pagetables, figures, result files
- report.htmlQuarto results report
- Table 14.4.3.1bioequivalence result (RTF alongside)
- be_results.csvunrounded estimates
Not covered: Phoenix WinNonlin and SAS were not used.
Stage 5
QC: re-derivation by a second method
What was executed
- 40 of 40 checks passed (5100 values compared with diffdf 1.1.2 at stated tolerances).
- NonCompart 0.8.4 vs PKNCA for every NCA parameter, including the profile with a BLQ value between quantifiable samples, and the SAP parameter-rule flags; the BE model refit as a closed-form period-difference t-test and with nlme::lme().
- ADPC times (including the experimental derive_var_nfrlt()), ADSL population flags and the adverse-event counts rebuilt in base R from the SDTM-like data.
- Controls: a planted 0.1% change is found exactly once, and NonCompart's unadjusted AUC(0-t) differs from PKNCA in exactly the BLQ profile, so the comparison is shown to fail when it should.
Artifacts
- qc_ledger.htmlexecuted ledger with pass/fail counts
- qc_ledger.csvone row per compared variable
- qc_nca_side_by_side.csvboth engines, every profile
Not covered: This is same-author re-derivation: the same person wrote both routes. No second programmer re-derived the results.
Stage 6
CSR section 11-style text, checked against the outputs
What was executed
- Section 11-style text (data sets analyzed, demographics, compliance, PK and BE results, missing data, conclusions), with every number read from an output file.
- The tlf_interpret number-grounding validator, unchanged: 10 of 10 paragraphs have every number found in the outputs.
- Controls: an altered number is rejected; a wrong statement built from real numbers passes, which is the known limitation; 'well tolerated' is flagged for review.
Artifacts
- csr_section11.htmltext, check results, controls
- csr_section11.txtplain text
- csr_check.jsonvalidator output
Not covered: The validator checks that numbers exist in the outputs, not that statements are correct. No language model runs in this pipeline; the text is filled into fixed sentence templates (written with an AI coding assistant) and still needs a statistician's review.
Rerun it
Rscript -e 'install.packages("renv"); renv::restore(project = "study", prompt = FALSE)' # packages pinned in study/renv.lock
Rscript study/run_all.R # from the repository root; regenerates every output above
run_all.R runs the stages in order and renders the report with Quarto. renv.lock pins R 4.6.0 and the package versions below. Two consecutive local runs produced byte-identical files. The CI workflow .github/workflows/study.yml (r-lib/actions v2) runs it on Ubuntu and fails if a committed output changes; the figures and the rendered report are excluded from that byte check because PNG rendering depends on system fonts. Its run history is public; it first passed on main on 25 October 2026.
Package versions (renv.lock)
| Package | Version |
|---|---|
| admiral | 1.5.0 |
| metacore | 0.3.0 |
| metatools | 0.3.0 |
| xportr | 0.6.0 |
| datasetjson | 0.4.1 |
| PKNCA | 0.12.1 |
| NonCompart | 0.8.4 |
| PowerTOST | 1.5-7 |
| blockrand | 1.5 |
| rtables | 0.6.17 |
| tern | 0.9.13 |
| formatters | 0.5.13 |
| r2rtf | 1.3.1 |
| diffdf | 1.1.2 |
| dplyr | 1.2.1 |
| ggplot2 | 4.0.3 |
| knitr | 1.51 |
| rmarkdown | 2.31 |
| openxlsx | 4.2.9 |
| renv | 1.3.1 |
Not covered by this example. define.xml and ADRG authoring; Pinnacle 21 or CDISC CORE conformance; conformant SDTM; independent double programming by a second person; a validated (21 CFR Part 11) system; a regulatory submission package; interim analyses and a DMC. The study, product and every participant are simulated.