From clinical-trial-simulation
Core Mediana package functions for Clinical Scenario Evaluation (CSE). Use when designing data models, analysis models, evaluation models, and running comprehensive trial simulations.
npx claudepluginhub choxos/biostatagent --plugin clinical-trial-simulationThis skill uses the workspace's default tool permissions.
- Building Clinical Scenario Evaluation (CSE) frameworks
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.
Mediana (v1.0.9) by Gautier Paux and Alex Dmitrienko provides a general framework for clinical trial simulations based on the Clinical Scenario Evaluation approach (Benda et al., 2010).
data.model <- DataModel()
Specifies the distribution of patient outcomes.
OutcomeDist(
outcome.dist = "NormalDist", # Distribution type
outcome.type = "standard" # "standard" or "event"
)
Supported Distributions:
| Distribution | Parameters | Use Case |
|---|---|---|
UniformDist | max | Uniform outcomes |
NormalDist | mean, sd | Continuous endpoints |
BinomDist | prop | Binary endpoints |
BetaDist | a, b | Proportions |
ExpoDist | rate | Time-to-event |
WeibullDist | shape, scale | Survival with shape |
TruncatedExpoDist | rate, trunc | Truncated survival |
PoissonDist | lambda | Count data |
NegBinomDist | dispersion, mean | Overdispersed counts |
MultinomialDist | prob | Categorical outcomes |
Multivariate Distributions:
| Distribution | Parameters | Use Case |
|---|---|---|
MVNormalDist | par, corr | Correlated continuous |
MVBinomDist | par, corr | Correlated binary |
MVExpoDist | par, corr | Correlated survival |
MVExpoPFSOSDist | par, corr | PFS/OS endpoints |
MVMixedDist | type, par, corr | Mixed endpoint types |
# Normal distribution parameters
outcome.placebo <- parameters(mean = 0, sd = 70)
outcome.treatment <- parameters(mean = 40, sd = 70)
# Define samples
Sample(id = "Placebo",
outcome.par = parameters(outcome.placebo))
Sample(id = "Treatment",
outcome.par = parameters(outcome.treatment))
Multiple Scenarios:
# Define multiple effect size scenarios
outcome1.placebo <- parameters(mean = 0, sd = 70)
outcome1.treatment <- parameters(mean = 40, sd = 70) # Conservative
outcome2.placebo <- parameters(mean = 0, sd = 70)
outcome2.treatment <- parameters(mean = 50, sd = 70) # Optimistic
Sample(id = "Placebo",
outcome.par = parameters(outcome1.placebo, outcome2.placebo))
Sample(id = "Treatment",
outcome.par = parameters(outcome1.treatment, outcome2.treatment))
SampleSize(c(50, 55, 60, 65, 70)) # Per arm
SampleSize(seq(50, 100, 10))
Event(
n.events = c(390, 420), # Total event counts to evaluate
rando.ratio = c(1, 2) # Control:Treatment ratio
)
# Non-uniform enrollment with beta distribution
# 50% enrolled at 75% of enrollment period
enroll.par <- parameters(
a = log(0.5)/log(0.75),
b = 1
)
Design(
enroll.period = 12, # Enrollment duration (months)
study.duration = 36, # Total study duration
enroll.dist = "BetaDist", # Or "UniformDist"
enroll.dist.par = enroll.par,
dropout.dist = "ExpoDist",
dropout.dist.par = parameters(rate = 0.0115)
)
# Time-to-event trial with PFS and OS
median.pfs.placebo <- 6
median.pfs.treatment <- 9
median.os.placebo <- 15
median.os.treatment <- 19
placebo.par <- parameters(
parameters(rate = log(2)/median.pfs.placebo),
parameters(rate = log(2)/median.os.placebo)
)
treatment.par <- parameters(
parameters(rate = log(2)/median.pfs.treatment),
parameters(rate = log(2)/median.os.treatment)
)
corr.matrix <- matrix(c(1.0, 0.3, 0.3, 1.0), 2, 2)
data.model <- DataModel() +
OutcomeDist(outcome.dist = "MVExpoPFSOSDist",
outcome.type = c("event", "event")) +
Event(n.events = c(390, 420), rando.ratio = c(1, 2)) +
Design(enroll.period = 12, study.duration = 30,
enroll.dist = "BetaDist",
enroll.dist.par = parameters(a = log(0.5)/log(0.75), b = 1),
dropout.dist = "ExpoDist",
dropout.dist.par = parameters(rate = 0.0115)) +
Sample(id = list("Placebo PFS", "Placebo OS"),
outcome.par = parameters(parameters(par = placebo.par,
corr = corr.matrix))) +
Sample(id = list("Treatment PFS", "Treatment OS"),
outcome.par = parameters(parameters(par = treatment.par,
corr = corr.matrix)))
analysis.model <- AnalysisModel()
Test(
id = "Primary", # Unique test ID
samples = samples("Placebo", "Treatment"), # Samples to compare
method = "TTest", # Test method
par = parameters(...) # Optional parameters
)
Built-in Tests:
| Method | Description | Parameters |
|---|---|---|
TTest | Two-sample t-test | larger (optional) |
TTestNI | Non-inferiority t-test | margin, larger |
WilcoxTest | Wilcoxon-Mann-Whitney | larger |
PropTest | Two-sample proportion | yates, larger |
PropTestNI | NI proportion test | margin, yates, larger |
FisherTest | Fisher exact test | larger |
GLMPoissonTest | Poisson regression | larger |
GLMNegBinomTest | Negative binomial | larger |
LogrankTest | Log-rank test | larger |
OrdinalLogisticRegTest | Ordinal logistic | larger |
Note: Tests are one-sided. By default, larger values expected in Sample 2. Set larger = FALSE if larger values expected in Sample 1.
Statistic(
id = "Mean Treatment",
method = "MeanStat",
samples = samples("Treatment")
)
Built-in Statistics:
| Method | Description | Samples Required |
|---|---|---|
MeanStat | Mean | 1 |
MedianStat | Median | 1 |
SdStat | Standard deviation | 1 |
MinStat | Minimum | 1 |
MaxStat | Maximum | 1 |
PropStat | Proportion | 1 |
DiffMeanStat | Difference in means | 2 |
DiffPropStat | Difference in proportions | 2 |
EffectSizeContStat | Effect size (continuous) | 2 |
EffectSizePropStat | Effect size (binary) | 2 |
EffectSizeEventStat | Effect size (survival) | 2 |
HazardRatioStat | Hazard ratio | 2 |
EventCountStat | Number of events | 1+ |
PatientCountStat | Number of patients | 1+ |
MultAdjProc(
proc = "HolmAdj",
par = parameters(weight = c(0.5, 0.5)),
tests = tests("Test1", "Test2") # Optional: applies to all if omitted
)
Built-in Procedures:
| Procedure | Type | Parameters |
|---|---|---|
BonferroniAdj | Single-step | weight |
HolmAdj | Step-down | weight |
HochbergAdj | Step-up | weight |
HommelAdj | Step-up | weight |
FixedSeqAdj | Sequential | (order from tests) |
ChainAdj | Graphical | weight, transition |
FallbackAdj | Fallback | weight |
NormalParamAdj | Parametric | corr, weight |
ParallelGatekeepingAdj | Gatekeeping | family, proc, gamma |
MultipleSequenceGatekeepingAdj | Gatekeeping | family, proc, gamma |
MixtureGatekeepingAdj | Gatekeeping | family, proc, gamma, serial, parallel |
Chain Procedure:
MultAdjProc(
proc = "ChainAdj",
par = parameters(
weight = c(0.5, 0.5),
transition = matrix(c(0, 1,
1, 0), 2, 2, byrow = TRUE)
)
)
Parallel Gatekeeping:
MultAdjProc(
proc = "ParallelGatekeepingAdj",
par = parameters(
family = families(
family1 = c(1, 2), # Primary endpoints
family2 = c(3, 4) # Secondary endpoints
),
proc = families(
family1 = "HolmAdj",
family2 = "HolmAdj"
),
gamma = families(
family1 = 0.8, # Truncation parameter
family2 = 1
)
),
tests = tests("Primary1", "Primary2", "Secondary1", "Secondary2")
)
Multiple-Sequence Gatekeeping:
MultAdjProc(
proc = "MultipleSequenceGatekeepingAdj",
par = parameters(
family = families(family1 = c(1, 2), family2 = c(3, 4)),
proc = families(family1 = "HolmAdj", family2 = "HochbergAdj"),
gamma = families(family1 = 0.8, family2 = 1)
)
)
analysis.model <- AnalysisModel() +
# Primary tests
Test(id = "PFS test",
samples = samples("Placebo PFS", "Treatment PFS"),
method = "LogrankTest") +
Test(id = "OS test",
samples = samples("Placebo OS", "Treatment OS"),
method = "LogrankTest") +
# Fixed-sequence multiplicity adjustment
MultAdjProc(proc = "FixedSeqAdj") +
# Descriptive statistics
Statistic(id = "Patients Placebo",
samples = samples("Placebo PFS"),
method = "PatientCountStat") +
Statistic(id = "Patients Treatment",
samples = samples("Treatment PFS"),
method = "PatientCountStat")
evaluation.model <- EvaluationModel()
Criterion(
id = "Marginal power",
method = "MarginalPower",
tests = tests("Primary"),
labels = c("Primary Power"),
par = parameters(alpha = 0.025)
)
Built-in Criteria:
| Method | Description | Parameters |
|---|---|---|
MarginalPower | Power for each test | alpha |
WeightedPower | Weighted combination | alpha, weight |
DisjunctivePower | P(reject at least one) | alpha |
ConjunctivePower | P(reject all) | alpha |
ExpectedRejPower | Expected # rejected | alpha |
MeanSumm | Mean of statistics | - |
MedianSumm | Median of statistics | - |
evaluation.model <- EvaluationModel() +
Criterion(id = "Marginal power",
method = "MarginalPower",
tests = tests("PFS test", "OS test"),
labels = c("PFS Power", "OS Power"),
par = parameters(alpha = 0.025)) +
Criterion(id = "Disjunctive power",
method = "DisjunctivePower",
tests = tests("PFS test", "OS test"),
labels = c("At least one significant"),
par = parameters(alpha = 0.025)) +
Criterion(id = "Average patients",
method = "MeanSumm",
statistics = statistics("Patients Placebo", "Patients Treatment"),
labels = c("Mean Placebo N", "Mean Treatment N"))
sim.parameters <- SimParameters(
n.sims = 10000, # Number of simulations
proc.load = "full", # Parallelization: "low", "med", "high", "full", or integer
seed = 42938001 # For reproducibility
)
results <- CSE(
data.model,
analysis.model,
evaluation.model,
sim.parameters
)
# View summary
summary(results)
The CSE function returns a list with:
simulation.results: Data frame of results per scenarioanalysis.scenario.grid: Grid of data/analysis combinationsdata.structure: Data model structureanalysis.structure: Analysis model structureevaluation.structure: Evaluation model structuresim.parameters: Simulation parameterstimestamp: Start/end time and durationpresentation.model <- PresentationModel() +
Project(username = "Analyst Name",
title = "Phase III Trial Simulation",
description = "Power analysis for multi-endpoint trial") +
Section(by = "outcome.parameter") +
Subsection(by = "sample.size") +
Table(by = "multiplicity.adjustment") +
CustomLabel(param = "sample.size",
label = paste0("N = ", c(50, 60, 70))) +
CustomLabel(param = "outcome.parameter",
label = c("Conservative", "Expected", "Optimistic"))
GenerateReport(
presentation.model = presentation.model,
cse.results = results,
report.filename = "Simulation_Report.docx"
)
library(Mediana)
# Data Model
outcome.placebo <- parameters(mean = 0, sd = 1)
outcome.trt.conservative <- parameters(mean = 0.3, sd = 1)
outcome.trt.expected <- parameters(mean = 0.5, sd = 1)
data.model <- DataModel() +
OutcomeDist(outcome.dist = "NormalDist") +
SampleSize(seq(80, 120, 10)) +
Sample(id = "Placebo",
outcome.par = parameters(outcome.placebo, outcome.placebo)) +
Sample(id = "Treatment",
outcome.par = parameters(outcome.trt.conservative, outcome.trt.expected))
# Analysis Model
analysis.model <- AnalysisModel() +
Test(id = "Primary",
samples = samples("Placebo", "Treatment"),
method = "TTest") +
Statistic(id = "Effect Size",
samples = samples("Placebo", "Treatment"),
method = "EffectSizeContStat")
# Evaluation Model
evaluation.model <- EvaluationModel() +
Criterion(id = "Power",
method = "MarginalPower",
tests = tests("Primary"),
labels = "Primary Power",
par = parameters(alpha = 0.025)) +
Criterion(id = "Mean Effect",
method = "MeanSumm",
statistics = statistics("Effect Size"),
labels = "Mean Effect Size")
# Run Simulations
results <- CSE(
data.model,
analysis.model,
evaluation.model,
SimParameters(n.sims = 10000, proc.load = "full", seed = 12345)
)
# View Results
summary(results)
# Generate Report
presentation.model <- PresentationModel() +
Project(title = "Sample Size Analysis") +
Section(by = "outcome.parameter") +
Table(by = "sample.size") +
CustomLabel(param = "outcome.parameter",
label = c("Conservative", "Expected"))
GenerateReport(presentation.model, results, "Analysis_Report.docx")
proc.load = "full" for large simulations