Introduction to simFastBOIN

Gosuke Homma

2025-12-11

Overview

simFastBOIN provides fast and efficient simulation tools for Bayesian Optimal Interval (BOIN) designs in Phase I clinical trials. The package enables researchers to evaluate operating characteristics and design performance across different dose-toxicity scenarios.

Key Features

Installation

# Install from GitHub
devtools::install_github("gosukehommaEX/simFastBOIN")

Basic Usage

library(simFastBOIN)

Running a Basic BOIN Simulation

The most basic simulation requires only a few parameters:

# Define design parameters
target <- 0.30  # Target DLT rate (30%)
p_true <- c(0.10, 0.25, 0.40, 0.55, 0.70)  # True toxicity probabilities

# Run simulation (progress messages suppressed)
result <- sim_boin(
  n_trials = 1000,
  target = target,
  p_true = p_true,
  n_cohort = 10,
  cohort_size = 3,
  seed = 123
)

# Display results
print(result$summary)
#> |         Metric|       DL1|       DL2|       DL3|       DL4|       DL5|Total/No MTD|
#> ---------------------------------------------------------------------------------- 
#> |   True Tox (%)|      10.0|      25.0|      40.0|      55.0|      70.0|          |
#> |    MTD Sel (%)|      10.8|      52.4|      33.8|       2.8|       0.1|       0.1|
#> |        Avg Pts|       6.8|      11.9|       7.7|       1.8|       0.1|      28.4|
#> |       Avg DLTs|       0.7|       3.0|       3.1|       1.0|       0.1|       7.9|
#> ----------------------------------------------------------------------------------

Understanding the Output

The summary table contains four key sections:

  1. True Toxicity (%): The actual DLT rate at each dose
  2. MTD Selected (%): Percentage of trials selecting each dose as MTD
  3. Participants Treated (mean): Average patient enrollment at each dose
  4. Participants w/ DLTs (mean): Average DLT counts at each dose

BOIN Standard Implementation

For BOIN standard compliance, use the following recommended settings:

result_standard <- sim_boin(
  n_trials = 1000,
  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
  seed = 123
)

print(result_standard$summary, scenario_name = "BOIN Standard")
#> Scenario: BOIN Standard
#> 
#> |         Metric|       DL1|       DL2|       DL3|       DL4|       DL5|Total/No MTD|
#> ---------------------------------------------------------------------------------- 
#> |   True Tox (%)|      10.0|      25.0|      40.0|      55.0|      70.0|          |
#> |    MTD Sel (%)|      17.0|      55.8|      25.8|       1.2|       0.1|       0.1|
#> |        Avg Pts|       6.8|      11.9|       7.7|       1.8|       0.1|      28.4|
#> |       Avg DLTs|       0.7|       3.0|       3.1|       1.0|       0.1|       7.9|
#> ----------------------------------------------------------------------------------

Key Parameters

Safety Features

Extra Safety Stopping

For scenarios with high toxicity uncertainty:

result_safe <- sim_boin(
  n_trials = 1000,
  target = 0.30,
  p_true = c(0.05, 0.10, 0.20, 0.30, 0.45),
  n_cohort = 10,
  cohort_size = 3,
  extrasafe = TRUE,  # Safety monitoring at lowest dose
  offset = 0.05,     # Safety cutoff adjustment
  seed = 123
)

print(result_safe$summary, scenario_name = "With Extra Safety")
#> Scenario: With Extra Safety
#> 
#> |         Metric|       DL1|       DL2|       DL3|       DL4|       DL5|Total/No MTD|
#> ---------------------------------------------------------------------------------- 
#> |   True Tox (%)|       5.0|      10.0|      20.0|      30.0|      45.0|          |
#> |    MTD Sel (%)|       0.3|       5.1|      26.7|      48.4|      18.8|       0.7|
#> |        Avg Pts|       3.7|       5.4|       8.1|       8.2|       4.0|      29.5|
#> |       Avg DLTs|       0.2|       0.5|       1.6|       2.4|       1.8|       6.6|
#> ----------------------------------------------------------------------------------

