diff --git a/.github/workflows/sync-to-org.yml b/.github/workflows/sync-to-org.yml new file mode 100644 index 0000000..9feb017 --- /dev/null +++ b/.github/workflows/sync-to-org.yml @@ -0,0 +1,67 @@ +name: "Sync main & tags to org (SSH, prune)" + +on: + push: + branches: + - main # only when main changes + tags: + - "*" # and on any tag push + release: + types: [published] + workflow_dispatch: + +jobs: + sync: + # only from the source repo (so the fork doesn't need secrets) + if: github.repository == 'yqkiuo/BreastSubtypeR' + runs-on: ubuntu-latest + + steps: + - name: Checkout source + uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Ensure secret exists + run: | + if [ -z "${{ secrets.MIRROR_DEPLOY_KEY }}" ]; then + echo "::error:: Secret MIRROR_DEPLOY_KEY is missing in yqkiuo/BreastSubtypeR." + exit 1 + fi + + - name: Start ssh-agent & add deploy key + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.MIRROR_DEPLOY_KEY }} + + - name: Trust github.com host key + run: | + mkdir -p ~/.ssh + ssh-keyscan -t rsa,ecdsa,ed25519 github.com >> ~/.ssh/known_hosts + + - name: Configure git identity + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + + - name: Add mirror remote (org fork) + run: | + git remote add mirror git@github.com:JohanHartmanGroupBioteam/BreastSubtypeR.git + git remote -v + + - name: Fetch origin & mirror (for safe lease) + run: | + set -e + git fetch --tags --prune origin + git fetch --prune origin +refs/heads/*:refs/remotes/origin/* + git fetch --tags --prune mirror + git fetch --prune mirror +refs/heads/*:refs/remotes/mirror/* + + - name: Push main & tags to mirror (prune deleted tags) + run: | + set -e + # Push main with a safe force (only if mirror hasn't moved unexpectedly) + git push --force-with-lease mirror refs/remotes/origin/main:refs/heads/main + # Push all tags, pruning tag deletions + git push --prune --tags mirror diff --git a/DESCRIPTION b/DESCRIPTION index 62371b7..ed0c668 100755 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -10,8 +10,8 @@ Description: BreastSubtypeR provides an assumption-aware, multi-method framework (iBreastSubtypeR) is included for interactive analyses and to support users without programming experience. Encoding: UTF-8 -Version: 1.1.4 -biocViews: RNASeq, Software, GeneExpression, Classification +Version: 1.1.5 +biocViews: RNASeq, Software, GeneExpression, Classification, Preprocessing, Visualization Authors@R: c( person(given = "Qiao", family = "Yang", role = c("aut", "cre"), @@ -59,4 +59,4 @@ License: GPL-3 VignetteBuilder: knitr Roxygen: list(markdown = TRUE, roclets = c("rd", "namespace", "collate")) LazyData: FALSE -RoxygenNote: 7.3.2 +RoxygenNote: 7.3.3 diff --git a/NAMESPACE b/NAMESPACE index 1652a26..339fd94 100755 --- a/NAMESPACE +++ b/NAMESPACE @@ -15,13 +15,10 @@ export(Vis_boxplot) export(Vis_heatmap) export(Vis_pie) export(iBreastSubtypeR) -export(runShinyBreastSubtypeR) import(Biobase) import(ComplexHeatmap) import(RColorBrewer) import(circlize) -import(e1071) -import(ggplot2) import(ggrepel) import(grid) import(impute) @@ -35,7 +32,30 @@ importFrom(SummarizedExperiment,colData) importFrom(SummarizedExperiment,rowData) importFrom(data.table,data.table) importFrom(data.table,set) +importFrom(dplyr,across) +importFrom(dplyr,everything) +importFrom(dplyr,mutate) importFrom(dplyr,select) +importFrom(e1071,naiveBayes) +importFrom(ggplot2,aes) +importFrom(ggplot2,coord_polar) +importFrom(ggplot2,element_text) +importFrom(ggplot2,geom_bar) +importFrom(ggplot2,geom_boxplot) +importFrom(ggplot2,geom_hline) +importFrom(ggplot2,geom_line) +importFrom(ggplot2,geom_point) +importFrom(ggplot2,geom_text) +importFrom(ggplot2,geom_vline) +importFrom(ggplot2,ggplot) +importFrom(ggplot2,labs) +importFrom(ggplot2,scale_color_manual) +importFrom(ggplot2,scale_fill_manual) +importFrom(ggplot2,scale_x_continuous) +importFrom(ggplot2,theme) +importFrom(ggplot2,theme_classic) +importFrom(ggplot2,theme_minimal) +importFrom(ggplot2,theme_void) importFrom(grDevices,dev.off) importFrom(grDevices,pdf) importFrom(graphics,barplot) @@ -48,6 +68,7 @@ importFrom(stats,cor) importFrom(stats,cor.test) importFrom(stats,dist) importFrom(stats,median) +importFrom(stats,na.omit) importFrom(stats,prcomp) importFrom(stats,quantile) importFrom(tidyselect,everything) diff --git a/NEWS.md b/NEWS.md index 2c22233..a6a97e4 100755 --- a/NEWS.md +++ b/NEWS.md @@ -1,9 +1,9 @@ -# BreastSubtypeR 1.1.3 +# BreastSubtypeR 1.1.5 ## Highlights - Paper published in *NAR Genomics and Bioinformatics* (2025), **Editor’s Choice** (DOI: 10.1093/nargab/lqaf131). -- **New:** Support for raw RNA-seq counts (requires gene lengths). -- Major **iBreastSubtypeR** refresh: cleaner UX, smarter AUTO guidance, consistent exports. +- Support for raw RNA-seq counts (requires gene lengths). +- **iBreastSubtypeR** refresh: cleaner UX, smarter AUTO guidance, consistent exports. - Refined, data-driven thresholds for ER/HER2 skew detection in AUTO. - Broader input-validation across the subtyping pipeline. @@ -41,4 +41,4 @@ ## Upgrade Notes - Raw RNA-seq counts are supported **from v1.1.3 onward** (requires gene lengths). - If you previously parsed `BS` / `BS.Subtype`, switch to **`Call_5class` / `Call_4class`**. -- Package API unchanged (the `Subtype` argument remains). +- Package API unchanged. diff --git a/R/BreastSubtypeR_methods.R b/R/BreastSubtypeR_methods.R index e2208ff..bf77be0 100755 --- a/R/BreastSubtypeR_methods.R +++ b/R/BreastSubtypeR_methods.R @@ -1,6 +1,6 @@ #' @import stringr -#' @import e1071 #' @import Biobase +#' @importFrom e1071 naiveBayes #' @importFrom SummarizedExperiment SummarizedExperiment #' @importFrom SummarizedExperiment colData #' @importFrom SummarizedExperiment rowData @@ -12,12 +12,13 @@ #' @importFrom utils data installed.packages read.delim write.table #' @importFrom graphics barplot mtext par #' @importFrom grDevices dev.off pdf -#' @importFrom stats prcomp cor cor.test dist quantile median +#' @importFrom stats prcomp cor cor.test dist quantile median na.omit #' @importFrom methods is #' NULL -#' BreastSubtypeR: A Unified R/Bioconductor Package for Intrinsic Molecular Subtyping in Breast Cancer Research +#' BreastSubtypeR: A Unified R/Bioconductor Package +#' for Intrinsic Molecular Subtyping in Breast Cancer Research #' #' #' @name BreastSubtypeR @@ -77,7 +78,8 @@ NULL #' #' @param se_obj A `SummarizedExperiment` object containing: #' - **Assay data**: -#' - If `RawCounts = FALSE`: `assay()` must contain log2-normalized expression (e.g., pre-normalized microarray/nCounter, or log2(FPKM+1) RNAseq). +#' - If `RawCounts = FALSE`: `assay()` must contain log2-normalized expression +#' (e.g., pre-normalized microarray/nCounter, or log2(FPKM+1) RNAseq). #' - If `RawCounts = TRUE`: `assay()` contains raw RNA-seq counts (see `RawCounts`). #' - **Row metadata** (required): #' - `"probe"`: feature identifiers (e.g., gene symbols or probe IDs) @@ -87,7 +89,8 @@ NULL #' - **Column metadata** (optional): sample-level metadata in `colData()`. #' #' @param RawCounts Logical. If `TRUE`, indicates that `assay()` holds raw RNA-seq counts. -#' In this case, `rowData()` must also provide gene lengths (column `"Length"`, in base pairs), used for: +#' In this case, `rowData()` must also provide gene lengths +#' (column `"Length"`, in base pairs), used for: #' - NC-based methods: log2-CPM (upper-quartile normalization). #' - SSP-based methods: linear FPKM (not log-transformed). #' @@ -104,8 +107,10 @@ NULL #' #' @return A named list with: #' \describe{ -#' \item{se_NC}{`SummarizedExperiment` holding log2-transformed data prepared for NC-based methods (assay name: `counts`).} -#' \item{se_SSP}{`SummarizedExperiment` holding linear-scale data prepared for SSP-based methods (assay name: `counts`).} +#' \item{se_NC}{`SummarizedExperiment` holding log2-transformed data prepared for NC-based methods +#' (assay name: `counts`).} +#' \item{se_SSP}{`SummarizedExperiment` holding linear-scale data prepared for SSP-based methods +#' (assay name: `counts`).} #' } #' #' @references @@ -123,38 +128,26 @@ NULL #' BreastSubtypeR automatically applies method-specific preprocessing. #' #' @examples -#' \donttest{ -#' library(BreastSubtypeR) -#' -#' # Using raw RNA-seq counts (with gene lengths) -#' se_obj <- SummarizedExperiment( -#' assays = list(counts = raw_counts_mat), -#' rowData = DataFrame( -#' probe = rownames(raw_counts_mat), -#' ENTREZID = entrez_ids, -#' Length = gene_lengths -#' ) -#' ) -#' res <- Mapping(se_obj, RawCounts = TRUE) -#' -#' # Using pre-normalized log2(FPKM+1) -#' se_obj_fpkm <- SummarizedExperiment( -#' assays = list(expr = log2_fpkm_mat), -#' rowData = DataFrame( -#' probe = rownames(log2_fpkm_mat), -#' ENTREZID = entrez_ids -#' ) -#' ) -#' res <- Mapping(se_obj_fpkm, RawCounts = FALSE) +#' if (requireNamespace("SummarizedExperiment", quietly = TRUE)) { +#' # Using example raw RNA-seq counts (with gene lengths) +#' data("TCGABRCAobj") +#' se_obj_counts <- TCGABRCAobj$se_obj[, 1:3] # tiny subset to keep checks fast +#' res <- Mapping(se_obj_counts, RawCounts = TRUE) +#' +#' # Using example pre-normalized log2(FPKM+0.1) +#' data("OSLO2EMIT0obj") +#' se_obj_fpkm <- OSLO2EMIT0obj$se_obj[, 1:3] # tiny subset to keep checks fast +#' res <- Mapping(se_obj_fpkm, RawCounts = FALSE) #' } +#' #' @export Mapping <- function( - se_obj, - RawCounts = FALSE, - method = c("max", "mean", "median", "iqr", "stdev"), - impute = TRUE, - verbose = TRUE) { + se_obj, + RawCounts = FALSE, + method = c("max", "mean", "median", "iqr", "stdev"), + impute = TRUE, + verbose = TRUE) { method <- match.arg(method) arguments <- rlang::dots_list( @@ -198,8 +191,8 @@ Mapping <- function( #' (Luminal A, Luminal B, HER2-enriched, Basal-like, and optionally Normal-like). #' #' @param se_obj A `SummarizedExperiment` object containing: -#' - **Assay data**: A log-transformed, normalized gene expression matrix with genes (Gene Symbols) as rows -#' and samples as columns. +#' - **Assay data**: A log-transformed, normalized gene expression matrix with genes +#' (Gene Symbols) as rows and samples as columns. #' - **Column metadata** (`colData`): Optional sample- or patient-level #' information. #' @@ -231,8 +224,8 @@ Mapping <- function( #' #' @param hasClinical Logical. If `TRUE`, incorporates clinical variables from #' `colData(se_obj)`. Required columns: -#' - `"TSIZE"`: Tumor size (`0` = ≤2 cm; `1` = >2 cm). -#' - `"NODE"`: Lymph node status (`0` = negative; ≥1 = positive). Must be numeric. +#' - "TSIZE": Tumor size (0 = \eqn{\le 2}{<= 2} cm; 1 = \eqn{> 2}{> 2} cm). +#' - "NODE": Lymph node status (0 = negative; \eqn{\ge 1}{>= 1} = positive). Must be numeric. #' #' @return A list containing PAM50 intrinsic subtype calls using the Parker #' classifier and selected calibration strategy. @@ -262,13 +255,13 @@ Mapping <- function( #' @export BS_parker <- function( - se_obj, - calibration = "None", - internal = NA, - external = NA, - medians = NA, - Subtype = FALSE, - hasClinical = FALSE) { + se_obj, + calibration = "None", + internal = NA, + external = NA, + medians = NA, + Subtype = FALSE, + hasClinical = FALSE) { # Check if input is a SummarizedExperiment object if (!inherits(se_obj, "SummarizedExperiment")) { stop("Input must be a SummarizedExperiment object.") @@ -359,8 +352,8 @@ BS_parker <- function( #' #' @param hasClinical Logical. If `TRUE`, incorporates additional clinical #' variables from `colData(se_obj)`. Required columns: -#' - `"TSIZE"`: Tumor size (`0` = ≤2 cm; `1` = >2 cm). -#' - `"NODE"`: Lymph node status (`0` = negative; ≥1 = positive). Must be numeric. +#' - "TSIZE": Tumor size (0 = \eqn{\le 2}{<= 2} cm; 1 = \eqn{> 2}{> 2} cm). +#' - "NODE": Lymph node status (0 = negative; \eqn{\ge 1}{>= 1} = positive). Must be numeric. #' #' @param seed Integer. Random seed for reproducibility of ER-balancing. #' @@ -385,9 +378,9 @@ BS_parker <- function( BS_cIHC <- function(se_obj, - Subtype = FALSE, - hasClinical = FALSE, - seed = 118) { + Subtype = FALSE, + hasClinical = FALSE, + seed = 118) { # Check if input is a SummarizedExperiment object if (!inherits(se_obj, "SummarizedExperiment")) { stop("Input must be a SummarizedExperiment object.") @@ -406,7 +399,8 @@ BS_cIHC <- function(se_obj, if (hasClinical) { req <- c("TSIZE", "NODE") miss <- setdiff(req, colnames(pheno)) - if (length(miss)) stop("When hasClinical = TRUE, colData(se_obj) must include: TSIZE and NODE. Missing: ", paste(miss, collapse = ", "), ".") + if (length(miss)) stop("When hasClinical = TRUE, colData(se_obj) must include: TSIZE and NODE. + Missing: ", paste(miss, collapse = ", "), ".") if (!is.numeric(pheno$NODE)) stop("colData(se_obj)$NODE must be numeric.") } @@ -444,7 +438,7 @@ BS_cIHC <- function(se_obj, #' - `"ER"`: Estrogen receptor status, coded as `"ER+"` or `"ER-"`. #' #' @param iteration Integer. Number of iterations for the ER-balancing procedure. -#' Default: `100`. +#' Default: 100. #' #' @param ratio Numeric. Target ER+/ER– ratio for balancing. Options: #' - `1:1`: Equal balancing. @@ -455,8 +449,8 @@ BS_cIHC <- function(se_obj, #' #' @param hasClinical Logical. If `TRUE`, incorporates additional clinical #' variables from `colData(se_obj)`. Required columns: -#' - `"TSIZE"`: Tumor size (`0` = ≤2 cm; `1` = >2 cm). -#' - `"NODE"`: Lymph node status (`0` = negative; ≥1 = positive). Must be numeric. +#' - "TSIZE": Tumor size (0 = \eqn{\le 2}{<= 2} cm; 1 = \eqn{> 2}{> 2} cm). +#' - "NODE": Lymph node status (0 = negative; \eqn{\ge 1}{>= 1} = positive). Must be numeric. #' #' @param seed Integer. Random seed for reproducibility. #' @@ -483,11 +477,11 @@ BS_cIHC <- function(se_obj, #' @export BS_cIHC.itr <- function(se_obj, - iteration = 100, - ratio = 54 / 64, - Subtype = FALSE, - hasClinical = FALSE, - seed = 118) { + iteration = 100, + ratio = 54 / 64, + Subtype = FALSE, + hasClinical = FALSE, + seed = 118) { # Check if input is a SummarizedExperiment object if (!inherits(se_obj, "SummarizedExperiment")) { stop("Input must be a SummarizedExperiment object.") @@ -506,7 +500,8 @@ BS_cIHC.itr <- function(se_obj, if (hasClinical) { req <- c("TSIZE", "NODE") miss <- setdiff(req, colnames(pheno)) - if (length(miss)) stop("When hasClinical = TRUE, colData(se_obj) must include: TSIZE and NODE. Missing: ", paste(miss, collapse = ", "), ".") + if (length(miss)) stop("When hasClinical = TRUE, colData(se_obj) must include: TSIZE and NODE. + Missing: ", paste(miss, collapse = ", "), ".") if (!is.numeric(pheno$NODE)) stop("colData(se_obj)$NODE must be numeric.") } @@ -549,8 +544,8 @@ BS_cIHC.itr <- function(se_obj, #' #' @param hasClinical Logical. If `TRUE`, incorporates additional clinical #' variables from `colData(se_obj)`. Required columns: -#' - `"TSIZE"`: Tumor size (`0` = ≤2 cm; `1` = >2 cm). -#' - `"NODE"`: Lymph node status (`0` = negative; ≥1 = positive). Must be numeric. +#' - "TSIZE": Tumor size (0 = \eqn{\le 2}{<= 2} cm; 1 = \eqn{> 2}{> 2} cm). +#' - "NODE": Lymph node status (0 = negative; \eqn{\ge 1}{>= 1} = positive). Must be numeric. #' #' @param seed Integer. Random seed for reproducibility. #' @@ -574,9 +569,9 @@ BS_cIHC.itr <- function(se_obj, #' @export BS_PCAPAM50 <- function(se_obj, - Subtype = FALSE, - hasClinical = FALSE, - seed = 118) { + Subtype = FALSE, + hasClinical = FALSE, + seed = 118) { # Check if input is a SummarizedExperiment object if (!inherits(se_obj, "SummarizedExperiment")) { stop("Input must be a SummarizedExperiment object.") @@ -595,7 +590,8 @@ BS_PCAPAM50 <- function(se_obj, if (hasClinical) { req <- c("TSIZE", "NODE") miss <- setdiff(req, colnames(pheno)) - if (length(miss)) stop("When hasClinical = TRUE, colData(se_obj) must include: TSIZE and NODE. Missing: ", paste(miss, collapse = ", "), ".") + if (length(miss)) stop("When hasClinical = TRUE, colData(se_obj) must include: TSIZE and NODE. + Missing: ", paste(miss, collapse = ", "), ".") if (!is.numeric(pheno$NODE)) stop("colData(se_obj)$NODE must be numeric.") } @@ -696,8 +692,8 @@ BS_PCAPAM50 <- function(se_obj, #' #' @param hasClinical Logical. If `TRUE`, incorporates additional clinical #' variables from `colData(se_obj)`. Required columns: -#' - `"TSIZE"`: Tumor size (`0` = ≤2 cm; `1` = >2 cm). -#' - `"NODE"`: Lymph node status (`0` = negative; ≥1 = positive). Must be numeric. +#' - "TSIZE": Tumor size (0 = \eqn{\le 2}{<= 2} cm; 1 = \eqn{> 2}{> 2} cm). +#' - "NODE": Lymph node status (0 = negative; \eqn{\ge 1}{>= 1} = positive). Must be numeric. #' #' @return A character vector of intrinsic subtype predictions assigned to each #' sample using the ssBC method. @@ -726,9 +722,9 @@ BS_PCAPAM50 <- function(se_obj, #' @export BS_ssBC <- function(se_obj, - s, - Subtype = FALSE, - hasClinical = FALSE) { + s, + Subtype = FALSE, + hasClinical = FALSE) { # Check that input is a SummarizedExperiment object if (!inherits(se_obj, "SummarizedExperiment")) { stop("Input must be a SummarizedExperiment object.") @@ -747,7 +743,8 @@ BS_ssBC <- function(se_obj, if (hasClinical) { req <- c("TSIZE", "NODE") miss <- setdiff(req, colnames(pheno)) - if (length(miss)) stop("When hasClinical = TRUE, colData(se_obj) must include: TSIZE and NODE. Missing: ", paste(miss, collapse = ", "), ".") + if (length(miss)) stop("When hasClinical = TRUE, colData(se_obj) must include: TSIZE and NODE. + Missing: ", paste(miss, collapse = ", "), ".") if (!is.numeric(pheno$NODE)) stop("colData(se_obj)$NODE must be numeric.") } @@ -762,7 +759,8 @@ BS_ssBC <- function(se_obj, missing_columns <- setdiff(required_columns, colnames(pheno)) if (length(missing_columns) > 0) { - stop(paste("The following required columns are missing from 'colData':", paste(missing_columns, collapse = ", "))) + stop(paste("The following required columns are missing from 'colData':", + paste(missing_columns, collapse = ", "))) } arguments <- rlang::dots_list( @@ -958,8 +956,8 @@ BS_sspbc <- function(se_obj, ssp.name = "ssp.pam50") { #' #' @param hasClinical Logical. If `TRUE`, incorporates clinical data from #' `colData(se_obj)`. Required columns: -#' - `"TSIZE"`: Tumor size (`0` = ≤2 cm; `1` = >2 cm). -#' - `"NODE"`: Lymph node status (`0` = negative; ≥1 = positive; must be numeric). +#' - "TSIZE": Tumor size (0 = \eqn{\le 2}{<= 2} cm; 1 = \eqn{> 2}{> 2} cm). +#' - "NODE": Lymph node status (0 = negative; \eqn{\ge 1}{>= 1} = positive). #' #' @return A list containing per-method subtype assignments for each sample. #' @@ -1014,10 +1012,10 @@ BS_sspbc <- function(se_obj, ssp.name = "ssp.pam50") { #' @export BS_Multi <- function( - data_input, - methods = "AUTO", - Subtype = FALSE, - hasClinical = FALSE) { + data_input, + methods = "AUTO", + Subtype = FALSE, + hasClinical = FALSE) { valid_methods <- c( "parker.original", "genefu.scale", "genefu.robust", "ssBC", "ssBC.v2", "cIHC", "cIHC.itr", "PCAPAM50", @@ -1044,7 +1042,8 @@ BS_Multi <- function( rownames(pheno) <- pheno$PatientID # Check ER and HER2 columns in pheno - if (!("ER" %in% colnames(pheno)) && any(methods %in% c("ssBC", "ssBC.v2", "cIHC", "cIHC.itr", "PCAPAM50"))) { + if (!("ER" %in% colnames(pheno)) && any(methods %in% + c("ssBC", "ssBC.v2", "cIHC", "cIHC.itr", "PCAPAM50"))) { stop("The 'ER' column is required for selected methods.") } if (!("HER2" %in% colnames(pheno)) && "ssBC.v2" %in% methods) { @@ -1141,7 +1140,7 @@ BS_Multi <- function( }, error = function(e) { # Error handling - warning("PCAPAM50 failed in this iteration. Error: ") + warning("PCAPAM50 failed in this iteration: ") return(NULL) # Return NULL or a dummy tibble with NAs } ) @@ -1183,7 +1182,7 @@ BS_Multi <- function( length(samples_ER.icd) < nrow(pheno)) { unprocessed_patients <- base::setdiff(pheno$PatientID, samples_ER.icd) - # Create NA-filled dataframe for unprocessed patients with matching structure + # Create NA-filled data frame for unprocessed patients with matching structure na_df <- data.frame( PatientID = unprocessed_patients, matrix( @@ -1235,7 +1234,7 @@ BS_Multi <- function( unprocessed_patients <- base::setdiff(pheno$PatientID, samples_ERHER2.icd) - # Create NA-filled dataframe for unprocessed patients with matching structure + # Create NA-filled data frame for unprocessed patients with matching structure na_df <- data.frame( PatientID = unprocessed_patients, matrix( @@ -1306,8 +1305,10 @@ BS_Multi <- function( names(results) <- methods # Prefer se_NC samples; fall back to se_SSP if needed - samples_NC <- if (!is.null(data_input$se_NC)) colnames(SummarizedExperiment::assay(data_input$se_NC)) else character(0) - samples_SSP <- if (!is.null(data_input$se_SSP)) colnames(SummarizedExperiment::assay(data_input$se_SSP)) else character(0) + samples_NC <- if (!is.null(data_input$se_NC)) + colnames(SummarizedExperiment::assay(data_input$se_NC)) else character(0) + samples_SSP <- if (!is.null(data_input$se_SSP)) + colnames(SummarizedExperiment::assay(data_input$se_SSP)) else character(0) samples <- if (length(samples_NC)) samples_NC else samples_SSP # Hold per-method calls; start empty and fill by matching PatientID @@ -1349,7 +1350,8 @@ BS_Multi <- function( res_subtypes$row_id <- NULL if (Subtype) { - res_subtypes.Subtype <- as.data.frame(res_subtypes.Subtype, stringsAsFactors = FALSE, check.names = FALSE) + res_subtypes.Subtype <- + as.data.frame(res_subtypes.Subtype, stringsAsFactors = FALSE, check.names = FALSE) rownames(res_subtypes.Subtype) <- res_subtypes.Subtype$row_id res_subtypes.Subtype$row_id <- NULL } diff --git a/R/NC_functions.R b/R/NC_functions.R index 21b8861..3e3147e 100755 --- a/R/NC_functions.R +++ b/R/NC_functions.R @@ -99,11 +99,11 @@ overlapSets <- function(x, y) { #' @param external For External: NAME of a column in medians.all (e.g., "RNAseq.V2", "Given.mdns") #' @noRd docalibration <- function( - y, - medians.all, - calibration = c("None", "Internal", "External"), - internal = NA, - external = NA) { + y, + medians.all, + calibration = c("None", "Internal", "External"), + internal = NA, + external = NA) { calibration <- match.arg(calibration) y <- as.matrix(y) mq <- 0.05 # robust quantiles for 'qCtr' (genefu.robust) @@ -169,8 +169,8 @@ standardize <- function(x) { #' Function for suffix of medians for gene centering #' @noRd getsuffix <- function(calibration, - internal = NA, - external = NA) { + internal = NA, + external = NA) { calibration <- match.arg(calibration, choices = c("None", "Internal", "External")) suffix <- switch(calibration, @@ -327,14 +327,14 @@ sspPredict <- function(x, y, std = FALSE, distm = "spearman", Subtype = TRUE) { #' @param Subtype Logic. #' @param hasClinical Logic. Specify whether clinical information is included. #' For example, tumor size should be in the "T" column, and lymph node status -#' should be in the "NODE" column. +#' - "NODE": Lymph node status (0 = negative; \eqn{\ge 1}{>= 1} = positive). #' @return ROR, ROR risk group and other indications #' @noRd RORgroup <- function(out, - df.cln, - Subtype = FALSE, - hasClinical = FALSE) { + df.cln, + Subtype = FALSE, + hasClinical = FALSE) { sample <- data.frame(patientID = names(out$predictions)) distance <- data.frame(out$distances, row.names = names(out$predictions)) @@ -717,13 +717,13 @@ RORgroup <- function(out, #' makeCalls.parker <- function(mat, - df.cln, - calibration = c("None", "Internal", "External"), - internal = NA, - external = NA, - medians = NULL, - Subtype = FALSE, - hasClinical = FALSE) { + df.cln, + calibration = c("None", "Internal", "External"), + internal = NA, + external = NA, + medians = NULL, + Subtype = FALSE, + hasClinical = FALSE) { ## loading dataset data_env <- new.env(parent = emptyenv()) data("BreastSubtypeRobj", envir = data_env, package = "BreastSubtypeR") @@ -828,20 +828,20 @@ makeCalls.parker <- function(mat, #' @param Subtype Logic. #' @param hasClinical Logic. Specify whether clinical information is included. #' For example, tumor size should be in the "T" column, and lymph node status -#' should be in the "NODE" column. +#' - "NODE": Lymph node status (0 = negative; \eqn{\ge 1}{>= 1} = positive). #' @param seed An integer value is used to set the random seed. #' @noRd makeCalls_ihc <- function(mat, - df.cln, - calibration = "Internal", - internal = "IHC.mdns", - external = NA, - medians = NA, - Subtype = FALSE, - hasClinical = FALSE, - seed = 118) { + df.cln, + calibration = "Internal", + internal = "IHC.mdns", + external = NA, + medians = NA, + Subtype = FALSE, + hasClinical = FALSE, + seed = 118) { ## loading dataset data_env <- new.env(parent = emptyenv()) data("BreastSubtypeRobj", envir = data_env, package = "BreastSubtypeR") @@ -971,16 +971,16 @@ makeCalls_ihc <- function(mat, #' @noRd makeCalls_ihc.iterative <- function(mat, - df.cln, - iteration = 100, - ratio = 54 / 64, - calibration = "Internal", - internal = "ER.mdns", - external = NA, - medians = NA, - Subtype = FALSE, - hasClinical = FALSE, - seed = 118) { + df.cln, + iteration = 100, + ratio = 54 / 64, + calibration = "Internal", + internal = "ER.mdns", + external = NA, + medians = NA, + Subtype = FALSE, + hasClinical = FALSE, + seed = 118) { ## loading dataset data_env <- new.env(parent = emptyenv()) data("BreastSubtypeRobj", envir = data_env, package = "BreastSubtypeR") @@ -1163,20 +1163,20 @@ makeCalls_ihc.iterative <- function(mat, #' @param Subtype Logic #' @param hasClinical Logic. Specify whether clinical information is included. #' For example, tumor size should be in the "T" column, and lymph node status -#' should be in the "NODE" column. +#' - "NODE": Lymph node status (0 = negative; \eqn{\ge 1}{>= 1} = positive). #' @param seed An integer value is used to set the random seed. #' @noRd makeCalls.PC1ihc <- function( - mat, - df.cln, - calibration = "Internal", - internal = "PC1ihc.mdns", - external = NA, - medians = NA, - Subtype = FALSE, - hasClinical = FALSE, - seed = 118) { + mat, + df.cln, + calibration = "Internal", + internal = "PC1ihc.mdns", + external = NA, + medians = NA, + Subtype = FALSE, + hasClinical = FALSE, + seed = 118) { ## loading dataset data_env <- new.env(parent = emptyenv()) data("BreastSubtypeRobj", envir = data_env, package = "BreastSubtypeR") @@ -1356,20 +1356,20 @@ makeCalls.PC1ihc <- function( #' @param Subtype Logic. #' @param hasClinical Logic. Specify whether clinical information is included. #' For example, tumor size should be in the "T" column, and lymph node status -#' should be in the "NODE" column. +#' - "NODE": Lymph node status (0 = negative; \eqn{\ge 1}{>= 1} = positive). #' @param seed An integer value is used to set the random seed. #' @noRd makeCalls.v1PAM <- function( - mat, - df.pam, - calibration = "Internal", - internal = "v1PAM.mdns", - external = NA, - medians = NA, - Subtype = FALSE, - hasClinical = FALSE, - seed = 118) { + mat, + df.pam, + calibration = "Internal", + internal = "v1PAM.mdns", + external = NA, + medians = NA, + Subtype = FALSE, + hasClinical = FALSE, + seed = 118) { ## loading dataset data_env <- new.env(parent = emptyenv()) data("BreastSubtypeRobj", envir = data_env, package = "BreastSubtypeR") @@ -1484,15 +1484,15 @@ makeCalls.v1PAM <- function( #' @param Subtype Logic. Specify whether to predict Subtype-like subtyping. #' @param hasClinical Logic. Specify whether clinical information is included. #' For example, tumor size should be in the "T" column, and lymph node status -#' should be in the "NODE" column. +#' - "NODE": Lymph node status (0 = negative; \eqn{\ge 1}{>= 1} = positive). #' @noRd makeCalls.ssBC <- function( - mat, - df.cln, - s = c("ER", "TN", "ER.v2", "TN.v2"), - Subtype = FALSE, - hasClinical = FALSE) { + mat, + df.cln, + s = c("ER", "TN", "ER.v2", "TN.v2"), + Subtype = FALSE, + hasClinical = FALSE) { ## loading dataset data_env <- new.env(parent = emptyenv()) data("BreastSubtypeRobj", envir = data_env, package = "BreastSubtypeR") diff --git a/R/SSPBC_functions.R b/R/SSPBC_functions.R index fa335e6..2aeed2b 100755 --- a/R/SSPBC_functions.R +++ b/R/SSPBC_functions.R @@ -1,10 +1,11 @@ ## adapted from original sspbc codes (Staaf et al., 2022) #' Function sspbc -#' @import e1071 #' @import methods #' @import stats #' @import grid +#' @importFrom e1071 naiveBayes +#' #' @description #' This function assign classes to breast cancer samples using a selection of provided models. It works on raw gene expression data. #' Provided models were developed using gene expression data from mRNAseq generated using HiSat/StringTie. @@ -13,19 +14,19 @@ #' @noRd applySSP <- function( - tsv, - ssp = "", - plot = FALSE, - txt = FALSE, - report = FALSE, - add.is.num = TRUE, - mylas = 1, - ssp.name = "", - gex = NULL, - id = NULL, - id.type = "Gene.ID", - full.out = FALSE, - output) { + tsv, + ssp = "", + plot = FALSE, + txt = FALSE, + report = FALSE, + add.is.num = TRUE, + mylas = 1, + ssp.name = "", + gex = NULL, + id = NULL, + id.type = "Gene.ID", + full.out = FALSE, + output) { data_env <- new.env(parent = emptyenv()) data("Gene.ID.ann", envir = data_env, package = "BreastSubtypeR") Gene.ID.ann <- data_env[["Gene.ID.ann"]] @@ -334,11 +335,11 @@ read_StringTie_tsv_FPKM <- function(tsv, id, report = FALSE) { # function translate_id2entrez <- function( - id, - ann, - id.type = "Gene.ID", - e = FALSE, - report = FALSE) { + id, + ann, + id.type = "Gene.ID", + e = FALSE, + report = FALSE) { if (id.type %in% c("Gene.ID", "Gene.Name", "HGNC", "EntrezGene")) { # find first index for id in ann id.i <- match(as.character(id), ann[, id.type]) diff --git a/R/Utilities.R b/R/Utilities.R index 1438829..0d8894b 100755 --- a/R/Utilities.R +++ b/R/Utilities.R @@ -1,16 +1,17 @@ #' #' Functions for BreastSubtypeR package -#' @import ggplot2 +#' @keywords internal #' @import ComplexHeatmap #' @import RColorBrewer #' @import circlize #' @import ggrepel #' @import magrittr #' @import impute -#' @importFrom SummarizedExperiment SummarizedExperiment -#' @importFrom SummarizedExperiment colData -#' @importFrom SummarizedExperiment rowData -#' @importFrom SummarizedExperiment assay +#' @importFrom dplyr mutate across everything +#' @importFrom ggplot2 ggplot aes geom_point geom_bar geom_text geom_line geom_vline geom_hline +#' @importFrom ggplot2 geom_boxplot scale_x_continuous scale_color_manual scale_fill_manual +#' @importFrom ggplot2 coord_polar labs theme theme_classic theme_minimal theme_void element_text +#' @importFrom SummarizedExperiment SummarizedExperiment colData rowData assay #' #' @noRd NULL @@ -170,10 +171,10 @@ prepare_ssp_matrix <- function(x, genes.s, RawCounts, samplenames, verbose) { #' @noRd domapping <- function(se_obj, - RawCounts = FALSE, - method = "max", - impute = TRUE, - verbose = TRUE) { + RawCounts = FALSE, + method = "max", + impute = TRUE, + verbose = TRUE) { ## 1. Input raw counts if (RawCounts && !"Length" %in% colnames(rowData(se_obj))) { stop( @@ -515,12 +516,15 @@ get_average_subtype <- function(res_ihc_iterative, consensus_subtypes) { ## if FALSE, make the cell as NULL keep <- res_ihc$predictions == consensus_subtypes - res_ihc$distances[!keep, ] <- as.list(rep(NA, 5)) + res_ihc$distances[!keep, ] <- as.list(rep(NA, ncol(res_ihc$distances))) - res <- dplyr::mutate_at( + ## convert every column to numeric (preserving NA), same as before + res <- dplyr::mutate( res_ihc$distances, - vars(everything()), - ~ ifelse(!is.na(.), as.numeric(as.character(.)), NA) + dplyr::across( + dplyr::everything(), + ~ ifelse(!is.na(.x), as.numeric(as.character(.x)), NA_real_) + ) ) return(res) @@ -577,12 +581,14 @@ get_average_subtype <- function(res_ihc_iterative, consensus_subtypes) { ## if FALSE, make the cell as NULL keep <- res_ihc$predictions == consensus_subtypes - res_ihc$distances.Subtype[!keep, ] <- as.list(rep(NA, 4)) + res_ihc$distances.Subtype[!keep, ] <- as.list(rep(NA, ncol(res_ihc$distances.Subtype))) - res <- dplyr::mutate_at( + res <- dplyr::mutate( res_ihc$distances.Subtype, - vars(everything()), - ~ ifelse(!is.na(.), as.numeric(as.character(.)), NA) + dplyr::across( + dplyr::everything(), + ~ ifelse(!is.na(.x), as.numeric(as.character(.x)), NA_real_) + ) ) return(res) diff --git a/R/runShinyBreastSubtypeR.R b/R/runShinyBreastSubtypeR.R index 9b634d6..9519488 100755 --- a/R/runShinyBreastSubtypeR.R +++ b/R/runShinyBreastSubtypeR.R @@ -9,21 +9,18 @@ #' @param attach_tidyverse Logical; if TRUE and tidyverse is installed, it will #' be attached quietly for the session (purely optional). #' @param max_upload_mb Numeric; Shiny upload size limit (in MB). Default 1000. -#' @return Opens the app; returns the value of `shiny::runApp()`. +#' @return The value returned by `shiny::runApp()` (usually `invisible(NULL)`). #' #' @examples -#' \donttest{ -#' # Basic -#' iBreastSubtypeR() -#' -#' # Skip attaching packages (if UI/server fully qualify all calls) -#' iBreastSubtypeR(attach = character(0)) +#' if (interactive()) { +#' iBreastSubtypeR() +#' iBreastSubtypeR(attach = character(0)) #' } #' #' @export iBreastSubtypeR <- function(attach = c("shiny", "bslib"), - attach_tidyverse = FALSE, - max_upload_mb = 1000) { + attach_tidyverse = FALSE, + max_upload_mb = 1000) { # helper: check install + attach quietly .attach_if <- function(pkgs) { pkgs <- unique(pkgs) @@ -70,14 +67,11 @@ iBreastSubtypeR <- function(attach = c("shiny", "bslib"), shiny::runApp(appDir, display.mode = "normal") } -#' @title (Deprecated) Run iBreastSubtypeR -#' @description Back-compat wrapper; use [iBreastSubtypeR()] instead. -#' @param ... Arguments passed on to [iBreastSubtypeR()]. -#' @export +#' (Deprecated) Run iBreastSubtypeR +#' +#' Internal wrapper kept for back-compat. Use [iBreastSubtypeR()] directly. +#' @keywords internal +#' @noRd runShinyBreastSubtypeR <- function(...) { - .Deprecated("iBreastSubtypeR", - package = "BreastSubtypeR", - msg = "runShinyBreastSubtypeR() is deprecated; use iBreastSubtypeR()." - ) iBreastSubtypeR(...) } diff --git a/R/zzz.R b/R/zzz.R index 32d611a..259f505 100755 --- a/R/zzz.R +++ b/R/zzz.R @@ -2,4 +2,4 @@ utils::globalVariables(c("AIMSmodel", "BreastSubtypeRobj")) ## AIMS/sspbc -utils::globalVariables(c("Gene.ID.ann", "sspbc.models", "sspbc.models.fullname")) +utils::globalVariables(c(".data", "Gene.ID.ann", "sspbc.models", "sspbc.models.fullname")) diff --git a/man/BS_Multi.Rd b/man/BS_Multi.Rd index b67d72c..b818970 100755 --- a/man/BS_Multi.Rd +++ b/man/BS_Multi.Rd @@ -38,8 +38,8 @@ HER2-enriched, Basal-like), excluding Normal-like.} \item{hasClinical}{Logical. If \code{TRUE}, incorporates clinical data from \code{colData(se_obj)}. Required columns: \itemize{ -\item \code{"TSIZE"}: Tumor size (\code{0} = ≤2 cm; \code{1} = >2 cm). -\item \code{"NODE"}: Lymph node status (\code{0} = negative; ≥1 = positive; must be numeric). +\item "TSIZE": Tumor size (0 = \eqn{\le 2}{<= 2} cm; 1 = \eqn{> 2}{> 2} cm). +\item "NODE": Lymph node status (0 = negative; \eqn{\ge 1}{>= 1} = positive). }} } \value{ diff --git a/man/BS_PCAPAM50.Rd b/man/BS_PCAPAM50.Rd index e99cc22..3230755 100755 --- a/man/BS_PCAPAM50.Rd +++ b/man/BS_PCAPAM50.Rd @@ -24,8 +24,8 @@ genes (Gene Symbols) as rows and samples as columns. \item{hasClinical}{Logical. If \code{TRUE}, incorporates additional clinical variables from \code{colData(se_obj)}. Required columns: \itemize{ -\item \code{"TSIZE"}: Tumor size (\code{0} = ≤2 cm; \code{1} = >2 cm). -\item \code{"NODE"}: Lymph node status (\code{0} = negative; ≥1 = positive). Must be numeric. +\item "TSIZE": Tumor size (0 = \eqn{\le 2}{<= 2} cm; 1 = \eqn{> 2}{> 2} cm). +\item "NODE": Lymph node status (0 = negative; \eqn{\ge 1}{>= 1} = positive). Must be numeric. }} \item{seed}{Integer. Random seed for reproducibility.} diff --git a/man/BS_cIHC.Rd b/man/BS_cIHC.Rd index 97c6f3a..d855fe9 100755 --- a/man/BS_cIHC.Rd +++ b/man/BS_cIHC.Rd @@ -24,8 +24,8 @@ genes (Gene Symbols) as rows and samples as columns. \item{hasClinical}{Logical. If \code{TRUE}, incorporates additional clinical variables from \code{colData(se_obj)}. Required columns: \itemize{ -\item \code{"TSIZE"}: Tumor size (\code{0} = ≤2 cm; \code{1} = >2 cm). -\item \code{"NODE"}: Lymph node status (\code{0} = negative; ≥1 = positive). Must be numeric. +\item "TSIZE": Tumor size (0 = \eqn{\le 2}{<= 2} cm; 1 = \eqn{> 2}{> 2} cm). +\item "NODE": Lymph node status (0 = negative; \eqn{\ge 1}{>= 1} = positive). Must be numeric. }} \item{seed}{Integer. Random seed for reproducibility of ER-balancing.} diff --git a/man/BS_cIHC.itr.Rd b/man/BS_cIHC.itr.Rd index 945a16a..52d3b39 100755 --- a/man/BS_cIHC.itr.Rd +++ b/man/BS_cIHC.itr.Rd @@ -26,7 +26,7 @@ genes (Gene Symbols) as rows and samples as columns. }} \item{iteration}{Integer. Number of iterations for the ER-balancing procedure. -Default: \code{100}.} +Default: 100.} \item{ratio}{Numeric. Target ER+/ER– ratio for balancing. Options: \itemize{ @@ -40,8 +40,8 @@ Default: \code{100}.} \item{hasClinical}{Logical. If \code{TRUE}, incorporates additional clinical variables from \code{colData(se_obj)}. Required columns: \itemize{ -\item \code{"TSIZE"}: Tumor size (\code{0} = ≤2 cm; \code{1} = >2 cm). -\item \code{"NODE"}: Lymph node status (\code{0} = negative; ≥1 = positive). Must be numeric. +\item "TSIZE": Tumor size (0 = \eqn{\le 2}{<= 2} cm; 1 = \eqn{> 2}{> 2} cm). +\item "NODE": Lymph node status (0 = negative; \eqn{\ge 1}{>= 1} = positive). Must be numeric. }} \item{seed}{Integer. Random seed for reproducibility.} diff --git a/man/BS_parker.Rd b/man/BS_parker.Rd index 90f29c8..6ad2a8d 100755 --- a/man/BS_parker.Rd +++ b/man/BS_parker.Rd @@ -17,8 +17,8 @@ BS_parker( \arguments{ \item{se_obj}{A \code{SummarizedExperiment} object containing: \itemize{ -\item \strong{Assay data}: A log-transformed, normalized gene expression matrix with genes (Gene Symbols) as rows -and samples as columns. +\item \strong{Assay data}: A log-transformed, normalized gene expression matrix with genes +(Gene Symbols) as rows and samples as columns. \item \strong{Column metadata} (\code{colData}): Optional sample- or patient-level information. }} @@ -60,8 +60,8 @@ excluding Normal-like.} \item{hasClinical}{Logical. If \code{TRUE}, incorporates clinical variables from \code{colData(se_obj)}. Required columns: \itemize{ -\item \code{"TSIZE"}: Tumor size (\code{0} = ≤2 cm; \code{1} = >2 cm). -\item \code{"NODE"}: Lymph node status (\code{0} = negative; ≥1 = positive). Must be numeric. +\item "TSIZE": Tumor size (0 = \eqn{\le 2}{<= 2} cm; 1 = \eqn{> 2}{> 2} cm). +\item "NODE": Lymph node status (0 = negative; \eqn{\ge 1}{>= 1} = positive). Must be numeric. }} } \value{ diff --git a/man/BS_ssBC.Rd b/man/BS_ssBC.Rd index a4c25fe..2d714d5 100755 --- a/man/BS_ssBC.Rd +++ b/man/BS_ssBC.Rd @@ -35,8 +35,8 @@ genes (Gene Symbols) as rows and samples as columns. \item{hasClinical}{Logical. If \code{TRUE}, incorporates additional clinical variables from \code{colData(se_obj)}. Required columns: \itemize{ -\item \code{"TSIZE"}: Tumor size (\code{0} = ≤2 cm; \code{1} = >2 cm). -\item \code{"NODE"}: Lymph node status (\code{0} = negative; ≥1 = positive). Must be numeric. +\item "TSIZE": Tumor size (0 = \eqn{\le 2}{<= 2} cm; 1 = \eqn{> 2}{> 2} cm). +\item "NODE": Lymph node status (0 = negative; \eqn{\ge 1}{>= 1} = positive). Must be numeric. }} } \value{ diff --git a/man/BreastSubtypeR.Rd b/man/BreastSubtypeR.Rd index b3d613a..59c03ad 100755 --- a/man/BreastSubtypeR.Rd +++ b/man/BreastSubtypeR.Rd @@ -4,7 +4,8 @@ \name{BreastSubtypeR} \alias{BreastSubtypeR} \alias{BreastSubtypeR-package} -\title{BreastSubtypeR: A Unified R/Bioconductor Package for Intrinsic Molecular Subtyping in Breast Cancer Research} +\title{BreastSubtypeR: A Unified R/Bioconductor Package +for Intrinsic Molecular Subtyping in Breast Cancer Research} \description{ \strong{BreastSubtypeR} is an R/Bioconductor package that unifies multiple published intrinsic subtyping (IS) methods for breast cancer into a single, diff --git a/man/Mapping.Rd b/man/Mapping.Rd index 4f3907f..088b77b 100755 --- a/man/Mapping.Rd +++ b/man/Mapping.Rd @@ -17,7 +17,8 @@ Mapping( \itemize{ \item \strong{Assay data}: \itemize{ -\item If \code{RawCounts = FALSE}: \code{assay()} must contain log2-normalized expression (e.g., pre-normalized microarray/nCounter, or log2(FPKM+1) RNAseq). +\item If \code{RawCounts = FALSE}: \code{assay()} must contain log2-normalized expression +(e.g., pre-normalized microarray/nCounter, or log2(FPKM+1) RNAseq). \item If \code{RawCounts = TRUE}: \code{assay()} contains raw RNA-seq counts (see \code{RawCounts}). } \item \strong{Row metadata} (required): @@ -31,7 +32,8 @@ renamed as \code{probe}. }} \item{RawCounts}{Logical. If \code{TRUE}, indicates that \code{assay()} holds raw RNA-seq counts. -In this case, \code{rowData()} must also provide gene lengths (column \code{"Length"}, in base pairs), used for: +In this case, \code{rowData()} must also provide gene lengths +(column \code{"Length"}, in base pairs), used for: \itemize{ \item NC-based methods: log2-CPM (upper-quartile normalization). \item SSP-based methods: linear FPKM (not log-transformed). @@ -53,8 +55,10 @@ In this case, \code{rowData()} must also provide gene lengths (column \code{"Len \value{ A named list with: \describe{ -\item{se_NC}{\code{SummarizedExperiment} holding log2-transformed data prepared for NC-based methods (assay name: \code{counts}).} -\item{se_SSP}{\code{SummarizedExperiment} holding linear-scale data prepared for SSP-based methods (assay name: \code{counts}).} +\item{se_NC}{\code{SummarizedExperiment} holding log2-transformed data prepared for NC-based methods +(assay name: \code{counts}).} +\item{se_SSP}{\code{SummarizedExperiment} holding linear-scale data prepared for SSP-based methods +(assay name: \code{counts}).} } } \description{ @@ -73,30 +77,18 @@ This design allows users to supply a single expression format, while BreastSubtypeR automatically applies method-specific preprocessing. } \examples{ -\donttest{ -library(BreastSubtypeR) +if (requireNamespace("SummarizedExperiment", quietly = TRUE)) { + # Using example raw RNA-seq counts (with gene lengths) + data("TCGABRCAobj") + se_obj_counts <- TCGABRCAobj$se_obj[, 1:3] # tiny subset to keep checks fast + res <- Mapping(se_obj_counts, RawCounts = TRUE) -# Using raw RNA-seq counts (with gene lengths) -se_obj <- SummarizedExperiment( - assays = list(counts = raw_counts_mat), - rowData = DataFrame( - probe = rownames(raw_counts_mat), - ENTREZID = entrez_ids, - Length = gene_lengths - ) -) -res <- Mapping(se_obj, RawCounts = TRUE) - -# Using pre-normalized log2(FPKM+1) -se_obj_fpkm <- SummarizedExperiment( - assays = list(expr = log2_fpkm_mat), - rowData = DataFrame( - probe = rownames(log2_fpkm_mat), - ENTREZID = entrez_ids - ) -) -res <- Mapping(se_obj_fpkm, RawCounts = FALSE) + # Using example pre-normalized log2(FPKM+0.1) + data("OSLO2EMIT0obj") + se_obj_fpkm <- OSLO2EMIT0obj$se_obj[, 1:3] # tiny subset to keep checks fast + res <- Mapping(se_obj_fpkm, RawCounts = FALSE) } + } \references{ Yang Q, Hartman J, Sifakis EG. diff --git a/man/iBreastSubtypeR.Rd b/man/iBreastSubtypeR.Rd index 20807ce..6f7abb3 100755 --- a/man/iBreastSubtypeR.Rd +++ b/man/iBreastSubtypeR.Rd @@ -20,7 +20,7 @@ be attached quietly for the session (purely optional).} \item{max_upload_mb}{Numeric; Shiny upload size limit (in MB). Default 1000.} } \value{ -Opens the app; returns the value of \code{shiny::runApp()}. +The value returned by \code{shiny::runApp()} (usually \code{invisible(NULL)}). } \description{ Starts the Shiny UI bundled with the BreastSubtypeR package. @@ -28,12 +28,9 @@ The launcher can (optionally) attach Shiny/Bslib so UI/server can use unqualified functions like \code{tags}, \code{icon}, \code{fileInput}, etc. } \examples{ -\donttest{ -# Basic -iBreastSubtypeR() - -# Skip attaching packages (if UI/server fully qualify all calls) -iBreastSubtypeR(attach = character(0)) +if (interactive()) { + iBreastSubtypeR() + iBreastSubtypeR(attach = character(0)) } } diff --git a/man/runShinyBreastSubtypeR.Rd b/man/runShinyBreastSubtypeR.Rd deleted file mode 100644 index cae88d0..0000000 --- a/man/runShinyBreastSubtypeR.Rd +++ /dev/null @@ -1,14 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/runShinyBreastSubtypeR.R -\name{runShinyBreastSubtypeR} -\alias{runShinyBreastSubtypeR} -\title{(Deprecated) Run iBreastSubtypeR} -\usage{ -runShinyBreastSubtypeR(...) -} -\arguments{ -\item{...}{Arguments passed on to \code{\link[=iBreastSubtypeR]{iBreastSubtypeR()}}.} -} -\description{ -Back-compat wrapper; use \code{\link[=iBreastSubtypeR]{iBreastSubtypeR()}} instead. -} diff --git a/vignettes/BreastSubtypeR.Rmd b/vignettes/BreastSubtypeR.Rmd index 6688b80..62da02d 100755 --- a/vignettes/BreastSubtypeR.Rmd +++ b/vignettes/BreastSubtypeR.Rmd @@ -22,6 +22,7 @@ knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.path = "figures/", + fig.crop = FALSE, warning = FALSE, message = FALSE ) @@ -91,7 +92,7 @@ The package includes implementations of commonly used subtyping methods (NC-base The examples below use small example datasets shipped with the package. For your own data, provide a `SummarizedExperiment` **with clinical metadata in `colData`** (e.g., `PatientID`, ER/HER2; for ROR: `TSIZE`, `NODE`). -```{r, eval=FALSE} +```{r pkgs-and-data, message=FALSE} library(BreastSubtypeR) # Example data @@ -101,8 +102,9 @@ data("OSLO2EMIT0obj") **1) Map & prepare (method-specific pre-processing + mapping)** -```{r, eval=FALSE} -# Pre-processing: automatically apply tailored normalisation, map probes/IDs to Entrez, and (optionally) impute missing values +```{r, eval=TRUE} +# Pre-processing: automatically apply tailored normalisation, map probes/IDs to Entrez, +# and (optionally) impute missing values data_input <- Mapping( OSLO2EMIT0obj$se_obj, method = "max", # mapping strategy (example) @@ -127,7 +129,7 @@ data_input <- Mapping( **2) Multi-method run (user-defined)** -```{r, eval=FALSE} +```{r, eval=TRUE} methods <- c("parker.original", "PCAPAM50", "sspbc") res <- BS_Multi( @@ -145,7 +147,7 @@ head(res$res_subtypes, 5) AUTO evaluates cohort diagnostics (for example, ER/HER2 distribution, subtype purity, and subgroup sizes) and selects methods compatible with the cohort. It disables classifiers whose distributional assumptions would likely be violated. -```{r, eval=FALSE} +```{r, eval=TRUE} res_auto <- BS_Multi( data_input = data_input, methods = "AUTO", @@ -161,7 +163,7 @@ Vis_Multi(res_auto$res_subtypes) PAM50 (NC-based) -```{r, eval=FALSE} +```{r, eval=TRUE} res_pam <- BS_parker( se_obj = data_input$se_NC, # object prepared for NC-based methods calibration = "Internal", @@ -173,7 +175,7 @@ res_pam <- BS_parker( AIMS (SSP-based) -```{r, eval=FALSE} +```{r, eval=TRUE} res_aims <- BS_AIMS(data_input$se_SSP) ``` @@ -191,7 +193,8 @@ res_aims <- BS_AIMS(data_input$se_SSP) - Use `methods = "AUTO"` (i.e. `BS_Multi(methods = "AUTO", ...)`) for exploratory datasets or cohorts of unknown / skewed composition. - Use `AUTO` when you want the package to **select only classifiers compatible with the cohort** (it disables methods whose assumptions appear violated). -- For validation against a single published method or a clinical assay (e.g., Prosigna®), run the corresponding **single-method** implementation directly (e.g., `BS_parker()`). +- For validation against a single published method or a clinical assay (e.g., Prosigna®), +run the corresponding **single-method** implementation directly (e.g., `BS_parker()`). ## Interpretation @@ -226,7 +229,8 @@ The app runs locally; **no data leave your machine**. BreastSubtypeR harmonises many published, signature-based classifiers but has known limitations:\ -- It is not a clinical-grade replacement for assays like Prosigna; clinical validation requires paired clinical assay data.\ +- It is not a clinical-grade replacement for assays like Prosigna; +clinical validation requires paired clinical assay data.\ - AUTO selects compatible methods; it does not perform consensus voting by default.