--- title: "Case Study: Breast Cancer Diagnosis" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Case Study: Breast Cancer Diagnosis} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 6, fig.height = 4 ) ``` ```{r setup} library(triageR) library(mlbench) ``` ## Overview This vignette applies triageR to the **Wisconsin Breast Cancer** dataset, a real-world dataset of tumor characteristics used to predict whether a biopsy is benign or malignant. It complements the introductory vignette by demonstrating a three-engine comparison, and by illustrating an important nuance in automated pipeline review: the difference between genuine biological signal and true data leakage. ## 1. Load and prepare data The dataset stores its predictors as factors representing ordinal scores (1-10). We convert these to numeric, and note that patient IDs contain duplicates (repeat biopsies), both are realistic data quality issues that `triageR` is designed to surface rather than hide. ```{r} data(BreastCancer) bc <- BreastCancer score_cols <- c("Cl.thickness", "Cell.size", "Cell.shape", "Marg.adhesion", "Epith.c.size", "Bare.nuclei", "Bl.cromatin", "Normal.nucleoli", "Mitoses") bc[score_cols] <- lapply(bc[score_cols], function(x) as.numeric(as.character(x))) bc_loaded <- tr_load_clinical(bc, id_col = "Id") ``` ```{r} tr_check_missing(bc_loaded) ``` ## 2. Impute missing data ```{r, message = FALSE, results = "hide"} bc_for_impute <- bc_loaded[, setdiff(names(bc_loaded), "Id")] bc_imputed <- tr_impute(bc_for_impute, method = "mice", m = 5) ``` ## 3. Fit and compare three engines ```{r} set.seed(7) n <- nrow(bc_imputed) train_idx <- sample(seq_len(n), size = floor(0.7 * n)) bc_train <- bc_imputed[train_idx, ] bc_test <- bc_imputed[-train_idx, ] bc_model_glm <- tr_fit(bc_train, outcome = "Class", engine = "logistic_reg") bc_model_rf <- tr_fit(bc_train, outcome = "Class", engine = "random_forest") bc_model_xgb <- tr_fit(bc_train, outcome = "Class", engine = "boost_tree") ``` ```{r} bc_val_glm <- tr_validate(bc_model_glm, newdata = bc_test) bc_val_rf <- tr_validate(bc_model_rf, newdata = bc_test) bc_val_xgb <- tr_validate(bc_model_xgb, newdata = bc_test) ``` ```{r} bc_comparison <- dplyr::bind_rows( dplyr::mutate(bc_val_glm$metrics, engine = "logistic_reg"), dplyr::mutate(bc_val_rf$metrics, engine = "random_forest"), dplyr::mutate(bc_val_xgb$metrics, engine = "boost_tree") ) tidyr::pivot_wider(bc_comparison, names_from = .metric, values_from = .estimate) ``` All three engines perform very well on this dataset (AUC > 0.98), reflecting the fact that tumor morphology is highly predictive of malignancy. Notably, logistic regression performs competitively with the more flexible tree-based methods, a reminder that added model complexity does not always yield better clinical performance. ```{r} bc_val_glm$roc_plot ``` ## 4. Explainability ```{r} tr_explain(bc_model_glm, method = "permutation") ``` Cell thickness, bare nuclei, and bland chromatin emerge as the strongest predictors which is consistent with established cytological indicators of malignancy. ## 5. Automated pipeline review: interpreting the leakage flag ```{r} bc_review <- tr_agent_review(bc_train, bc_model_glm, use_agent = FALSE) ``` `tr_agent_review()` flags `Cell.shape` as a possible leakage concern, since it alone achieves a high AUC predicting malignancy. **This is an important nuance to understand**: the check cannot distinguish between genuine, strong biological signal and true data leakage (e.g. a variable derived from the outcome itself). In this case, cell shape is a well-established cytological indicator of malignancy, not a leakage artifact. This illustrates the intended role of `tr_agent_review()`: it flags statistically unusual patterns for **human review**, not automatic disqualification. A researcher with domain knowledge can correctly interpret this flag as expected biological signal rather than an error. ## 6. Sensitivity analysis ```{r} bc_sensitivity <- tr_sensitivity(bc_train, bc_model_glm) ``` Performance remains stable across the complete-case and outlier-exclusion scenarios, indicating the model is not overly sensitive to a small number of extreme values or missing-data handling. ## Summary This case study demonstrated triageR's three-engine comparison workflow on a second real-world clinical dataset, and highlighted an important interpretive nuance in automated pipeline review: statistical flags require clinical context to interpret correctly. triageR is designed to surface these patterns for expert review, not to make final judgments autonomously.