Maximum Conservatism

Combining all safety features:

result_conservative <- sim_boin(
  n_trials = 1000,
  target = 0.30,
  p_true = seq(0.05, 0.45, by = 0.05),
  n_cohort = 20,
  cohort_size = 3,
  extrasafe = TRUE,
  boundMTD = TRUE,
  n_earlystop_rule = "with_stay",
  seed = 123
)

print(result_conservative$summary, scenario_name = "Maximum Conservatism")
#> Scenario: Maximum Conservatism
#> 
#> |         Metric|       DL1|       DL2|       DL3|       DL4|       DL5|       DL6|       DL7|       DL8|       DL9|Total/No MTD|
#> ------------------------------------------------------------------------------------------------------------------------------ 
#> |   True Tox (%)|       5.0|      10.0|      15.0|      20.0|      25.0|      30.0|      35.0|      40.0|      45.0|          |
#> |    MTD Sel (%)|       0.3|       2.0|       7.0|      22.0|      25.5|      23.1|      13.0|       5.5|       0.9|       0.7|
#> |        Avg Pts|       3.7|       4.8|       6.2|       8.6|       8.8|       7.1|       4.3|       1.8|       0.4|      45.8|
#> |       Avg DLTs|       0.2|       0.5|       0.9|       1.7|       2.2|       2.1|       1.5|       0.7|       0.2|      10.1|
#> ------------------------------------------------------------------------------------------------------------------------------

Multi-Scenario Simulations

Evaluate multiple dose-toxicity scenarios simultaneously:

# Define multiple scenarios
scenarios <- list(
  list(name = "Scenario 1: MTD at DL3", 
       p_true = c(0.05, 0.10, 0.20, 0.30, 0.45)),
  list(name = "Scenario 2: MTD at DL4", 
       p_true = c(0.10, 0.15, 0.25, 0.30, 0.45)),
  list(name = "Scenario 3: All doses safe", 
       p_true = c(0.05, 0.10, 0.15, 0.20, 0.25))
)

# Run multi-scenario simulation
result_multi <- sim_boin_multi(
  scenarios = scenarios,
  target = 0.30,
  n_trials = 1000,
  n_cohort = 10,
  cohort_size = 3,
  seed = 123
)
# Display aggregated results
print(result_multi)
#>                    Scenario         Item DL1  DL2  DL3  DL4  DL5 Total/No MTD
#>      Scenario 1: MTD at DL3 True Tox (%)   5   10   20   30   45             
#>                              MTD Sel (%) 0.2    5 27.9 48.4 18.4          0.1
#>                                  Avg Pts 3.9  5.5  8.2  8.1    4         29.7
#>                                 Avg DLTs 0.2  0.5  1.6  2.4  1.8          6.6
#>      Scenario 2: MTD at DL4 True Tox (%)  10   15   25   30   45             
#>                              MTD Sel (%)   2 12.6 32.6 37.4 15.2          0.2
#>                                  Avg Pts 4.8  6.9  8.5    6  3.2         29.3
#>                                 Avg DLTs 0.5    1  2.1  1.8  1.4          6.8
#>  Scenario 3: All doses safe True Tox (%)   5   10   15   20   25             
#>                              MTD Sel (%) 0.8  2.3 12.2 22.6 62.1            0
#>                                  Avg Pts 3.7  4.9  6.2  6.3  8.7         29.8
#>                                 Avg DLTs 0.2  0.5  0.9  1.3  2.2          5.1

Output Formatting Options

Percentage Format

Display values as percentages instead of absolute numbers:

print(result$summary, percent = TRUE)
#> |         Metric|       DL1|       DL2|       DL3|       DL4|       DL5|Total/No MTD|
#> ---------------------------------------------------------------------------------- 
#> |   True Tox (%)|      10.0|      25.0|      40.0|      55.0|      70.0|          |
#> |    MTD Sel (%)|      10.8|      52.4|      33.8|       2.8|       0.1|       0.1|
#> |    Avg Pts (%)|      24.0|      41.9|      27.2|       6.5|       0.5|      28.4|
#> |   Avg DLTs (%)|       8.6|      38.1|      39.0|      13.2|       1.1|       7.9|
#> ----------------------------------------------------------------------------------

