--- title: "Complete Quarto Workflow for BOIN-ET Reports" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Complete Quarto Workflow for BOIN-ET Reports} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 8, fig.height = 6 ) # Check package availability gt_available <- requireNamespace("gt", quietly = TRUE) ggplot2_available <- requireNamespace("ggplot2", quietly = TRUE) quarto_available <- nzchar(Sys.which("quarto")) ``` ```{r setup} library(boinet) library(dplyr) # Load additional packages for reporting if (requireNamespace("gt", quietly = TRUE)) library(gt) if (requireNamespace("ggplot2", quietly = TRUE)) library(ggplot2) ``` # Overview This vignette demonstrates how to create complete, reproducible clinical trial reports using the boinet package with Quarto. The workflow covers everything from simulation to final report generation suitable for regulatory submissions, academic publications, and internal documentation. ```{r availability-check, echo=FALSE, results='asis'} if (!gt_available) { cat("**Note:** Some features require the gt package. Install with: `install.packages('gt')`\n\n") } if (!ggplot2_available) { cat("**Note:** Visualization features require ggplot2. Install with: `install.packages('ggplot2')`\n\n") } if (!quarto_available) { cat("**Note:** Full Quarto features require Quarto CLI. Install from: https://quarto.org/docs/get-started/\n\n") } ``` ## Helper Functions for Reports ```{r helper-functions} # Extract operating characteristics data 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 ) } # Create summary statistics create_summary_stats <- function(boinet_result) { oc_data <- extract_oc_data(boinet_result) optimal_dose <- oc_data$dose_level[which.max(oc_data$selection_prob)] list( optimal_dose = optimal_dose, max_selection_prob = max(oc_data$selection_prob), early_stop_rate = as.numeric(boinet_result$prop.stop), avg_duration = as.numeric(boinet_result$duration), total_patients = sum(oc_data$n_patients), design_type = class(boinet_result)[1] ) } # Create formatted text summaries create_text_summary <- function(boinet_result) { stats <- create_summary_stats(boinet_result) sprintf( "The %s design selected dose level %s in %.1f%% of trials, with an average trial duration of %.0f days and early stopping rate of %.1f%%.", toupper(gsub("\\.", "-", stats$design_type)), stats$optimal_dose, stats$max_selection_prob, stats$avg_duration, stats$early_stop_rate ) } # Create design parameters table create_design_table <- function(result) { design_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)", "Toxicity Assessment Window (days)", "Efficacy Assessment Window (days)", "Accrual Rate (days)"), Value = c(result$phi, result$delta, result$lambda1, result$lambda2, result$eta1, result$prop.stop, result$duration, result$tau.T, result$tau.E, result$accrual), stringsAsFactors = FALSE ) # Format values design_params$Value <- round(as.numeric(design_params$Value), 3) if (gt_available) { design_params %>% gt() %>% tab_header( title = "TITE-BOIN-ET Design Parameters", subtitle = paste("Based on", result$n.sim, "simulated trials") ) %>% fmt_number(columns = "Value", decimals = 3) %>% cols_align(align = "left", columns = "Parameter") %>% cols_align(align = "center", columns = "Value") } else { # Fallback to basic table print(design_params) } } # Create operating characteristics table create_oc_table <- function(result) { dose_levels <- names(result$n.patient) oc_data <- data.frame( `Dose Level` = dose_levels, `True Toxicity Probability` = round(as.numeric(result$toxprob), 3), `True Efficacy Probability` = round(as.numeric(result$effprob), 3), `Average N Treated` = round(as.numeric(result$n.patient), 1), `Selection Probability (%)` = round(as.numeric(result$prop.select), 1), check.names = FALSE ) if (gt_available) { oc_data %>% gt() %>% tab_header( title = "Operating Characteristics", subtitle = "TITE-BOIN-ET Design Simulation Results" ) %>% fmt_number(columns = c("True Toxicity Probability", "True Efficacy Probability"), decimals = 3) %>% fmt_number(columns = "Average N Treated", decimals = 1) %>% fmt_number(columns = "Selection Probability (%)", decimals = 1) %>% cols_align(align = "center", columns = everything()) %>% cols_align(align = "left", columns = "Dose Level") %>% tab_style( style = cell_fill(color = "lightblue"), locations = cells_body( rows = `Selection Probability (%)` == max(`Selection Probability (%)`) ) ) } else { # Fallback to basic table print(oc_data) } } ``` ## Mock Data for Demonstration ```{r mock-data} # Create mock result for demonstration boinet_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(boinet_result) <- "tite.boinet" ``` ## Complete Quarto Document Template Here's a complete Quarto document template for BOIN-ET analysis that you can save as a separate `.qmd` file: ### Basic Template Structure ```yaml --- title: "BOIN-ET Clinical Trial Design Analysis" subtitle: "Protocol ABC-2024-001: Novel Kinase Inhibitor" author: "Biostatistics Team" date: "`r Sys.Date()`" format: html: theme: cosmo toc: true toc-depth: 3 code-fold: true fig-width: 8 fig-height: 6 pdf: toc: true number-sections: true fig-width: 7 fig-height: 5 docx: toc: true fig-width: 6.5 fig-height: 4.5 execute: echo: false warning: false message: false --- ``` ### Example Report Sections The following demonstrates what would go in your actual Quarto report: ```{r demo-design-table, eval=gt_available} # Design specifications table create_design_table(boinet_result) ``` ```{r demo-oc-table, eval=gt_available} # Operating characteristics table create_oc_table(boinet_result) ``` ```{r demo-analysis} # Extract key statistics for inline reporting dose_levels <- names(boinet_result$n.patient) selection_probs <- as.numeric(boinet_result$prop.select) best_dose_idx <- which.max(selection_probs) best_dose <- dose_levels[best_dose_idx] max_selection <- max(selection_probs) avg_duration <- as.numeric(boinet_result$duration) early_stop <- as.numeric(boinet_result$prop.stop) cat("Key findings:\n") cat("- Optimal dose level:", best_dose, "\n") cat("- Selection probability:", round(max_selection, 1), "%\n") cat("- Average trial duration:", round(avg_duration, 0), "days\n") cat("- Early stopping rate:", round(early_stop, 1), "%\n") ``` ## Dose Selection Visualization ```{r demo-selection-plot, eval=ggplot2_available, fig.cap="Dose selection probabilities"} if (ggplot2_available) { # Create data frame for plotting plot_data <- data.frame( dose_level = names(boinet_result$n.patient), selection_prob = as.numeric(boinet_result$prop.select) ) plot_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) + labs( x = "Dose Level", y = "Selection Probability (%)", title = "TITE-BOIN-ET Dose Selection Performance" ) + theme_minimal() } else { cat("ggplot2 package not available for visualization.\n") } ``` ## Risk-Benefit Analysis ```{r demo-risk-benefit, eval=ggplot2_available, fig.cap="Efficacy-toxicity profile"} if (ggplot2_available) { # Create risk-benefit plot data rb_data <- data.frame( dose_level = names(boinet_result$n.patient), toxicity_prob = as.numeric(boinet_result$toxprob), efficacy_prob = as.numeric(boinet_result$effprob), selection_prob = as.numeric(boinet_result$prop.select) ) rb_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.5) + scale_size_continuous(name = "Selection\nProbability (%)", range = c(3, 12)) + labs( x = "True Toxicity Probability", y = "True Efficacy Probability", title = "Risk-Benefit Profile" ) + theme_minimal() } else { cat("ggplot2 package not available for risk-benefit visualization.\n") } ``` ## Parameterized Reports Quarto supports parameterized reports where you can pass values to customize the analysis. ### Setting Up Parameters **Step 1:** Add parameters to your YAML header: ``` --- title: "BOIN-ET Analysis Report" params: target_tox: 0.30 target_eff: 0.60 protocol_id: "ABC-001" compound_name: "XYZ-123" --- ``` **Step 2:** Use parameters in your analysis code (in a real parameterized document): ``` # Access parameters like this: # target_toxicity <- params$target_tox # protocol_name <- params$protocol_id ``` **Step 3:** Render with custom parameters: ``` # quarto::quarto_render( # "your_template.qmd", # execute_params = list( # target_tox = 0.25, # target_eff = 0.65, # protocol_id = "XYZ-002", # compound_name = "New Drug" # ) # ) ``` ```{r parameterized-demo} # Demonstration of the concept using mock parameters mock_params <- list( target_tox = 0.30, target_eff = 0.60, protocol_id = "ABC-001", compound_name = "XYZ-123" ) cat("Parameterized report demonstration:\n") cat("Protocol:", mock_params$protocol_id, "\n") cat("Compound:", mock_params$compound_name, "\n") cat("Target toxicity:", mock_params$target_tox, "\n") cat("Target efficacy:", mock_params$target_eff, "\n") # This shows how you could customize analysis based on parameters cat("\nCustomized analysis settings:\n") if (mock_params$target_tox < 0.25) { cat("- Conservative toxicity approach\n") } else if (mock_params$target_tox > 0.35) { cat("- Aggressive toxicity approach\n") } else { cat("- Standard toxicity approach\n") } ``` ## Batch Report Generation ```{r batch-demo} # Demonstrate the concept with executable code scenarios <- list( conservative = list(phi = 0.25, delta = 0.50, name = "Conservative"), standard = list(phi = 0.30, delta = 0.60, name = "Standard"), aggressive = list(phi = 0.35, delta = 0.70, name = "Aggressive") ) # Function to create scenario-specific summaries create_scenario_summary <- function(scenario) { sprintf( "Scenario: %s (φ=%.2f, δ=%.2f)", scenario$name, scenario$phi, scenario$delta ) } # Generate summaries for all scenarios scenario_summaries <- lapply(scenarios, create_scenario_summary) cat("Available scenarios:\n") for(summary in scenario_summaries) { cat("-", summary, "\n") } ``` For batch processing, you would typically: 1. Create a parameterized template 2. Define scenarios with different parameters 3. Use `quarto::quarto_render()` with different `execute_params` for each scenario ## Conditional Content Show different content based on results: ```{r conditional-content} # Extract results for conditional logic oc_data <- extract_oc_data(boinet_result) max_selection_prob <- max(oc_data$selection_prob) optimal_dose <- oc_data$dose_level[which.max(oc_data$selection_prob)] # Conditional content based on results if (max_selection_prob > 40) { recommendation <- "Strong evidence for optimal dose identification" confidence_level <- "High" } else if (max_selection_prob > 25) { recommendation <- "Moderate evidence for dose selection" confidence_level <- "Moderate" } else { recommendation <- "Weak evidence - consider design modifications" confidence_level <- "Low" } cat("Recommendation Confidence:", confidence_level, "\n") cat("Analysis Conclusion:", recommendation, "\n") cat(sprintf("The optimal dose level %s was selected in %.1f%% of simulations.\n", optimal_dose, max_selection_prob)) ``` ## Best Practices for Quarto Reports ### 1. Document Structure Use meaningful YAML headers with appropriate output formats for your audience. ### 2. Chunk Management - Use descriptive chunk labels - Set `cache: true` for expensive computations - Use `echo: false` to hide code in final reports - Use `eval: false` for demonstration code ### 3. Reproducible Workflows ```{r reproducible-workflow-demo} # Include session information cat("R version:", R.version.string, "\n") cat("boinet version:", as.character(packageVersion("boinet")), "\n") if (gt_available) { cat("gt version:", as.character(packageVersion("gt")), "\n") } if (ggplot2_available) { cat("ggplot2 version:", as.character(packageVersion("ggplot2")), "\n") } # Document analysis timestamp cat("Analysis completed:", format(Sys.time(), "%Y-%m-%d %H:%M:%S"), "\n") ``` ### 4. Output Formatting Consider your target audience and output format when designing tables and figures. ## Template Library ### Clinical Protocol Report Template ```{r clinical-template} # Function to create standardized clinical report content create_clinical_report_content <- function(boinet_result, protocol_info) { # Extract key information oc_data <- extract_oc_data(boinet_result) stats <- create_summary_stats(boinet_result) # Create content structure content <- list( title = sprintf("Protocol %s: %s Analysis", protocol_info$id, protocol_info$compound), summary = create_text_summary(boinet_result), key_findings = list( optimal_dose = stats$optimal_dose, selection_prob = round(stats$max_selection_prob, 1), duration = round(stats$avg_duration, 0), early_stop = round(stats$early_stop_rate, 1) ), oc_data = oc_data ) return(content) } # Example usage protocol_info <- list(id = "XYZ-2024-001", compound = "Novel Kinase Inhibitor") demo_result <- boinet_result report_content <- create_clinical_report_content(demo_result, protocol_info) cat("Report title:", report_content$title, "\n") cat("Summary:", report_content$summary, "\n") ``` ### Regulatory Submission Template ```{r regulatory-template} # Function to create regulatory-style formatting create_regulatory_content <- function(boinet_result, submission_info) { stats <- create_summary_stats(boinet_result) # Regulatory-style summary reg_summary <- sprintf( "Study %s evaluated %d dose levels using a %s design. The recommended Phase II dose is %s, selected in %s%% of %d simulated trials.", submission_info$study_id, length(names(boinet_result$n.patient)), toupper(gsub("\\.", "-", stats$design_type)), stats$optimal_dose, format(stats$max_selection_prob, digits = 3), boinet_result$n.sim ) return(list( summary = reg_summary, table_title = sprintf("Table %s: Operating Characteristics", submission_info$table_number), stats = stats )) } # Example usage submission_info <- list(study_id = "ABC-123-001", table_number = "14.2.1") demo_result <- boinet_result reg_content <- create_regulatory_content(demo_result, submission_info) cat("Regulatory summary:", reg_content$summary, "\n") ``` ## Troubleshooting Common Issues ### Package Availability Issues ```{r troubleshooting} # Check required packages required_packages <- c("boinet", "dplyr") optional_packages <- c("gt", "ggplot2", "knitr", "rmarkdown") check_package_status <- function(packages, required = TRUE) { status <- sapply(packages, function(pkg) { requireNamespace(pkg, quietly = TRUE) }) missing <- names(status)[!status] if (length(missing) > 0) { cat(ifelse(required, "Missing required", "Missing optional"), "packages:", paste(missing, collapse = ", "), "\n") cat("Install with: install.packages(c('", paste(missing, collapse = "', '"), "'))\n") } else { cat("All", ifelse(required, "required", "optional"), "packages available\n") } } check_package_status(required_packages, TRUE) check_package_status(optional_packages, FALSE) # Check Quarto availability if (quarto_available) { cat("✓ Quarto CLI available\n") } else { cat("⚠ Quarto CLI not found. Install from: https://quarto.org\n") } ``` ### Memory and Performance Issues ```{r performance-tips} # Tips for handling large simulations check_result_size <- function(result) { size_mb <- object.size(result) / (1024^2) cat(sprintf("Result object size: %.1f MB\n", size_mb)) if (size_mb > 50) { cat("⚠ Large result object detected. Consider:\n") cat(" - Using cache: true in chunks\n") cat(" - Reducing n.sim for development\n") cat(" - Extracting only needed data\n") } else { cat("✓ Result size is manageable\n") } } # Use the demo result we created earlier demo_result <- boinet_result check_result_size(demo_result) # Memory-efficient data extraction extract_minimal_data <- function(result) { list( oc_summary = data.frame( dose = names(result$n.patient), selection_prob = as.numeric(result$prop.select), stringsAsFactors = FALSE ), key_stats = list( optimal_dose = names(result$prop.select)[which.max(result$prop.select)], duration = result$duration, early_stop = result$prop.stop ) ) } minimal_data <- extract_minimal_data(demo_result) cat("Minimal data extracted successfully\n") ``` ## Conclusion The boinet package provides comprehensive support for Quarto-based reporting workflows, enabling: - **Reproducible analysis** with parameterized reports - **Professional formatting** suitable for various audiences - **Flexible output** in multiple formats (HTML, PDF, Word) - **Interactive elements** for enhanced user experience - **Standardized templates** for consistent reporting This workflow ensures that BOIN-ET analysis results can be effectively communicated to clinical teams, regulatory agencies, and academic audiences while maintaining full reproducibility and professional presentation standards. For basic result formatting, see the `result-formatting` vignette. For publication-ready tables, see the `gt-integration` vignette.