knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 5 ) # Check package availability ggplot2_available <- requireNamespace("ggplot2", quietly = TRUE)
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 result <- tite.boinet( 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 )
# Create mock result for demonstration result <- list( toxprob = c("1" = 0.02, "2" = 0.08, "3" = 0.15, "4" = 0.25, "5" = 0.40), effprob = c("1" = 0.10, "2" = 0.20, "3" = 0.35, "4" = 0.50, "5" = 0.65), n.patient = c("1" = 8.2, "2" = 12.5, "3" = 15.8, "4" = 10.3, "5" = 7.2), prop.select = c("1" = 5.2, "2" = 18.7, "3" = 42.1, "4" = 28.3, "5" = 5.7), phi = 0.30, delta = 0.60, lambda1 = 0.03, lambda2 = 0.42, eta1 = 0.36, tau.T = 28, tau.E = 42, accrual = 7, duration = 156.3, prop.stop = 3.2, n.sim = 1000 ) class(result) <- "tite.boinet"
# Extract operating characteristics manually extract_oc_data <- function(boinet_result) { dose_levels <- names(boinet_result$n.patient) 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 extract_design_data <- function(boinet_result) { params <- data.frame( 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, boinet_result$lambda1, boinet_result$lambda2, boinet_result$eta1, boinet_result$prop.stop, boinet_result$duration, boinet_result$n.sim), stringsAsFactors = FALSE ) # Add time-specific parameters if available if (!is.null(boinet_result$tau.T)) { time_params <- data.frame( 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 ) params <- rbind(params, time_params) } return(params) } # Extract data oc_data <- extract_oc_data(result) design_data <- extract_design_data(result) # View operating characteristics oc_data
# View design parameters
design_data
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 optimal_dose <- oc_data$dose_level[which.max(oc_data$selection_prob)] cat("Optimal dose level:", optimal_dose, "\n") cat("Selection probability:", round(max(oc_data$selection_prob), 1), "%\n") # Doses with reasonable selection probability (>10%) viable_doses <- oc_data[oc_data$selection_prob > 10, ] viable_doses
# Assess safety profile safety_summary <- oc_data %>% mutate( safety_category = case_when( toxicity_prob <= 0.10 ~ "Low toxicity", toxicity_prob <= 0.25 ~ "Moderate toxicity", 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
# Analyze efficacy-toxicity trade-off tradeoff_data <- oc_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)
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") }
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") }
# Create a nicely formatted table using base R create_oc_summary <- function(oc_data) { formatted_data <- 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) # 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 formatted_oc <- create_oc_summary(oc_data) print(formatted_oc)
# Create formatted design parameters table create_design_summary <- function(design_data) { formatted_design <- design_data formatted_design$value <- round(as.numeric(formatted_design$value), 3) # Clean up parameter names names(formatted_design) <- c("Parameter", "Value") return(formatted_design) } formatted_design <- create_design_summary(design_data) print(formatted_design)
The package provides enhanced summary methods for all boinet result types:
# Enhanced summary automatically detects design type summary(result)
# 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 summary_stats <- list( 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 total_selection <- sum(oc_data$selection_prob) + as.numeric(result$prop.stop) cat("Total probability (selection + early stop):", round(total_selection, 1), "%\n") # Check for reasonable dose allocation cat("Patient allocation summary:\n") print(summary(oc_data$n_patients)) # 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") }
# Document analysis parameters analysis_info <- list( 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)
# Create reusable utility functions calculate_utility_score <- function(eff_prob, tox_prob, eff_weight = 1, tox_weight = 2) { eff_weight * eff_prob - tox_weight * tox_prob } find_best_doses <- function(oc_data, n_top = 3) { oc_data %>% arrange(desc(selection_prob)) %>% head(n_top) %>% select(dose_level, selection_prob, toxicity_prob, efficacy_prob) } # Use utility functions oc_data$utility <- calculate_utility_score(oc_data$efficacy_prob, oc_data$toxicity_prob) top_doses <- find_best_doses(oc_data) cat("Top doses by selection probability:\n") print(top_doses)
# Analyze sensitivity to design parameters sensitivity_summary <- data.frame( 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)
# Framework for comparing multiple designs create_design_comparison <- function(result_list, design_names) { comparison_data <- data.frame() for (i in seq_along(result_list)) { oc_data <- extract_oc_data(result_list[[i]]) optimal_dose <- oc_data$dose_level[which.max(oc_data$selection_prob)] summary_row <- data.frame( 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 ) comparison_data <- rbind(comparison_data, summary_row) } return(comparison_data) } # Example usage (would work with multiple results) cat("Comparison framework ready for multiple design evaluation\n")
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.
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.