BOIN, CRM and 3+3
BOIN
- Define trial parameters (target DLT, cohort size, number of
cohorts)
- Generate BOIN decision boundaries
- Extract escalation and de-escalation cutoffs
- Input current cohort toxicity data
- Calculate observed toxicity rate
- Compare toxicity rate with BOIN boundaries
- Make dose decision: escalate, stay, or de-escalate
# =========================================================
# Simple BOIN Example in R
# Single-Agent Phase I Dose Escalation Trial
# =========================================================
library(BOIN)
# =========================================================
# Step 1: Define Trial Design Parameters
# =========================================================
target <- 0.30
# Target DLT (Dose Limiting Toxicity) rate = 30%
ncohort <- 10
# Maximum number of cohorts planned in the trial
cohortsize <- 3
# Number of patients enrolled per cohort
# =========================================================
# Step 2: Generate BOIN Decision Boundaries
# =========================================================
bound <- get.boundary(
target = target,
ncohort = ncohort,
cohortsize = cohortsize
)
# Print BOIN design summary and boundaries
print(bound)
## $lambda_e
## [1] 0.2364907
##
## $lambda_d
## [1] 0.3585195
##
## $boundary_tab
##
## Number of patients treated 3 6 9 12 15 18 21 24 27 30
## Escalate if # of DLT <= 0 1 2 2 3 4 4 5 6 7
## Deescalate if # of DLT >= 2 3 4 5 6 7 8 9 10 11
## Eliminate if # of DLT >= 3 4 5 7 8 9 10 11 12 14
##
## $full_boundary_tab
##
## Number of patients treated 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
## Escalate if # of DLT <= 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3 4 4 4 4
## Deescalate if # of DLT >= 1 1 2 2 2 3 3 3 4 4 4 5 5 6 6 6 7 7 7 8
## Eliminate if # of DLT >= NA NA 3 3 4 4 5 5 5 6 6 7 7 8 8 8 9 9 9 10
##
## Number of patients treated 21 22 23 24 25 26 27 28 29 30
## Escalate if # of DLT <= 4 5 5 5 5 6 6 6 6 7
## Deescalate if # of DLT >= 8 8 9 9 9 10 10 11 11 11
## Eliminate if # of DLT >= 10 11 11 11 12 12 12 13 13 14
##
## attr(,"class")
## [1] "boin"
# =========================================================
# Step 3: Extract Escalation / De-escalation Boundaries
# =========================================================
lambda_e <- bound$lambda_e
# Escalation boundary
# If observed toxicity rate is BELOW this value,
# escalate to the next higher dose
lambda_d <- bound$lambda_d
# De-escalation boundary
# If observed toxicity rate is ABOVE this value,
# de-escalate to the next lower dose
# Print boundaries
cat("Escalation Boundary (lambda_e):", lambda_e, "\n")
## Escalation Boundary (lambda_e): 0.2364907
cat("De-escalation Boundary (lambda_d):", lambda_d, "\n")
## De-escalation Boundary (lambda_d): 0.3585195
# =========================================================
# Step 4: Current Cohort Data
# =========================================================
current_n <- 3
# Number of patients treated at current dose
current_tox <- 0
# Number of DLTs observed at current dose
# Calculate observed toxicity rate
obs_rate <- current_tox / current_n
cat("Observed Toxicity Rate:", obs_rate, "\n")
## Observed Toxicity Rate: 0
# =========================================================
# Step 5: BOIN Dose Escalation Decision
# =========================================================
if (obs_rate < lambda_e) {
decision <- "Escalate"
} else if (obs_rate > lambda_d) {
decision <- "De-escalate"
} else {
decision <- "Stay at Current Dose"
}
# Print final decision
cat("BOIN Decision:", decision, "\n")
## BOIN Decision: Escalate
CRM
- Define CRM design parameters. Set the target DLT rate, dose levels,
and prior toxicity skeleton.
- Create observed trial data. Enter patient-level dose assignment and
DLT outcomes.
- Fit the CRM model. Use the observed data to update toxicity
probabilities for all dose levels.
- Summarize estimated toxicity. Compare prior skeleton values with
posterior estimated toxicity probabilities.
- Select next recommended dose. Choose the dose with estimated
toxicity closest to the target DLT rate.
- Output recommendation. Print the recommended dose level for the next
cohort.
# =========================================================
# Simple CRM Example in R
# Single-Agent Phase I Dose Escalation Trial
# =========================================================
# install.packages("dfcrm")
library(dfcrm)
# =========================================================
# Step 1: Define CRM Design Parameters
# =========================================================
target <- 0.30
# Target DLT rate = 30%
dose_level <- 1:5
# Five dose levels
prior <- c(0.05, 0.10, 0.20, 0.35, 0.50)
# CRM skeleton:
# Initial guesses of DLT probability for each dose level
# Must be increasing with dose
# =========================================================
# Step 2: Create Observed Trial Data
# =========================================================
# Example:
# Cohort 1: Dose 1, 0/3 DLT
# Cohort 2: Dose 2, 0/3 DLT
# Cohort 3: Dose 3, 1/3 DLT
# Cohort 4: Dose 3, 1/3 DLT
level <- c(
rep(1, 3),
rep(2, 3),
rep(3, 3),
rep(3, 3)
)
tox <- c(
0, 0, 0, # Dose 1: 0/3 DLT
0, 0, 0, # Dose 2: 0/3 DLT
0, 0, 1, # Dose 3: 1/3 DLT
0, 1, 0 # Dose 3: 1/3 DLT
)
trial_data <- data.frame(
patient_id = 1:length(tox),
dose_level = level,
dlt = tox
)
print(trial_data)
## patient_id dose_level dlt
## 1 1 1 0
## 2 2 1 0
## 3 3 1 0
## 4 4 2 0
## 5 5 2 0
## 6 6 2 0
## 7 7 3 0
## 8 8 3 0
## 9 9 3 1
## 10 10 3 0
## 11 11 3 1
## 12 12 3 0
# =========================================================
# Step 3: Fit CRM Model
# =========================================================
fit <- crm(
prior = prior,
target = target,
tox = tox,
level = level,
model = "empiric",
method = "bayes"
)
print(fit)
## Today: Tue Jun 2 12:10:51 2026
## DATA SUMMARY (CRM)
## PID Level Toxicity Included
## 1 1 0 1
## 2 1 0 1
## 3 1 0 1
## 4 2 0 1
## 5 2 0 1
## 6 2 0 1
## 7 3 0 1
## 8 3 0 1
## 9 3 1 1
## 10 3 0 1
## 11 3 1 1
## 12 3 0 1
##
## Toxicity probability update (with 90 percent probability interval):
## Level Prior n total.wts total.tox Ptox LoLmt UpLmt
## 1 0.05 3 3 0 0.061 0.006 0.216
## 2 0.1 3 3 0 0.116 0.02 0.308
## 3 0.2 6 6 2 0.222 0.064 0.439
## 4 0.35 0 0 0 0.375 0.167 0.584
## 5 0.5 0 0 0 0.523 0.307 0.701
## Next recommended dose level: 4
## Recommendation is based on a target toxicity probability of 0.3
##
## Estimation details:
## Empiric dose-toxicity model: p = dose^{exp(beta)}
## dose = 0.05 0.1 0.2 0.35 0.5
## Normal prior on beta with mean 0 and variance 1.34
## Posterior mean of beta: -0.068
## Posterior variance of beta: 0.134
# =========================================================
# Step 4: Extract CRM Estimated Toxicity Probabilities
# =========================================================
estimated_tox <- fit$ptox
crm_summary <- data.frame(
dose_level = dose_level,
skeleton = prior,
estimated_toxicity = estimated_tox
)
print(crm_summary)
## dose_level skeleton estimated_toxicity
## 1 1 0.05 0.06093446
## 2 2 0.10 0.11641775
## 3 3 0.20 0.22242082
## 4 4 0.35 0.37511834
## 5 5 0.50 0.52341210
# =========================================================
# Step 5: Recommended Dose for Next Cohort
# =========================================================
recommended_dose <- fit$mtd
cat("Recommended dose level for next cohort:", recommended_dose, "\n")
## Recommended dose level for next cohort: 4
3+3
- Define patient dose levels and DLT outcomes
- Create trial dataset
- Summarize number of patients and DLTs at each dose
- Apply 3+3 rules sequentially:
0/3 DLT → escalate 1/3 DLT → expand to 6 patients ≤1/6 DLT → escalate
≥2/3 or ≥2/6 DLT → de-escalate/stop
- Select the previous safe dose as the MTD
# =========================================================
# Simple 3+3 Design Example in R
# Single-Agent Phase I Dose Escalation Trial
# =========================================================
# =========================================================
# Step 1: Define Observed Trial Data
# =========================================================
# Example:
# Cohort 1: Dose 1, 0/3 DLT -> escalate
# Cohort 2: Dose 2, 0/3 DLT -> escalate
# Cohort 3: Dose 3, 1/3 DLT -> expand dose 3
# Cohort 4: Dose 3, 0/3 DLT -> dose 3 has 1/6 DLT, escalate
# Cohort 5: Dose 4, 2/3 DLT -> de-escalate, stop
# Final MTD = Dose 3
dose_level <- c(
rep(1, 3),
rep(2, 3),
rep(3, 3),
rep(3, 3),
rep(4, 3)
)
dlt <- c(
0, 0, 0, # Dose 1: 0/3 DLT
0, 0, 0, # Dose 2: 0/3 DLT
0, 0, 1, # Dose 3: 1/3 DLT
0, 0, 0, # Dose 3: total 1/6 DLT
1, 1, 0 # Dose 4: 2/3 DLT
)
trial_data <- data.frame(
patient_id = 1:length(dlt),
dose_level = dose_level,
dlt = dlt
)
print(trial_data)
## patient_id dose_level dlt
## 1 1 1 0
## 2 2 1 0
## 3 3 1 0
## 4 4 2 0
## 5 5 2 0
## 6 6 2 0
## 7 7 3 0
## 8 8 3 0
## 9 9 3 1
## 10 10 3 0
## 11 11 3 0
## 12 12 3 0
## 13 13 4 1
## 14 14 4 1
## 15 15 4 0
# =========================================================
# Step 2: Summarize DLT by Dose
# =========================================================
summary_by_dose <- aggregate(
dlt ~ dose_level,
data = trial_data,
FUN = function(x) c(n = length(x), tox = sum(x), rate = mean(x))
)
summary_by_dose <- do.call(data.frame, summary_by_dose)
colnames(summary_by_dose) <- c("dose_level", "n", "dlt", "dlt_rate")
print(summary_by_dose)
## dose_level n dlt dlt_rate
## 1 1 3 0 0.0000000
## 2 2 3 0 0.0000000
## 3 3 6 1 0.1666667
## 4 4 3 2 0.6666667
# =========================================================
# Step 3: Apply Simple 3+3 Rule Manually
# =========================================================
cat("3+3 decision path:\n")
## 3+3 decision path:
cat("Dose 1: 0/3 DLT -> Escalate to Dose 2\n")
## Dose 1: 0/3 DLT -> Escalate to Dose 2
cat("Dose 2: 0/3 DLT -> Escalate to Dose 3\n")
## Dose 2: 0/3 DLT -> Escalate to Dose 3
cat("Dose 3: 1/3 DLT -> Expand Dose 3 to 6 patients\n")
## Dose 3: 1/3 DLT -> Expand Dose 3 to 6 patients
cat("Dose 3: 1/6 DLT -> Escalate to Dose 4\n")
## Dose 3: 1/6 DLT -> Escalate to Dose 4
cat("Dose 4: 2/3 DLT -> Stop and de-escalate\n")
## Dose 4: 2/3 DLT -> Stop and de-escalate
cat("Final MTD: Dose 3\n")
## Final MTD: Dose 3