Markdown Table Format

For R Markdown documents:

print(result$summary, kable = TRUE, kable_format = "pipe")
#> 
#> 
#> |Item         |DL1  |DL2  |DL3  |DL4 |DL5 |Total/No MTD |
#> |:------------|:----|:----|:----|:---|:---|:------------|
#> |True Tox (%) |10   |25   |40   |55  |70  |             |
#> |MTD Sel (%)  |10.8 |52.4 |33.8 |2.8 |0.1 |0.1          |
#> |Avg Pts      |6.8  |11.9 |7.7  |1.8 |0.1 |28.4         |
#> |Avg DLTs     |0.7  |3    |3.1  |1   |0.1 |7.9          |

HTML Format

For web-based reports:

print(result$summary, kable = TRUE, kable_format = "html")

Accessing Detailed Results

When return_details = TRUE, you can access trial-level information:

result_detailed <- sim_boin(
  n_trials = 100,
  target = 0.30,
  p_true = c(0.10, 0.25, 0.40, 0.55, 0.70),
  n_cohort = 10,
  cohort_size = 3,
  return_details = TRUE,
  seed = 123
)

# Check first trial
trial_1 <- result_detailed$detailed_results[[1]]
cat("Trial 1 MTD:", trial_1$mtd, "\n")
#> Trial 1 MTD: 2
cat("Trial 1 stopping reason:", trial_1$reason, "\n")
#> Trial 1 stopping reason: trial_completed

# Summary of stopping reasons
stopping_reasons <- table(sapply(result_detailed$detailed_results, 
                                function(x) x$reason))
print(stopping_reasons)
#> 
#> trial_completed 
#>             100

Design Comparison

Compare different safety configurations:

# Baseline
result_baseline <- 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,
  seed = 123
)

# With boundMTD
result_boundMTD <- 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,
  boundMTD = TRUE,
  seed = 123
)

# Create comparison
comparison <- data.frame(
  Setting = c("Baseline", "boundMTD"),
  Avg_Patients = c(
    result_baseline$summary$avg_total_n_pts,
    result_boundMTD$summary$avg_total_n_pts
  ),
  MTD_Selection_at_DL4 = c(
    result_baseline$summary$mtd_selection_percent[4],
    result_boundMTD$summary$mtd_selection_percent[4]
  )
)

print(comparison)
#>    Setting Avg_Patients MTD_Selection_at_DL4
#> 1 Baseline       40.842                 52.5
#> 2 boundMTD       40.842                 52.3

Performance Benchmarking

simFastBOIN is optimized for speed:

# Benchmark with 10,000 trials
system.time({
  result_large <- sim_boin(
    n_trials = 10000,
    target = 0.30,
    p_true = seq(0.05, 0.45, by = 0.05),
    n_cohort = 48,
    cohort_size = 3,
    seed = 123
  )
})

Typical performance: - 1,000 trials: ~0.02 seconds - 10,000 trials: ~0.25 seconds - 100,000 trials: ~2.7 seconds

Advanced Features

Custom Cohort Sizes

Use variable cohort sizes:

result_variable <- sim_boin(
  n_trials = 1000,
  target = 0.30,
  p_true = c(0.10, 0.25, 0.40, 0.55, 0.70),
  n_cohort = 10,
  cohort_size = c(1, 3, 3, 3, 3, 3, 3, 3, 3, 3),  # First cohort: 1 patient
  seed = 123
)

Titration Phase

Enable accelerated dose escalation:

result_titration <- sim_boin(
  n_trials = 1000,
  target = 0.30,
  p_true = c(0.05, 0.10, 0.20, 0.30, 0.45),
  n_cohort = 20,
  cohort_size = 3,
  titration = TRUE,  # Enable titration phase
  seed = 123
)

Stopping Reasons

The package tracks why each trial terminated:

References

Liu, 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 Also