The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
library(boinet)
library(dplyr)
# Load ggplot2 if available
if (requireNamespace("ggplot2", quietly = TRUE)) {
library(ggplot2)
}
The boinet package provides flexible result formatting capabilities for all BOIN-ET design family results. This vignette demonstrates how to format simulation results for analysis and reporting using the built-in functionality and standard R tools.
While the package continues to evolve with enhanced formatting functions, this vignette shows how to work with existing boinet results using standard R data manipulation techniques.
First, run your BOIN-ET simulation as usual:
# Example TITE-BOIN-ET simulation
<- tite.boinet(
result n.dose = 5,
start.dose = 1,
size.cohort = 3,
n.cohort = 15,
toxprob = c(0.02, 0.08, 0.15, 0.25, 0.40),
effprob = c(0.10, 0.20, 0.35, 0.50, 0.65),
phi = 0.30,
delta = 0.60,
tau.T = 28,
tau.E = 42,
accrual = 7,
estpt.method = "obs.prob",
obd.method = "max.effprob",
n.sim = 1000
)
# Extract operating characteristics manually
<- function(boinet_result) {
extract_oc_data <- names(boinet_result$n.patient)
dose_levels
data.frame(
dose_level = dose_levels,
toxicity_prob = as.numeric(boinet_result$toxprob),
efficacy_prob = as.numeric(boinet_result$effprob),
n_patients = as.numeric(boinet_result$n.patient),
selection_prob = as.numeric(boinet_result$prop.select),
stringsAsFactors = FALSE
)
}
# Extract design parameters manually
<- function(boinet_result) {
extract_design_data <- data.frame(
params parameter = c("Target Toxicity Rate (φ)", "Target Efficacy Rate (δ)",
"Lower Toxicity Boundary (λ₁)", "Upper Toxicity Boundary (λ₂)",
"Efficacy Boundary (η₁)", "Early Stop Rate (%)",
"Average Duration (days)", "Number of Simulations"),
value = c(boinet_result$phi, boinet_result$delta,
$lambda1, boinet_result$lambda2,
boinet_result$eta1, boinet_result$prop.stop,
boinet_result$duration, boinet_result$n.sim),
boinet_resultstringsAsFactors = FALSE
)
# Add time-specific parameters if available
if (!is.null(boinet_result$tau.T)) {
<- data.frame(
time_params parameter = c("Toxicity Assessment Window (days)",
"Efficacy Assessment Window (days)",
"Accrual Rate (days)"),
value = c(boinet_result$tau.T, boinet_result$tau.E, boinet_result$accrual),
stringsAsFactors = FALSE
)<- rbind(params, time_params)
params
}
return(params)
}
# Extract data
<- extract_oc_data(result)
oc_data <- extract_design_data(result)
design_data
# View operating characteristics
oc_data#> dose_level toxicity_prob efficacy_prob n_patients selection_prob
#> 1 1 0.02 0.10 8.2 5.2
#> 2 2 0.08 0.20 12.5 18.7
#> 3 3 0.15 0.35 15.8 42.1
#> 4 4 0.25 0.50 10.3 28.3
#> 5 5 0.40 0.65 7.2 5.7
# View design parameters
design_data#> parameter value
#> 1 Target Toxicity Rate (φ) 0.30
#> 2 Target Efficacy Rate (δ) 0.60
#> 3 Lower Toxicity Boundary (λ₁) 0.03
#> 4 Upper Toxicity Boundary (λ₂) 0.42
#> 5 Efficacy Boundary (η₁) 0.36
#> 6 Early Stop Rate (%) 3.20
#> 7 Average Duration (days) 156.30
#> 8 Number of Simulations 1000.00
#> 9 Toxicity Assessment Window (days) 28.00
#> 10 Efficacy Assessment Window (days) 42.00
#> 11 Accrual Rate (days) 7.00
The same extraction approach works with all BOIN-ET design family members:
# The extraction functions work with any boinet result type:
# - tite.boinet results
# - tite.gboinet results
# - boinet results
# - gboinet results
# Example usage:
# oc_data <- extract_oc_data(any_boinet_result)
# design_data <- extract_design_data(any_boinet_result)
# Find optimal dose
<- oc_data$dose_level[which.max(oc_data$selection_prob)]
optimal_dose cat("Optimal dose level:", optimal_dose, "\n")
#> Optimal dose level: 3
cat("Selection probability:", round(max(oc_data$selection_prob), 1), "%\n")
#> Selection probability: 42.1 %
# Doses with reasonable selection probability (>10%)
<- oc_data[oc_data$selection_prob > 10, ]
viable_doses
viable_doses#> dose_level toxicity_prob efficacy_prob n_patients selection_prob
#> 2 2 0.08 0.20 12.5 18.7
#> 3 3 0.15 0.35 15.8 42.1
#> 4 4 0.25 0.50 10.3 28.3
# Assess safety profile
<- oc_data %>%
safety_summary mutate(
safety_category = case_when(
<= 0.10 ~ "Low toxicity",
toxicity_prob <= 0.25 ~ "Moderate toxicity",
toxicity_prob TRUE ~ "High toxicity"
)%>%
) group_by(safety_category) %>%
summarise(
n_doses = n(),
total_selection_prob = sum(selection_prob),
avg_patients = mean(n_patients),
.groups = "drop"
)
safety_summary#> # A tibble: 3 × 4
#> safety_category n_doses total_selection_prob avg_patients
#> <chr> <int> <dbl> <dbl>
#> 1 High toxicity 1 5.7 7.2
#> 2 Low toxicity 2 23.9 10.4
#> 3 Moderate toxicity 2 70.4 13.0
# Analyze efficacy-toxicity trade-off
<- oc_data %>%
tradeoff_data mutate(
benefit_risk_ratio = efficacy_prob / (toxicity_prob + 0.01), # Add small constant to avoid division by zero
utility_score = efficacy_prob - 2 * toxicity_prob # Simple utility function
%>%
) arrange(desc(utility_score))
# Top doses by utility
head(tradeoff_data[, c("dose_level", "toxicity_prob", "efficacy_prob",
"selection_prob", "utility_score")], 3)
#> dose_level toxicity_prob efficacy_prob selection_prob utility_score
#> 1 1 0.02 0.10 5.2 0.06
#> 2 3 0.15 0.35 42.1 0.05
#> 3 2 0.08 0.20 18.7 0.04
if (ggplot2_available) {
%>%
oc_data ggplot(aes(x = dose_level, y = selection_prob)) +
geom_col(fill = "steelblue", alpha = 0.7) +
geom_text(aes(label = paste0(round(selection_prob, 1), "%")),
vjust = -0.3, size = 3.5) +
labs(
x = "Dose Level",
y = "Selection Probability (%)",
title = "TITE-BOIN-ET: Dose Selection Performance",
subtitle = paste("Optimal dose:", optimal_dose,
"selected in", round(max(oc_data$selection_prob), 1), "% of trials")
+
) theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
plot.subtitle = element_text(hjust = 0.5, size = 12)
)else {
} cat("ggplot2 package not available. Install with: install.packages('ggplot2')\n")
}
Dose Selection Probabilities
if (ggplot2_available) {
%>%
oc_data ggplot(aes(x = toxicity_prob, y = efficacy_prob)) +
geom_point(aes(size = selection_prob), alpha = 0.7, color = "darkred") +
geom_text(aes(label = dose_level), vjust = -1.2) +
scale_size_continuous(name = "Selection\nProbability (%)", range = c(2, 10)) +
labs(
x = "True Toxicity Probability",
y = "True Efficacy Probability",
title = "Efficacy-Toxicity Profile",
subtitle = "Point size represents selection probability"
+
) theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
plot.subtitle = element_text(hjust = 0.5, size = 12)
)else {
} cat("ggplot2 package not available for visualization.\n")
}
Efficacy vs Toxicity Trade-off
# Create a nicely formatted table using base R
<- function(oc_data) {
create_oc_summary <- oc_data
formatted_data $toxicity_prob <- round(formatted_data$toxicity_prob, 3)
formatted_data$efficacy_prob <- round(formatted_data$efficacy_prob, 3)
formatted_data$n_patients <- round(formatted_data$n_patients, 1)
formatted_data$selection_prob <- round(formatted_data$selection_prob, 1)
formatted_data
# Rename columns for display
names(formatted_data) <- c("Dose Level", "True Toxicity Prob",
"True Efficacy Prob", "Avg N Treated",
"Selection Prob (%)")
return(formatted_data)
}
# Create formatted table
<- create_oc_summary(oc_data)
formatted_oc print(formatted_oc)
#> Dose Level True Toxicity Prob True Efficacy Prob Avg N Treated
#> 1 1 0.02 0.10 8.2
#> 2 2 0.08 0.20 12.5
#> 3 3 0.15 0.35 15.8
#> 4 4 0.25 0.50 10.3
#> 5 5 0.40 0.65 7.2
#> Selection Prob (%)
#> 1 5.2
#> 2 18.7
#> 3 42.1
#> 4 28.3
#> 5 5.7
# Create formatted design parameters table
<- function(design_data) {
create_design_summary <- design_data
formatted_design $value <- round(as.numeric(formatted_design$value), 3)
formatted_design
# Clean up parameter names
names(formatted_design) <- c("Parameter", "Value")
return(formatted_design)
}
<- create_design_summary(design_data)
formatted_design print(formatted_design)
#> Parameter Value
#> 1 Target Toxicity Rate (φ) 0.30
#> 2 Target Efficacy Rate (δ) 0.60
#> 3 Lower Toxicity Boundary (λ₁) 0.03
#> 4 Upper Toxicity Boundary (λ₂) 0.42
#> 5 Efficacy Boundary (η₁) 0.36
#> 6 Early Stop Rate (%) 3.20
#> 7 Average Duration (days) 156.30
#> 8 Number of Simulations 1000.00
#> 9 Toxicity Assessment Window (days) 28.00
#> 10 Efficacy Assessment Window (days) 42.00
#> 11 Accrual Rate (days) 7.00
The package provides enhanced summary methods for all boinet result types:
# Enhanced summary automatically detects design type
summary(result)
#> TITE-BOIN-ET Design Operating Characteristics
#> =========================================
#>
#> Design Parameters:
#> Target Toxicity Probability: 0.30
#> Target Efficacy Probability: 0.60
#> Trial Duration: 156.3 days
#> Early Stop Probability: 3.2%
#>
#> Operating Characteristics by Dose Level:
#> # A tibble: 5 × 6
#> dose_level toxicity_prob efficacy_prob n_patients selection_prob selection_pct
#> <chr> <dbl> <dbl> <dbl> <dbl> <chr>
#> 1 1 0.02 0.1 8.2 5.2 5.2%
#> 2 2 0.08 0.2 12.5 18.7 18.7%
#> 3 3 0.15 0.35 15.8 42.1 42.1%
#> 4 4 0.25 0.5 10.3 28.3 28.3%
#> 5 5 0.4 0.65 7.2 5.7 5.7%
# Export data for external analysis
write.csv(oc_data, "operating_characteristics.csv", row.names = FALSE)
write.csv(design_data, "design_parameters.csv", row.names = FALSE)
# Save as RDS for R users
saveRDS(list(oc_data = oc_data, design_data = design_data), "boinet_results.rds")
# Create summary report
<- list(
summary_stats optimal_dose = optimal_dose,
max_selection_prob = max(oc_data$selection_prob),
early_stop_rate = result$prop.stop,
avg_duration = result$duration,
design_type = class(result)[1]
)
saveRDS(summary_stats, "summary_statistics.rds")
Always follow the pattern: simulate first, then extract and analyze data.
# Always check your results make sense
<- sum(oc_data$selection_prob) + as.numeric(result$prop.stop)
total_selection cat("Total probability (selection + early stop):", round(total_selection, 1), "%\n")
#> Total probability (selection + early stop): 103.2 %
# Check for reasonable dose allocation
cat("Patient allocation summary:\n")
#> Patient allocation summary:
print(summary(oc_data$n_patients))
#> Min. 1st Qu. Median Mean 3rd Qu. Max.
#> 7.2 8.2 10.3 10.8 12.5 15.8
# Verify dose ordering makes sense
if (all(diff(oc_data$toxicity_prob) >= 0)) {
cat("✓ Toxicity probabilities are non-decreasing\n")
else {
} cat("⚠ Warning: Toxicity probabilities not monotonic\n")
}#> ✓ Toxicity probabilities are non-decreasing
# Document analysis parameters
<- list(
analysis_info date = Sys.Date(),
design_type = class(result)[1],
r_version = R.version.string,
boinet_version = as.character(packageVersion("boinet")),
key_findings = list(
optimal_dose = optimal_dose,
selection_probability = max(oc_data$selection_prob),
early_stop_rate = as.numeric(result$prop.stop)
)
)
# Display analysis info
str(analysis_info)
#> List of 5
#> $ date : Date[1:1], format: "2025-06-05"
#> $ design_type : chr "tite.boinet"
#> $ r_version : chr "R version 4.3.3 (2024-02-29 ucrt)"
#> $ boinet_version: chr "1.2.0"
#> $ key_findings :List of 3
#> ..$ optimal_dose : chr "3"
#> ..$ selection_probability: num 42.1
#> ..$ early_stop_rate : num 3.2
# Create reusable utility functions
<- function(eff_prob, tox_prob, eff_weight = 1, tox_weight = 2) {
calculate_utility_score * eff_prob - tox_weight * tox_prob
eff_weight
}
<- function(oc_data, n_top = 3) {
find_best_doses %>%
oc_data arrange(desc(selection_prob)) %>%
head(n_top) %>%
select(dose_level, selection_prob, toxicity_prob, efficacy_prob)
}
# Use utility functions
$utility <- calculate_utility_score(oc_data$efficacy_prob, oc_data$toxicity_prob)
oc_data<- find_best_doses(oc_data)
top_doses
cat("Top doses by selection probability:\n")
#> Top doses by selection probability:
print(top_doses)
#> dose_level selection_prob toxicity_prob efficacy_prob
#> 1 3 42.1 0.15 0.35
#> 2 4 28.3 0.25 0.50
#> 3 2 18.7 0.08 0.20
# Analyze sensitivity to design parameters
<- data.frame(
sensitivity_summary metric = c("Optimal Dose", "Max Selection %", "Early Stop %",
"Avg Duration", "Total Patients"),
value = c(optimal_dose, round(max(oc_data$selection_prob), 1),
round(result$prop.stop, 1), round(result$duration, 0),
round(sum(oc_data$n_patients), 0)),
stringsAsFactors = FALSE
)
print(sensitivity_summary)
#> metric value
#> 1 Optimal Dose 3
#> 2 Max Selection % 42.1
#> 3 Early Stop % 3.2
#> 4 Avg Duration 156
#> 5 Total Patients 54
# Framework for comparing multiple designs
<- function(result_list, design_names) {
create_design_comparison <- data.frame()
comparison_data
for (i in seq_along(result_list)) {
<- extract_oc_data(result_list[[i]])
oc_data <- oc_data$dose_level[which.max(oc_data$selection_prob)]
optimal_dose
<- data.frame(
summary_row design = design_names[i],
optimal_dose = optimal_dose,
max_selection = max(oc_data$selection_prob),
early_stop = result_list[[i]]$prop.stop,
avg_duration = result_list[[i]]$duration,
stringsAsFactors = FALSE
)
<- rbind(comparison_data, summary_row)
comparison_data
}
return(comparison_data)
}
# Example usage (would work with multiple results)
cat("Comparison framework ready for multiple design evaluation\n")
#> Comparison framework ready for multiple design evaluation
The boinet package provides a solid foundation for analyzing BOIN-ET simulation results. Using standard R data manipulation techniques, you can:
As the package continues to evolve, additional convenience functions will be added, but the core approach of extracting and analyzing the structured results remains consistent across all BOIN-ET design types.
For publication-ready tables, see the gt-integration
vignette. For complete reporting workflows, see the
quarto-workflow
vignette.
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.