| name | clinical-trial-design-patterns |
| description | Common clinical trial design patterns including multi-arm, multi-endpoint, adaptive, and stratified designs. Use when selecting or implementing trial designs. |
Clinical Trial Design Patterns
When to Use This Skill
- Selecting appropriate trial design for clinical objectives
- Implementing multi-arm or multi-endpoint trials
- Designing stratified trials
- Planning adaptive designs
- Understanding design trade-offs
Two-Arm Parallel Design
Standard Design
The most common design: randomize patients to treatment or control.
sim_pw_surv(
n = 400,
block = c(rep("control", 1), rep("experimental", 1)),
enroll_rate = data.frame(rate = 20, duration = 12),
fail_rate = fail_rate
)
DataModel() +
OutcomeDist(outcome.dist = "NormalDist") +
SampleSize(200) +
Sample(id = "Control", outcome.par = parameters(mean = 0, sd = 1)) +
Sample(id = "Treatment", outcome.par = parameters(mean = 0.5, sd = 1))
Unequal Randomization
When to Use:
- Increase exposure to experimental treatment
- Ethical considerations
- Resource optimization
sim_pw_surv(
n = 300,
block = c("control", rep("experimental", 2))
)
DataModel() +
Sample(id = "Control", sample.size = 100, ...) +
Sample(id = "Treatment", sample.size = 200, ...)
Trade-off: Unequal allocation reduces power for same total N.
Multi-Arm Designs
Dose-Finding (Multiple Doses vs Placebo)
DataModel() +
OutcomeDist(outcome.dist = "NormalDist") +
SampleSize(75) +
Sample(id = "Placebo", outcome.par = parameters(mean = 0, sd = 1)) +
Sample(id = "Low Dose", outcome.par = parameters(mean = 0.3, sd = 1)) +
Sample(id = "Mid Dose", outcome.par = parameters(mean = 0.5, sd = 1)) +
Sample(id = "High Dose", outcome.par = parameters(mean = 0.7, sd = 1))
AnalysisModel() +
Test(id = "Low vs Placebo", samples = samples("Placebo", "Low Dose"), method = "TTest") +
Test(id = "Mid vs Placebo", samples = samples("Placebo", "Mid Dose"), method = "TTest") +
Test(id = "High vs Placebo", samples = samples("Placebo", "High Dose"), method = "TTest") +
MultAdjProc(proc = "HolmAdj")
Active Comparator Design
DataModel() +
Sample(id = "Active Control", outcome.par = parameters(mean = 0.4, sd = 1)) +
Sample(id = "New Treatment", outcome.par = parameters(mean = 0.6, sd = 1))
Multi-Endpoint Designs
Co-Primary Endpoints
Both endpoints must be significant for trial success.
corr.matrix <- matrix(c(1.0, 0.5, 0.5, 1.0), 2, 2)
DataModel() +
OutcomeDist(outcome.dist = "MVNormalDist") +
SampleSize(100) +
Sample(id = list("Control E1", "Control E2"),
outcome.par = parameters(
parameters(par = parameters(
parameters(mean = 0, sd = 1),
parameters(mean = 0, sd = 1)
), corr = corr.matrix))) +
Sample(id = list("Treatment E1", "Treatment E2"),
outcome.par = parameters(
parameters(par = parameters(
parameters(mean = 0.4, sd = 1),
parameters(mean = 0.3, sd = 1)
), corr = corr.matrix)))
EvaluationModel() +
Criterion(id = "Co-primary",
method = "ConjunctivePower",
tests = tests("E1 Test", "E2 Test"),
par = parameters(alpha = 0.025))
Hierarchical Endpoints
Primary must succeed before secondary is tested.
AnalysisModel() +
Test(id = "Primary", ...) +
Test(id = "Key Secondary", ...) +
Test(id = "Other Secondary", ...) +
MultAdjProc(proc = "FixedSeqAdj")
Multiple Primary with Gatekeeping
MultAdjProc(
proc = "ParallelGatekeepingAdj",
par = parameters(
family = families(family1 = c(1, 2), family2 = c(3, 4)),
proc = families(family1 = "HolmAdj", family2 = "HolmAdj"),
gamma = families(family1 = 0.8, family2 = 1)
)
)
Stratified Designs
Single Stratification Factor
sim_pw_surv(
n = 400,
stratum = data.frame(
stratum = c("Low Risk", "High Risk"),
p = c(0.4, 0.6)
),
fail_rate = data.frame(
stratum = rep(c("Low Risk", "High Risk"), each = 2),
period = rep(1, 4),
treatment = rep(c("control", "experimental"), 2),
duration = rep(100, 4),
rate = c(0.03, 0.02, 0.06, 0.04)
)
)
Biomarker-Defined Subgroups
DataModel() +
OutcomeDist(outcome.dist = "NormalDist") +
SampleSize(100) +
Sample(id = "Control M+", outcome.par = parameters(mean = 0, sd = 1)) +
Sample(id = "Control M-", outcome.par = parameters(mean = 0, sd = 1)) +
Sample(id = "Treatment M+", outcome.par = parameters(mean = 0.6, sd = 1)) +
Sample(id = "Treatment M-", outcome.par = parameters(mean = 0.2, sd = 1))
AnalysisModel() +
Test(id = "Overall",
samples = samples(c("Control M+", "Control M-"),
c("Treatment M+", "Treatment M-")),
method = "TTest")
AnalysisModel() +
Test(id = "M+ Subgroup",
samples = samples("Control M+", "Treatment M+"),
method = "TTest") +
Test(id = "M- Subgroup",
samples = samples("Control M-", "Treatment M-"),
method = "TTest")
Event-Driven Designs
Time-to-Event with Fixed Events
DataModel() +
OutcomeDist(outcome.dist = "ExpoDist", outcome.type = "event") +
Event(n.events = c(300, 350, 400), rando.ratio = c(1, 1)) +
Design(
enroll.period = 24,
study.duration = 48,
enroll.dist = "UniformDist",
dropout.dist = "ExpoDist",
dropout.dist.par = parameters(rate = 0.01)
) +
Sample(id = "Control", outcome.par = parameters(rate = log(2)/12)) +
Sample(id = "Treatment", outcome.par = parameters(rate = log(2)/18))
PFS/OS Correlated Endpoints
DataModel() +
OutcomeDist(outcome.dist = "MVExpoPFSOSDist",
outcome.type = c("event", "event")) +
Event(n.events = 350, rando.ratio = c(1, 1)) +
Sample(id = list("Control PFS", "Control OS"),
outcome.par = parameters(
parameters(
par = parameters(
parameters(rate = log(2)/6),
parameters(rate = log(2)/15)
),
corr = matrix(c(1, 0.3, 0.3, 1), 2, 2)
))) +
Sample(id = list("Treatment PFS", "Treatment OS"),
outcome.par = parameters(
parameters(
par = parameters(
parameters(rate = log(2)/9),
parameters(rate = log(2)/20)
),
corr = matrix(c(1, 0.3, 0.3, 1), 2, 2)
)))
Adaptive Designs
Sample Size Re-Estimation
Concept: Adjust sample size at interim based on observed effect size.
simulate_adaptive <- function(initial_n, interim_frac, target_power) {
n_interim <- round(initial_n * interim_frac)
interim_data <- generate_data(n_interim)
effect_estimate <- estimate_effect(interim_data)
new_n <- calculate_sample_size(effect_estimate, target_power)
new_n <- max(new_n, initial_n)
final_data <- generate_data(new_n)
return(final_data)
}
Response-Adaptive Randomization
Concept: Adjust randomization ratio based on interim results.
Note: More common in Bayesian settings; simtrial/Mediana focus on fixed designs.
Design Selection Flowchart
START
│
├─ How many treatment arms?
│ ├─ 2 → Two-arm parallel
│ └─ 3+ → Multi-arm design
│
├─ How many primary endpoints?
│ ├─ 1 → Single primary
│ ├─ 2 (both required) → Co-primary
│ └─ 2+ (any success) → Multiple primary with multiplicity
│
├─ Are there secondary endpoints?
│ ├─ Yes, hierarchical → Fixed-sequence or gatekeeping
│ └─ Yes, equal priority → Holm/Hochberg
│
├─ Is stratification needed?
│ ├─ Yes → Stratified randomization
│ └─ No → Simple randomization
│
├─ Endpoint type?
│ ├─ Continuous → Normal-based tests
│ ├─ Binary → Proportion tests
│ ├─ Time-to-event → Logrank/survival methods
│ └─ Count → Poisson/NegBinom tests
│
└─ Interim analyses needed?
├─ Yes → Group sequential design
└─ No → Fixed design
END
Best Practices
- Match Design to Objectives: Choose design that directly addresses primary question
- Consider Multiplicity Early: Plan adjustment strategy during design phase
- Stratify When Important: Use stratification for known prognostic factors
- Pre-specify Everything: Document design choices before data collection
- Simulate Extensively: Validate operating characteristics via simulation
- Consider Regulatory Path: Align design with agency expectations