From clinical-trial-simulation
Multiple testing procedures reference for clinical trials. Use when selecting or implementing multiplicity adjustments, gatekeeping procedures, or graphical approaches.
npx claudepluginhub choxos/biostatagent --plugin clinical-trial-simulationThis skill uses the workspace's default tool permissions.
- Selecting appropriate multiplicity adjustment procedures
Provides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
Analyzes competition with Porter's Five Forces, Blue Ocean Strategy, and positioning maps to identify differentiation opportunities and market positioning for startups and pitches.
FWER = P(reject at least one true null hypothesis)
Multiplicity adjustments control FWER at level α (typically 0.025 one-sided or 0.05 two-sided).
A hypothesis H_i can be rejected at level α if and only if all intersection hypotheses containing H_i are rejected at level α.
This principle underlies most powerful multiplicity procedures.
Method: Reject H_i if p_i ≤ α × w_i (where Σw_i = 1)
Properties:
MultAdjProc(proc = "BonferroniAdj",
par = parameters(weight = c(0.5, 0.5)))
Method:
Properties:
MultAdjProc(proc = "HolmAdj",
par = parameters(weight = c(0.6, 0.4)))
Method: Test hypotheses in predetermined order; stop at first non-rejection.
Properties:
MultAdjProc(proc = "FixedSeqAdj")
# Tests in order defined in AnalysisModel
Method:
Properties:
MultAdjProc(proc = "HochbergAdj",
par = parameters(weight = c(0.5, 0.5)))
Method: More complex step-up based on Simes' inequality
Properties:
MultAdjProc(proc = "HommelAdj")
Generalizes fixed-sequence with flexible weight transfer.
Components:
Algorithm:
# Equal split with full transfer
MultAdjProc(
proc = "ChainAdj",
par = parameters(
weight = c(0.5, 0.5),
transition = matrix(c(0, 1,
1, 0), 2, 2, byrow = TRUE)
)
)
Special case of chain where rejected hypothesis passes weight to next in sequence.
MultAdjProc(
proc = "FallbackAdj",
par = parameters(weight = c(0.5, 0.3, 0.2))
)
For trials with primary and secondary endpoint families where secondary can only be tested if at least one primary is rejected.
Structure:
Components:
family: List of hypothesis indices per familyproc: Procedure for each familygamma: Truncation parameter (0 = Bonferroni, 1 = Holm within family)MultAdjProc(
proc = "ParallelGatekeepingAdj",
par = parameters(
family = families(
family1 = c(1, 2), # Primary (H1, H2)
family2 = c(3, 4) # Secondary (H3, H4)
),
proc = families(
family1 = "HolmAdj",
family2 = "HolmAdj"
),
gamma = families(
family1 = 0.8, # Truncation for primary
family2 = 1 # Full Holm for secondary
)
),
tests = tests("Primary1", "Primary2", "Secondary1", "Secondary2")
)
For complex hierarchies with multiple sequences of hypotheses.
Example: Two doses (High, Low) each with primary and secondary endpoints.
MultAdjProc(
proc = "MultipleSequenceGatekeepingAdj",
par = parameters(
family = families(
family1 = c(1, 2), # Primary: DoseH, DoseL
family2 = c(3, 4) # Secondary: DoseH, DoseL
),
proc = families(
family1 = "HolmAdj",
family2 = "HolmAdj"
),
gamma = families(
family1 = 0.8,
family2 = 1
)
)
)
Combines serial and parallel gatekeeping components.
Components:
serial: Matrix indicating serial relationshipsparallel: Matrix indicating parallel relationshipsMultAdjProc(
proc = "MixtureGatekeepingAdj",
par = parameters(
family = families(family1 = c(1), family2 = c(2, 3)),
proc = families(family1 = "BonferroniAdj", family2 = "HolmAdj"),
gamma = families(family1 = 1, family2 = 0.8),
serial = matrix(c(0, 0, 0,
1, 0, 0,
1, 0, 0), 3, 3, byrow = TRUE),
parallel = matrix(c(0, 0, 0,
0, 0, 0,
0, 1, 0), 3, 3, byrow = TRUE)
)
)
Uses correlation structure for more powerful testing when test statistics are multivariate normal.
# Correlation from study design
corr.matrix <- matrix(c(1.0, 0.5, 0.5, 1.0), 2, 2)
MultAdjProc(
proc = "NormalParamAdj",
par = parameters(
corr = corr.matrix,
weight = c(0.5, 0.5)
)
)
# Compare multiple gamma values
gamma.values <- c(0.5, 0.6, 0.7, 0.8, 0.9, 1.0)
for (g in gamma.values) {
mult.adj <- MultAdjProc(
proc = "ParallelGatekeepingAdj",
par = parameters(
family = families(family1 = c(1, 2), family2 = c(3, 4)),
proc = families(family1 = "HolmAdj", family2 = "HolmAdj"),
gamma = families(family1 = g, family2 = 1)
)
)
# Run CSE and record power
}
| Structure | Recommended Procedure |
|---|---|
| Independent hypotheses | Holm or Hochberg |
| Strict hierarchy | Fixed-Sequence |
| Primary/Secondary | Parallel Gatekeeping |
| Multiple doses × endpoints | Multiple-Sequence |
| Complex dependencies | Graphical (Chain) |
| Dependence | Valid Procedures |
|---|---|
| Any | Bonferroni, Holm |
| PRDS/Independent | Hochberg, Hommel |
| Known correlation | NormalParamAdj |
| Priority | Procedure |
|---|---|
| First hypothesis | Fixed-Sequence |
| Equal priority | Holm with equal weights |
| Weighted priority | Graphical with weights |
# H1, H2 = primary; H3, H4 = secondary
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)
)
)
# All pairwise comparisons with equal weight
MultAdjProc(
proc = "HolmAdj",
par = parameters(weight = c(1/3, 1/3, 1/3))
)
# Primary → Key Secondary → Other Secondary
MultAdjProc(proc = "FixedSeqAdj")
# Two primary with full recycling
MultAdjProc(
proc = "ChainAdj",
par = parameters(
weight = c(0.5, 0.5),
transition = matrix(c(0, 1,
1, 0), 2, 2, byrow = TRUE)
)
)
Always validate FWER control under the global null:
# Set all treatment effects to null
null.data.model <- DataModel() +
OutcomeDist(outcome.dist = "NormalDist") +
SampleSize(100) +
Sample(id = "Control", outcome.par = parameters(mean = 0, sd = 1)) +
Sample(id = "Treatment", outcome.par = parameters(mean = 0, sd = 1))
# Check rejection rate ≤ alpha
null.results <- CSE(null.data.model, analysis.model, evaluation.model,
SimParameters(n.sims = 100000, proc.load = "full", seed = 123))
# DisjunctivePower under null = simulated FWER
# Should be ≤ 0.025 (one-sided)