
Fast and efficient simulation tools for Bayesian Optimal Interval (BOIN) designs in Phase I clinical trials.
simFastBOIN provides comprehensive functions for simulating Phase I dose-finding trials using the BOIN design methodology. The package enables researchers to evaluate operating characteristics and design performance across different dose-toxicity scenarios, essential for protocol development and regulatory submissions.
Key Features: - High Performance: Vectorized implementation and optimized algorithms - BOIN Compatible: Results match BOIN package within <0.5% across all scenarios - User-Friendly: Simplified API with automatic decision table generation - Flexible: Multiple safety stopping rules and customizable design parameters - Multi-Scenario Support: Evaluate multiple dose-toxicity scenarios simultaneously - Conservative MTD Selection: Optional boundMTD constraint for enhanced safety - Publication-Ready Output: Professional table formatting with HTML, LaTeX, and Markdown options
You can install the development version from GitHub:
# Install if not already installed
# install.packages("devtools")
devtools::install_github("gosukehommaEX/simFastBOIN")library(simFastBOIN)
# Define design parameters
target <- 0.30 # Target DLT rate (30%)
p_true <- seq(0.05, 0.45, by = 0.05) # True toxicity probabilities
# Run simulation (runs silently by default)
result <- sim_boin(
n_trials = 10000,
target = target,
p_true = p_true,
n_cohort = 20,
cohort_size = 3,
seed = 123
)
# Display results
print(result$summary)
# To see progress messages, use verbose = TRUE
result_verbose <- sim_boin(
n_trials = 10000,
target = target,
p_true = p_true,
n_cohort = 20,
cohort_size = 3,
verbose = TRUE, # Show progress
seed = 123
)# Display with percentages instead of absolute numbers
print(result$summary, percent = TRUE)
# Display in Markdown pipe format for R Markdown documents
print(result$summary, kable = TRUE, kable_format = "pipe")
# LaTeX format for publication
print(result$summary, kable = TRUE, kable_format = "latex", scenario_name = "My Scenario")
# HTML format for web display
print(result$summary, kable = TRUE, kable_format = "html")# Define multiple dose-toxicity scenarios
scenarios <- list(
list(name = "Scenario 1: Linear Increase",
p_true = c(0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45)),
list(name = "Scenario 2: Steep Early",
p_true = c(0.01, 0.025, 0.05, 0.075, 0.10, 0.125, 0.15, 0.20, 0.30)),
list(name = "Scenario 3: Plateau",
p_true = c(0.15, 0.20, 0.25, 0.30, 0.45, 0.50, 0.50, 0.50, 0.50))
)
# Run simulations across all scenarios
result <- sim_boin_multi(
scenarios = scenarios,
target = 0.30,
n_trials = 10000,
n_cohort = 48,
cohort_size = 3,
seed = 123
)
# View aggregated results
print(result)
# View as HTML table
print(result, kable = TRUE, kable_format = "html")
# Access scenario-specific results
result$results_by_scenario[["Scenario 1: Linear Increase"]]sim_boin(): Run BOIN trial simulations
with automatic decision table generation
extrasafe,
boundMTD)n_earlystop_rule)sim_boin_multi(): Run simulations
across multiple scenarios
sim_boin() for each scenario
automaticallyget_boin_boundary(): Calculate BOIN
interval boundaries (lambda_e, lambda_d)get_boin_decision(): Generate decision
table for dose escalation/de-escalationget_boin_stopping_boundaries():
Generate safety stopping boundariesisotonic_regression(): Apply isotonic
regression to estimate dose-toxicity curveselect_mtd(): Select MTD with optional
boundMTD constraintprint.boin_summary(): Print formatted
summary tables with flexible optionsprint.boin_multi_summary(): Print
multi-scenario summary tablessimFastBOIN uses vectorized operations and efficient algorithms for superior performance:
Benchmark (5 doses, 20 cohorts, cohort_size=3, all safety features enabled):
| Number of Trials | simFastBOIN | BOIN Package | Speedup |
|---|---|---|---|
| 1,000 | 0.02 sec | 0.63 sec | 31.5x |
| 10,000 | 0.17 sec | 6.74 sec | 39.6x |
| 100,000 | 2.0 sec | 80.2 sec | 40.1x |
Results validated against the BOIN package using comprehensive test scenarios. All operating characteristics (MTD selection rates, sample sizes, toxicity counts) matched within <0.5%.
simFastBOIN produces results identical to the original BOIN package while offering significantly improved performance. This makes it ideal as a drop-in replacement for large-scale simulations where speed is critical.
Comprehensive validation was conducted comparing simFastBOIN with BOIN::get.oc() across multiple configurations:
Test Design: - Test Scenarios: 5 dose-toxicity scenarios (5 doses each) - scenario1: c(0.05, 0.10, 0.20, 0.30, 0.45) - MTD at dose 4 - scenario2: c(0.30, 0.40, 0.50, 0.60, 0.70) - All doses ≥ target - scenario3: c(0.05, 0.10, 0.15, 0.20, 0.25) - All doses < target - scenario4: c(0.15, 0.30, 0.45, 0.60, 0.75) - MTD at dose 2 - scenario5: c(0.01, 0.03, 0.08, 0.15, 0.30) - MTD at dose 5
Validation Results:
| Metric | Match Rate | Maximum Difference |
|---|---|---|
| PCS (MTD Selection %) | 100% (40/40) | 1.29% |
| Avg Patients per Dose | 100% (40/40) | 0.29 patients |
| Avg DLTs per Dose | 100% (40/40) | 0.10 DLTs |
| No MTD Selection Rate | 100% (40/40) | 0.76% |
Conclusion: All 40 configurations showed perfect metric match (all_match = TRUE) with simFastBOIN and BOIN package results. Maximum differences across all metrics were well within acceptable tolerance levels, confirming full compatibility.
The boundMTD option provides an additional safety
constraint during MTD selection:
# Without boundMTD: may select doses near toxicity boundary
result1 <- sim_boin(
n_trials = 1000,
target = 0.30,
p_true = c(0.10, 0.20, 0.28, 0.36, 0.50, 0.65),
n_cohort = 20,
cohort_size = 3,
boundMTD = FALSE,
seed = 123
)
# With boundMTD: more conservative selection
result2 <- sim_boin(
n_trials = 1000,
target = 0.30,
p_true = c(0.10, 0.20, 0.28, 0.36, 0.50, 0.65),
n_cohort = 20,
cohort_size = 3,
boundMTD = TRUE,
seed = 123
)Selected MTD must have isotonic-estimated toxicity rate below the de-escalation boundary (lambda_d), preventing selection of doses too close to overly toxic doses.
Two stopping rules are available:
n_earlystopn_earlystop
AND next decision is “Stay”# Simple rule: Stop immediately at n_earlystop
result_simple <- sim_boin(
n_trials = 1000,
target = 0.30,
p_true = c(0.05, 0.10, 0.20, 0.30, 0.45, 0.60),
n_cohort = 20,
cohort_size = 3,
n_earlystop_rule = "simple",
seed = 123
)
# With stay: Wait for convergence
result_with_stay <- sim_boin(
n_trials = 1000,
target = 0.30,
p_true = c(0.05, 0.10, 0.20, 0.30, 0.45, 0.60),
n_cohort = 20,
cohort_size = 3,
n_earlystop_rule = "with_stay",
seed = 123
)Additional safety rule to stop the entire trial if the lowest dose is overly toxic:
result <- sim_boin(
n_trials = 10000,
target = 0.30,
p_true = seq(0.05, 0.45, by = 0.05),
n_cohort = 48,
cohort_size = 3,
extrasafe = TRUE, # Enable safety stopping
offset = 0.05, # Cutoff = cutoff_eli - offset
seed = 123
)Multiple output format options for different use cases:
# Plain text (default)
print(result$summary)
# With percentages
print(result$summary, percent = TRUE)
# Markdown pipe format (for RMarkdown)
print(result$summary, kable = TRUE, kable_format = "pipe")
# LaTeX format (for publications)
print(result$summary, kable = TRUE, kable_format = "latex")
# HTML format (for web display)
print(result$summary, kable = TRUE, kable_format = "html")
# Simple format (minimal formatting)
print(result$summary, kable = TRUE, kable_format = "simple")
# Combine options
print(result$summary, scenario_name = "My Analysis", percent = TRUE, kable = TRUE)Recommended settings for BOIN standard compliance:
result <- sim_boin(
n_trials = 10000,
target = 0.30,
p_true = c(0.10, 0.25, 0.40, 0.55, 0.70),
n_cohort = 10,
cohort_size = 3,
boundMTD = TRUE, # Conservative MTD selection
n_earlystop_rule = "with_stay", # Stop when converged
extrasafe = TRUE, # Safety monitoring
seed = 123
)
print(result$summary, scenario_name = "BOIN Standard")library(simFastBOIN)
# Step 1: Define scenario
target <- 0.30
p_true <- c(0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45)
# Step 2: Run simulation
result <- sim_boin(
n_trials = 10000,
target = target,
p_true = p_true,
n_cohort = 48,
cohort_size = 3,
boundMTD = TRUE,
n_earlystop_rule = "with_stay",
extrasafe = TRUE,
return_details = TRUE, # Enable detailed trial-level results
seed = 123
)
# Step 3: Display results
print(result$summary, scenario_name = "Primary Analysis")
# Step 4: Export for RMarkdown
print(result$summary, kable = TRUE, kable_format = "pipe")
# Step 5: Check stopping reasons
table(sapply(result$detailed_results, function(x) x$reason))The package tracks why each trial terminated:
"trial_completed": Normal completion"n_earlystop_reached": Reached sample size limit
(simple rule)"n_earlystop_with_stay": Converged at sample size limit
(with_stay rule)"lowest_dose_too_toxic": Safety stopping at lowest dose
(extrasafe)"lowest_dose_eliminated": Lowest dose eliminated during
trial"no_dose_below_lambda_d": No dose satisfies boundMTD
constraint"max_cohorts_reached": Maximum number of cohorts
completedLiu, S. and Yuan, Y. (2015). Bayesian Optimal Interval Designs for Phase I Clinical Trials. Journal of the Royal Statistical Society: Series C, 64, 507–523.
See NEWS.md for detailed version history and changelog.
MIT © Gosuke Homma