diff --git a/.nf-core.yml b/.nf-core.yml index bbe519e..d6a5a34 100644 --- a/.nf-core.yml +++ b/.nf-core.yml @@ -12,4 +12,4 @@ template: name: nanostring org: nf-core outdir: . - version: 1.3.2 + version: 1.3.3 diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a8d3fc..505418e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v1.3.3 - 2026-01-30 - Micrometer patch 3 + +### `Fixed` + +- Addressed issues with latest GSVA function calls after GSVA >1.5.0 was introduced to be able to use gene score methods accordingly again +- [#156](https://github.com/nf-core/nanostring/pull/156) - Updated the analysis dependencies in the local module for gene score computation to latest versions of the R Scripts +- [#155](https://github.com/nf-core/nanostring/pull/155) - Removed container dependency on biocontainers and rather using Wave containers again +- [#161](https://github.com/nf-core/nanostring/pull/161) - Update container urls to use HTTPS for Singularity and update dependencies for other local modules too + ## v1.3.2 - 2026-01-15 - Micrometer patch 2 ### `Fixed` diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index 85d6b78..c22160d 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -1,5 +1,5 @@ report_comment: > - This report has been generated by the nf-core/nanostring analysis pipeline. For information about how to interpret these results, please see the documentation. + This report has been generated by the nf-core/nanostring analysis pipeline. For information about how to interpret these results, please see the documentation. report_section_order: BD: order: 970 diff --git a/modules.json b/modules.json index b58c978..91ff1d5 100644 --- a/modules.json +++ b/modules.json @@ -36,7 +36,7 @@ }, "utils_nfschema_plugin": { "branch": "master", - "git_sha": "ff506dcada6fc826ed0c641dc2ed1e98f7345fbe", + "git_sha": "fdc08b8b1ae74f56686ce21f7ea11ad11990ce57", "installed_by": ["subworkflows"] } } diff --git a/modules/local/compute_gene_scores/environment.yml b/modules/local/compute_gene_scores/environment.yml index 0c769bd..db04523 100644 --- a/modules/local/compute_gene_scores/environment.yml +++ b/modules/local/compute_gene_scores/environment.yml @@ -2,12 +2,12 @@ channels: - conda-forge - bioconda dependencies: - - conda-forge::r-yaml=2.3.7 - - conda-forge::r-ggplot2=3.4.4 + - conda-forge::r-yaml=2.3.12 + - conda-forge::r-ggplot2=4.0.1 - conda-forge::r-dplyr=1.1.4 - - conda-forge::r-stringr=1.5.0 - - bioconda::bioconductor-gsva=1.46.0 - - bioconda::bioconductor-singscore=1.18.0 - - conda-forge::r-factominer=2.8.0 - - conda-forge::r-tibble=3.2.1 - - conda-forge::r-matrixstats=1.1.0 + - conda-forge::r-stringr=1.6.0 + - bioconda::bioconductor-gsva=2.0.0 + - bioconda::bioconductor-singscore=1.26.0 + - conda-forge::r-factominer=2.13.0 + - conda-forge::r-tibble=3.3.1 + - conda-forge::r-matrixstats=1.5.0 diff --git a/modules/local/compute_gene_scores/main.nf b/modules/local/compute_gene_scores/main.nf index 805d5e8..ac9146d 100644 --- a/modules/local/compute_gene_scores/main.nf +++ b/modules/local/compute_gene_scores/main.nf @@ -3,8 +3,8 @@ process COMPUTE_GENE_SCORES { conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/mulled-v2-e6920e60d80922852a1b19630ebe16754cf5320d:75e2c0a29159bae8a964e43ae16a45c282fdf651-0' : - 'biocontainers/mulled-v2-e6920e60d80922852a1b19630ebe16754cf5320d:75e2c0a29159bae8a964e43ae16a45c282fdf651-0' }" + 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/88/880e5ac778d3985389c50f48135ab25eace11ea27e6a8267ecf350cf98f4f1ce/data' : + 'community.wave.seqera.io/library/bioconductor-gsva_bioconductor-singscore_r-dplyr_r-factominer_pruned:e6f1a5cd9110d36b' }" input: tuple val(meta), path(normalized_counts) diff --git a/modules/local/compute_gene_scores/resources/usr/bin/compute_gene_scores.R b/modules/local/compute_gene_scores/resources/usr/bin/compute_gene_scores.R index b9d7bea..0807703 100755 --- a/modules/local/compute_gene_scores/resources/usr/bin/compute_gene_scores.R +++ b/modules/local/compute_gene_scores/resources/usr/bin/compute_gene_scores.R @@ -7,29 +7,41 @@ library(stringr) library(dplyr) library(yaml) library(FactoMineR) -library(stringr) library(tibble) +library(data.table) +library(ggplot2) ###Gene Set Functions # helper functions -loadMappingTable <- function(ref.file="./ensembl_86_mpens.txt") { - s2e <- read.delim(file=ref.file, - stringsAsFactors = F, header=F, skip=1) +loadMappingTable <- function(ref.file = "./ensembl_86_mpens.txt") { + s2e <- read.delim( + file = ref.file, + stringsAsFactors = F, + header = F, + skip = 1 + ) colnames(s2e) <- c("ensembl", "genesymbol") return(s2e) } -geneSymbol2Ensembl <- function(x, s2e=NULL) { - if(is.null(s2e)) s2e <- loadMappingTable() +geneSymbol2Ensembl <- function(x, s2e = NULL) { + if (is.null(s2e)) { + s2e <- loadMappingTable() + } # also check for correct colnames (TODO) - ens <- unique(s2e[s2e$genesymbol %in% x,"ensembl"]) + ens <- unique(s2e[s2e$genesymbol %in% x, "ensembl"]) # print(ens) - if(length(ens)0,1] - retSet$downSet <- set[set[,2]<0,1] - retSet$knownDir <- TRUE - } + retSet$knownDir <- FALSE + } else if (is.data.frame(set)) { + if (!is.numeric(set[, 2])) { + stop("Second column of gene set data frame must be numeric.") + } else if (all(set[, 2] == 0)) { + retSet$upSet <- set[, 1] + retSet$downSet <- NULL + retSet$knownDir <- FALSE + } else { + retSet$upSet <- set[set[, 2] > 0, 1] + retSet$downSet <- set[set[, 2] < 0, 1] + retSet$knownDir <- TRUE + } } else { stop("Gene Set must be a character vector or a data.frame") } - if(length(retSet$upSet)==0) retSet$upSet<-NULL - if(length(retSet$downSet)==0) retSet$downSet<-NULL + if (length(retSet$upSet) == 0) { + retSet$upSet <- NULL + } + if (length(retSet$downSet) == 0) { + retSet$downSet <- NULL + } return(retSet) } @@ -110,115 +128,148 @@ geneSetAsList <- function(set) { ## Create an actual Geneset object from GSEABase package ## not used at the moment createGeneset <- function(set, name.set) { - gs <- GeneSet(unique(set), setName=name.set) + gs <- GeneSet(unique(set), setName = name.set) return(gs) } ## Singscore <- function(d, x, ...) { - if(is.list(x) && is.null(x$downSet)) s <- simpleScore(d, upSet = x$upSet, knownDirection=x$knownDir, ...) - if(is.list(x) && !is.null(x$downSet)) s <- simpleScore(d, upSet = x$upSet, downSet=x$downSet, knownDirection = x$knownDir, ...) + if (is.list(x) && is.null(x$downSet)) { + s <- simpleScore(d, upSet = x$upSet, knownDirection = x$knownDir, ...) + } else if (is.list(x) && !is.null(x$downSet)) { + s <- simpleScore( + d, + upSet = x$upSet, + downSet = x$downSet, + knownDirection = x$knownDir, + ... + ) + } return(s) } ## Calculate singscores (use log TPMs or RPKMs) -calculateSingscores <- function(xp, genes, raw=FALSE, ...) { - +calculateSingscores <- function(xp, genes, raw = FALSE, ...) { geneSets <- lapply(genes, geneSetAsList) ranked.exp <- rankGenes(xp) sscore <- lapply(geneSets, function(x) Singscore(ranked.exp, x, ...)) - flattenSingScores <- function(x,n) { - if("TotalScore" %in% colnames(x)) { - one.row <- x[,"TotalScore",drop=F] - return (one.row) - } + flattenSingScores <- function(x, n) { + if ("TotalScore" %in% colnames(x)) { + one.row <- x[, "TotalScore", drop = F] + return(one.row) + } } - sscore.df <- lapply(names(sscore), function(x) flattenSingScores(sscore[[x]],x)) + sscore.df <- lapply(names(sscore), function(x) { + flattenSingScores(sscore[[x]], x) + }) names(sscore.df) <- names(sscore) sscore.f <- bind_cols(sscore.df) colnames(sscore.f) <- names(sscore.df) rownames(sscore.f) <- rownames(sscore.df[[1]]) sscore.f <- t(sscore.f) - if(raw) return(sscore) - else return(sscore.f) + if (raw) { + return(sscore) + } else { + return(sscore.f) + } } ## Calculate GSVA (check kcdf paramater dependent on data) calculateGSVAscores <- function(xp, genes, ...) { - genes.flat <- lapply(genes, geneSetAsVector) - if(is.data.frame(xp)) xp.matrix <- as.matrix.data.frame(xp) - if(is.matrix(xp)) xp.matrix <- xp + if (is.data.frame(xp)) { + xp.matrix <- as.matrix.data.frame(xp) + } + if (is.matrix(xp)) { + xp.matrix <- xp + } - scores <- gsva(xp.matrix, genes.flat, method="gsva", ...) + scores <- gsva(gsvaParam(xp.matrix, genes.flat), ...) return(scores) } ## Calculate SSGSEA (use log TPMs or RPKMs) calculateSSGSEAscores <- function(xp, genes, ...) { - genes.flat <- lapply(genes, geneSetAsVector) - if(is.data.frame(xp)) xp.matrix <- as.matrix.data.frame(xp) - if(is.matrix(xp)) xp.matrix <- xp + if (is.data.frame(xp)) { + xp.matrix <- as.matrix.data.frame(xp) + } + if (is.matrix(xp)) { + xp.matrix <- xp + } - scores <- gsva(xp.matrix, genes.flat, method="ssgsea", ...) + scores <- gsva(ssgseaParam(xp.matrix, genes.flat), ...) return(scores) } ## Calculate PLAGE (probably also TPMs or RPKMs) -calculatePLAGEscores <- function(xp, genes, correct.dir=FALSE, ...) { - +calculatePLAGEscores <- function(xp, genes, correct.dir = FALSE, ...) { genes.flat <- lapply(genes, geneSetAsVector) - if(is.data.frame(xp)) xp.matrix <- as.matrix.data.frame(xp) - if(is.matrix(xp)) xp.matrix <- xp + if (is.data.frame(xp)) { + xp.matrix <- as.matrix.data.frame(xp) + } + if (is.matrix(xp)) { + xp.matrix <- xp + } - plagescores <- gsva(xp.matrix, genes.flat, method="plage", ...) + plagescores <- gsva(plageParam(xp.matrix, genes.flat), ...) - if(correct.dir) { + if (correct.dir) { meanscores <- calculateMeanScores(xp.matrix, genes.flat) i <- intersect(rownames(meanscores), rownames(plagescores)) - if(length(i)!=length(rownames(plagescores))) stop("Correction failed") - plagescores <- plagescores*sign(sapply(i, function(x) cor(plagescores[x,],meanscores[x,]))) + if (length(i) != length(rownames(plagescores))) { + stop("Correction failed") + } + plagescores <- plagescores * + sign(sapply(i, function(x) cor(plagescores[x, ], meanscores[x, ]))) } return(plagescores) } calculateMedianScores <- function(xp, genes, ...) { - genes.flat <- lapply(genes, geneSetAsVector) - if(is.data.frame(xp)) xp.matrix <- as.matrix.data.frame(xp) - if(is.matrix(xp)) xp.matrix <- xp + if (is.data.frame(xp)) { + xp.matrix <- as.matrix.data.frame(xp) + } + if (is.matrix(xp)) { + xp.matrix <- xp + } - median.result <- t(sapply(genes.flat, function(x) apply(xp.matrix[x, ],2,median, ...))) + median.result <- t(sapply(genes.flat, function(x) { + apply(xp.matrix[x, ], 2, median, ...) + })) return(median.result) } calculateMeanScores <- function(xp, genes, ...) { - genes.flat <- lapply(genes, geneSetAsVector) - if(is.data.frame(xp)) xp.matrix <- as.matrix.data.frame(xp) - if(is.matrix(xp)) xp.matrix <- xp + if (is.data.frame(xp)) { + xp.matrix <- as.matrix.data.frame(xp) + } + if (is.matrix(xp)) { + xp.matrix <- xp + } - mean.result <- t(sapply(genes.flat, function(x) apply(xp.matrix[x, ],2,mean, ...))) + mean.result <- t(sapply(genes.flat, function(x) { + apply(xp.matrix[x, ], 2, mean, ...) + })) return(mean.result) } - - ## geometrix mean functions from stackoverflow ## https://stackoverflow.com/questions/2602583/geometric-mean-is-there-a-built-in ## Note, normalized read counts can be < 1 => log2 cpm can be < 0 => geometric mean of log2 cpm values can be infinite and is not well defined @@ -227,133 +278,179 @@ calculateMeanScores <- function(xp, genes, ...) { # Geometric mean # Naive implementation assuming all values are > 0. -gm_mean_naive = function(a){prod(a)^(1/length(a))} +gm_mean_naive = function(a) { + prod(a)^(1 / length(a)) +} # Geometric mean # Implementation including only values > 0. -gm_mean_simple = function(x, na.rm=TRUE){ - exp(sum(log(x[x > 0]), na.rm=na.rm) / length(x)) +gm_mean_simple = function(x, na.rm = TRUE) { + exp(sum(log(x[x > 0]), na.rm = na.rm) / length(x)) } # Geometric mean # Implementation, which allows options. -gm_mean_advanced = function(x, na.rm=TRUE, zero.propagate = FALSE){ - if(any(x < 0, na.rm = TRUE)){ +gm_mean_advanced = function(x, na.rm = TRUE, zero.propagate = FALSE) { + if (any(x < 0, na.rm = TRUE)) { return(NaN) } - if(zero.propagate){ - if(any(x == 0, na.rm = TRUE)){ - return(0) - } - exp(mean(log(x), na.rm = na.rm)) + if (zero.propagate) { + if (any(x == 0, na.rm = TRUE)) { + return(0) + } + return(exp(mean(log(x), na.rm = na.rm))) } else { - exp(sum(log(x[x > 0]), na.rm=na.rm) / length(x)) + return(exp(sum(log(x[x > 0]), na.rm = na.rm) / length(x))) } } # wrapper called by calculateSamsscore to adapt up and down regulation information -Samsscore <- function(xp, x, center_rows = TRUE,geom_mean = FALSE, ...) { - if(is.list(x) && !is.null(x$downSet)) s <- sams_score(xp, up_genes = x$upSet, - down_genes = x$downSet, - center_rows = center_rows, - geom_mean = geom_mean, - ...) - if(is.list(x) && is.null(x$downSet)) s <- sams_score(xp, up_genes = x$upSet, - down_genes = character(length = 0L), - center_rows = center_rows, - geom_mean = geom_mean, - ...) -return(s) +Samsscore <- function(xp, x, center_rows = TRUE, geom_mean = FALSE, ...) { + if (is.list(x) && !is.null(x$downSet)) { + s <- sams_score( + xp, + up_genes = x$upSet, + down_genes = x$downSet, + center_rows = center_rows, + geom_mean = geom_mean, + ... + ) + } else if (is.list(x) && is.null(x$downSet)) { + s <- sams_score( + xp, + up_genes = x$upSet, + down_genes = character(length = 0L), + center_rows = center_rows, + geom_mean = geom_mean, + ... + ) + } + return(s) } # sams score wrapper to match implementation of other scores -calculateSamsscores <- function(xp, genes, raw = FALSE, - which_col = "score_sum", - center_rows = TRUE, - geom_mean = FALSE, - ...) { - -geneSets <- lapply(genes, geneSetAsList) +calculateSamsscores <- function( + xp, + genes, + raw = FALSE, + which_col = "score_sum", + center_rows = TRUE, + geom_mean = FALSE, + ... +) { + geneSets <- lapply(genes, geneSetAsList) -samsscore <- lapply(geneSets, function(x) Samsscore(xp, x, - center_rows = center_rows, - geom_mean = geom_mean, - ...)) -if (raw) return(samsscore) + samsscore <- lapply(geneSets, function(x) { + Samsscore(xp, x, center_rows = center_rows, geom_mean = geom_mean, ...) + }) + if (raw) { + return(samsscore) + } -my_flat <- function(x) { - my_tib <- dplyr::tibble(sample = rownames(samsscore[[x]]), - !!x := samsscore[[x]][, which_col]) - my_tib -} + my_flat <- function(x) { + my_tib <- dplyr::tibble( + sample = rownames(samsscore[[x]]), + !!x := samsscore[[x]][, which_col] + ) + my_tib + } -score_flat <- lapply(names(samsscore), my_flat) + score_flat <- lapply(names(samsscore), my_flat) -score_flat_join <- Reduce(function(...) dplyr::full_join(..., by = "sample"), score_flat) + score_flat_join <- Reduce( + function(...) dplyr::full_join(..., by = "sample"), + score_flat + ) -ret_val <- score_flat_join %>% data.table::as.data.table() %>% - data.table::melt(id.vars = "sample", variable.name = "geneset", value.name = "score") %>% - data.table::dcast(geneset~sample, value.var = "score") %>% - as.matrix(rownames = "geneset") -ret_val[, colnames(xp), drop = FALSE] + ret_val <- score_flat_join %>% + data.table::as.data.table() %>% + data.table::melt( + id.vars = "sample", + variable.name = "geneset", + value.name = "score" + ) %>% + data.table::dcast(geneset ~ sample, value.var = "score") %>% + as.matrix(rownames = "geneset") + return(ret_val[, colnames(xp), drop = FALSE]) } # basic implementation of samsscore -sams_score <- function(expr_mat, - up_genes = character(), - down_genes = character(), - center_rows = TRUE, - geom_mean = FALSE, - method = "sams") { # method "sams" or "median", median is just the column median +sams_score <- function( + expr_mat, + up_genes = character(), + down_genes = character(), + center_rows = TRUE, + geom_mean = FALSE, + method = "sams" +) { + # method "sams" or "median", median is just the column median expr_mat <- as.matrix(expr_mat) - assertthat::assert_that(is.numeric(expr_mat), msg = "expression matix is not numeric") - - assertthat::assert_that(all(up_genes %in% rownames(expr_mat)), - msg = "At least one element in up_genes is not in rownames of expression matrix.") - assertthat::assert_that(all(down_genes %in% rownames(expr_mat)), - msg = "At least one element down_genes is not in rownames of expression matrix.") + assertthat::assert_that( + is.numeric(expr_mat), + msg = "expression matix is not numeric" + ) + + assertthat::assert_that( + all(up_genes %in% rownames(expr_mat)), + msg = "At least one element in up_genes is not in rownames of expression matrix." + ) + assertthat::assert_that( + all(down_genes %in% rownames(expr_mat)), + msg = "At least one element down_genes is not in rownames of expression matrix." + ) if (center_rows) { if (geom_mean) { row_means <- apply(expr_mat, 1, gm_mean_simple) } else { row_means <- rowMeans(expr_mat, na.rm = TRUE) } - expr_mat <- sweep(expr_mat, 1, row_means, "-") + expr_mat <- sweep(expr_mat, 1, row_means, "-") } ## select data for signature - up_matrix <- expr_mat[up_genes, ] + up_matrix <- expr_mat[up_genes, ] down_matrix <- expr_mat[down_genes, ] ##set values, which do not follow up and down-reglation expection to 0 - up_cens <- up_matrix - up_cens[up_matrix < 0] <- matrix(0, nrow = nrow(up_matrix), ncol = ncol(up_matrix))[up_matrix < 0] + up_cens <- up_matrix + up_cens[up_matrix < 0] <- matrix( + 0, + nrow = nrow(up_matrix), + ncol = ncol(up_matrix) + )[up_matrix < 0] down_cens <- down_matrix - down_cens[down_matrix > 0] <- matrix(0, nrow = nrow(down_matrix), ncol = ncol(down_matrix))[down_matrix > 0] + down_cens[down_matrix > 0] <- matrix( + 0, + nrow = nrow(down_matrix), + ncol = ncol(down_matrix) + )[down_matrix > 0] ##calculate fraction of up and down-regulated genes #up_fraction <- apply(up_matrix, 2, function(x) sum(x >= 0) / length(x)) - up_fraction <- apply(up_matrix, 2, function(x) sum(x > 0) / length(x)) + up_fraction <- apply(up_matrix, 2, function(x) sum(x > 0) / length(x)) down_fraction <- apply(down_matrix, 2, function(x) sum(x < 0) / length(x)) - up_score <- colSums(up_cens) * up_fraction + up_score <- colSums(up_cens) * up_fraction down_score <- colSums(down_cens) * down_fraction - if(method == "median") { - up_score <- apply(up_matrix, 2, median, na.rm = TRUE) + if (method == "median") { + up_score <- apply(up_matrix, 2, median, na.rm = TRUE) down_score <- apply(down_matrix, 2, median, na.rm = TRUE) } my_score_mat <- cbind(up_score = up_score, down_score = down_score) - my_row_sums <- rowSums(abs(my_score_mat), na.rm = TRUE) + my_row_sums <- rowSums(abs(my_score_mat), na.rm = TRUE) - if(method == "median") { - my_score_mat_mod <- cbind(up_score = up_score, down_score = down_score * (-1)) - my_row_sums <- rowSums(my_score_mat_mod, na.rm = TRUE) + if (method == "median") { + my_score_mat_mod <- cbind( + up_score = up_score, + down_score = down_score * (-1) + ) + my_row_sums <- rowSums(my_score_mat_mod, na.rm = TRUE) } both_missing <- apply(my_score_mat, 1, function(x) all(is.na(x))) @@ -376,57 +473,87 @@ checkSignatures <- function(xp, genes) { genes.inter <- lapply(g.v, intersect, genes.xp) genes.length <- unlist(lapply(genes.diff, length)) - genes.diff <- lapply(genes.diff, paste0, collapse=", ") + genes.diff <- lapply(genes.diff, paste0, collapse = ", ") genes.diff <- unlist(genes.diff) - genes.inter <- lapply(genes.inter, paste0, collapse=", ") + genes.inter <- lapply(genes.inter, paste0, collapse = ", ") genes.inter <- unlist(genes.inter) l <- unlist(lapply(g.v, length)) - ret.df <- tibble(geneset=names(g.v), - size.geneset=l, - size.missing=genes.length, - size.left=l-genes.length, - genes.missing=genes.diff, - genes.used=genes.inter) + ret.df <- tibble( + geneset = names(g.v), + size.geneset = l, + size.missing = genes.length, + size.left = l - genes.length, + genes.missing = genes.diff, + genes.used = genes.inter + ) return(ret.df) } ## signature overlap warning lengthWarning <- function(x) { - warning("Geneset: ", x[1], ": ", x[3], " of ", x[2], " genes missing: ", x[5], call.=F) + warning( + "Geneset: ", + x[1], + ": ", + x[3], + " of ", + x[2], + " genes missing: ", + x[5], + call. = F + ) } ## global wrapper function -calculateSignatureScores <- function(xp, genes, method, phenotypes=NULL, case=NULL, control=NULL, raw=FALSE, ...) { +calculateSignatureScores <- function( + xp, + genes, + method, + phenotypes = NULL, + case = NULL, + control = NULL, + raw = FALSE, + ... +) { # Report mismatches between matrix and signatures checks <- checkSignatures(xp, genes) # print(checks) # TODO: new function to send warning - checks.filtered <- checks %>% filter(size.missing>0) + checks.filtered <- checks %>% filter(size.missing > 0) apply(checks.filtered, 1, lengthWarning) - - gene.sets.out <- checks %>% filter(size.left==0) %>% pull(geneset) - if(length(gene.sets.out)>0) warning("The following genesets show no overlap with the expression matrix: " , paste0(gene.sets.out,collapse=", "), call.=F) + gene.sets.out <- checks %>% filter(size.left == 0) %>% pull(geneset) + if (length(gene.sets.out) > 0) { + warning( + "The following genesets show no overlap with the expression matrix: ", + paste0(gene.sets.out, collapse = ", "), + call. = F + ) + } # Keep only genesets with at least 1 gene overlapping - gene.sets.left <- checks %>% filter(size.left>0) %>% pull(geneset) + gene.sets.left <- checks %>% filter(size.left > 0) %>% pull(geneset) genes <- genes[gene.sets.left] # Stop if now genesets are left - if(length(genes)==0) stop("No genesets show an overlap with the expression matrix. Please check if identifiers match.") + if (length(genes) == 0) { + stop( + "No genesets show an overlap with the expression matrix. Please check if identifiers match." + ) + } # Use intersection of gene set with expression matrix for further calculations genes <- lapply(genes, intersect, rownames(xp)) - -# Run actual score calculations -switch(method, + # Run actual score calculations + switch( + method, plage = calculatePLAGEscores(xp, genes, ...), plage.dir = calculatePLAGEscores(xp, genes, correct.dir = TRUE, ...), gsva = calculateGSVAscores(xp, genes, ...), @@ -435,43 +562,55 @@ switch(method, median = calculateMedianScores(xp, genes, ...), mean = calculateMeanScores(xp, genes, ...), sams = calculateSamsscores(xp, genes, ...), - stop(method, " is not a valid method for score calculation.")) + stop(method, " is not a valid method for score calculation.") + ) } ## wrapper for multiple methods multipleSignatureScores <- function(xp, genes, methods) { - stopifnot((is.data.frame(xp) || is.matrix(xp)), - is.list(genes), - length(genes)>0, - is.character(methods), length(methods)>0) - - scores <- lapply(methods, function(x) calculateSignatureScores(xp, genes, x)) + stopifnot( + (is.data.frame(xp) || is.matrix(xp)), + is.list(genes), + length(genes) > 0, + is.character(methods), + length(methods) > 0 + ) + + scores <- lapply(methods, function(x) { + calculateSignatureScores(xp, genes, x) + }) names(scores) <- methods return(scores) } - ## Check consistency across scores # add suffix to rownames -reformatRownames <- function(x, n){ - rownames(x) <- rownames(x) %>% str_replace_all("\\s|\\-",".") %>% paste0(".",n) +reformatRownames <- function(x, n) { + rownames(x) <- rownames(x) %>% + str_replace_all("\\s|\\-", ".") %>% + paste0(".", n) return(x) } # generic function to generate a correlation matrix, preferably based on output of multipleSignatureScores -correlationMatrix <- function(score.list, names.list=NULL) { +correlationMatrix <- function(score.list, names.list = NULL) { stopifnot(is.list(score.list)) - if(is.null(names.list)) names.list <- names(score.list) - stopifnot(length(score.list)==length(names.list)) + if (is.null(names.list)) { + names.list <- names(score.list) + } + stopifnot(length(score.list) == length(names.list)) renamed.score.list <- list() - for(i in 1:length(names.list)) { - renamed.score.list[[i]] <- reformatRownames(score.list[[i]], names.list[i]) + for (i in 1:length(names.list)) { + renamed.score.list[[i]] <- reformatRownames( + score.list[[i]], + names.list[i] + ) } names(renamed.score.list) <- names(score.list) @@ -483,15 +622,36 @@ correlationMatrix <- function(score.list, names.list=NULL) { } # legacy function -correlationMatrixOld <- function(plageScores = NULL, gsvaScores = NULL, ssgseaScores = NULL, paiScores = NULL, singScores = NULL) { - - if(!is.null(plageScores)) plageScores <- reformatRownames(plageScores,"PLAGE") - if(!is.null(gsvaScores)) gsvaScores <- reformatRownames(gsvaScores, "GSVA") - if(!is.null(ssgseaScores)) ssgseaScores <- reformatRownames(ssgseaScores, "SSGSEA") - if(!is.null(paiScores)) paiScores <- reformatRownames(paiScores, "PAI") - if(!is.null(singScores)) singScores <- reformatRownames(singScores, "Singscore") +correlationMatrixOld <- function( + plageScores = NULL, + gsvaScores = NULL, + ssgseaScores = NULL, + paiScores = NULL, + singScores = NULL +) { + if (!is.null(plageScores)) { + plageScores <- reformatRownames(plageScores, "PLAGE") + } + if (!is.null(gsvaScores)) { + gsvaScores <- reformatRownames(gsvaScores, "GSVA") + } + if (!is.null(ssgseaScores)) { + ssgseaScores <- reformatRownames(ssgseaScores, "SSGSEA") + } + if (!is.null(paiScores)) { + paiScores <- reformatRownames(paiScores, "PAI") + } + if (!is.null(singScores)) { + singScores <- reformatRownames(singScores, "Singscore") + } - combined.matrix <- rbind(plageScores, gsvaScores, ssgseaScores, paiScores, singScores) + combined.matrix <- rbind( + plageScores, + gsvaScores, + ssgseaScores, + paiScores, + singScores + ) cor.matrix.all <- cor(t(combined.matrix)) @@ -503,9 +663,9 @@ correlationMatrixOld <- function(plageScores = NULL, gsvaScores = NULL, ssgseaSc ## Boxplots -boxplotScores <- function(score.matrix, set, group, title="") { - bp.df <- data.frame(score=score.matrix[set,], group=group) - ggplot(bp.df, aes(x=group, y=score)) + +boxplotScores <- function(score.matrix, set, group, title = "") { + bp.df <- data.frame(score = score.matrix[set, ], group = group) + ggplot(bp.df, aes(x = group, y = score)) + geom_boxplot() + geom_point() + ggtitle(title) @@ -516,9 +676,12 @@ sessionInfo() ####Commandline Argument parsing### -args = commandArgs(trailingOnly=TRUE) +args = commandArgs(trailingOnly = TRUE) if (length(args) < 3) { - stop("Usage: process_nanostring_data.R algorithm", call.=FALSE) + stop( + "Usage: process_nanostring_data.R algorithm", + call. = FALSE + ) } ####Uncomment if debugging @@ -530,7 +693,7 @@ score.method <- args[3] yaml_parsed <- read_yaml(gene_sets_to_compute) #Get GEX into shape for Nanostring pipeline -counts <- read.table(counts_input, sep="\t", check.names=F, header=T) %>% +counts <- read.table(counts_input, sep = "\t", check.names = F, header = T) %>% filter(`CodeClass` == "Endogenous") %>% select(-`CodeClass`) %>% column_to_rownames("Name") @@ -540,52 +703,61 @@ cs <- checkSignatures(counts, yaml_parsed) #Write to multiQC output -qc.file="signature_scores_qc_mqc.txt" -line="# id: nf-core-nanoflow-signature-score-qc +qc.file = "signature_scores_qc_mqc.txt" +line = "# id: nf-core-nanoflow-signature-score-qc # section_name: 'Signature Score QC' # description: 'Compare Signatures to Expression Matrix' # plot_type: 'table' # section_href: 'https://github.com/nf-core/nanoflow'" -write(line, - file = qc.file) -write.table(cs, - file = qc.file, - append=TRUE, - sep="\t", - row.names = FALSE, - quote = FALSE, - na="" - ) +write(line, file = qc.file) +write.table( + cs, + file = qc.file, + append = TRUE, + sep = "\t", + row.names = FALSE, + quote = FALSE, + na = "" +) ## Compute scores we need in our case -scores <- calculateSignatureScores(xp = counts, genes = yaml_parsed, method=score.method) +scores <- calculateSignatureScores( + xp = counts, + genes = yaml_parsed, + method = score.method +) s.r <- 1 s.c <- 1 -if(ncol(scores)>1) { +if (ncol(scores) > 1) { hc <- hclust(dist(t(scores))) s.c <- hc$order } -if(nrow(scores)>1) { +if (nrow(scores) > 1) { hc.r <- hclust(dist(scores)) s.r <- hc.r$order } -scores.df <- as.data.frame.matrix(t(scores[s.r,s.c])) +scores.df <- as.data.frame.matrix(t(scores[s.r, s.c])) scores.df <- rownames_to_column(scores.df, "sample") #Write to multiQC output -score.file="signature_scores_mqc.txt" -line=paste0("# id: nf-core-nanoflow-signature-score +score.file = "signature_scores_mqc.txt" +line = paste0( + "# id: nf-core-nanoflow-signature-score # section_name: 'Signature Scores' -# description: 'Signature Scores: Algorithm: ",score.method,"' +# description: 'Signature Scores: Algorithm: ", + score.method, + "' # plot_type: 'heatmap' -# section_href: 'https://github.com/nf-core/nanoflow'") +# section_href: 'https://github.com/nf-core/nanoflow'" +) write(line, file = score.file) -write.table(scores.df, - file = score.file, - append=TRUE, - sep="\t", - row.names = FALSE, - quote = FALSE, - na="" +write.table( + scores.df, + file = score.file, + append = TRUE, + sep = "\t", + row.names = FALSE, + quote = FALSE, + na = "" ) diff --git a/modules/local/compute_gene_scores/tests/main.nf.test.snap b/modules/local/compute_gene_scores/tests/main.nf.test.snap index 34ae36e..f326c8b 100644 --- a/modules/local/compute_gene_scores/tests/main.nf.test.snap +++ b/modules/local/compute_gene_scores/tests/main.nf.test.snap @@ -25,7 +25,7 @@ ] ], "1": [ - "versions.yml:md5,267e6a81e12563418edabbdaa1a5ef79" + "versions.yml:md5,e3e9c4a42988d39a5d64477ffb44b256" ], "scores_for_mqc": [ [ @@ -39,14 +39,14 @@ ] ], "versions": [ - "versions.yml:md5,267e6a81e12563418edabbdaa1a5ef79" + "versions.yml:md5,e3e9c4a42988d39a5d64477ffb44b256" ] } ], "meta": { - "nf-test": "0.9.2", - "nextflow": "25.04.6" + "nf-test": "0.9.3", + "nextflow": "25.10.3" }, - "timestamp": "2025-09-03T18:36:23.840868438" + "timestamp": "2026-01-30T11:10:38.397673505" } } \ No newline at end of file diff --git a/modules/local/create_annotated_tables/environment.yml b/modules/local/create_annotated_tables/environment.yml index 6af817c..9ed7f81 100644 --- a/modules/local/create_annotated_tables/environment.yml +++ b/modules/local/create_annotated_tables/environment.yml @@ -2,7 +2,7 @@ channels: - conda-forge dependencies: - conda-forge::r-dplyr=1.1.4 - - conda-forge::r-ggplot2=3.4.4 - - conda-forge::r-readr=2.1.5 - - conda-forge::r-stringr=1.5.0 - - conda-forge::r-tidyr=1.3.0 + - conda-forge::r-ggplot2=4.0.1 + - conda-forge::r-readr=2.1.6 + - conda-forge::r-stringr=1.6.0 + - conda-forge::r-tidyr=1.3.2 diff --git a/modules/local/create_annotated_tables/main.nf b/modules/local/create_annotated_tables/main.nf index 82fbf21..c17c425 100644 --- a/modules/local/create_annotated_tables/main.nf +++ b/modules/local/create_annotated_tables/main.nf @@ -3,7 +3,10 @@ process CREATE_ANNOTATED_TABLES { label 'process_single' conda "${moduleDir}/environment.yml" - container "community.wave.seqera.io/library/r-dplyr_r-ggplot2_r-readr_r-stringr_r-tidyr:44c4e4fe69e11c2f" + + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/56/56c5ac7d61c88a64dc7d6f047e92fbcafbfa271a2df451181f3bac6addcc58b3/data' : + 'community.wave.seqera.io/library/r-dplyr_r-ggplot2_r-readr_r-stringr_r-tidyr:48d97bd8e8272dbe' }" input: tuple val(meta) , path(counts) diff --git a/modules/local/create_annotated_tables/tests/main.nf.test.snap b/modules/local/create_annotated_tables/tests/main.nf.test.snap index b1e7225..09a2f50 100644 --- a/modules/local/create_annotated_tables/tests/main.nf.test.snap +++ b/modules/local/create_annotated_tables/tests/main.nf.test.snap @@ -30,7 +30,7 @@ ] ], "3": [ - "versions.yml:md5,43b871def59fb08862ac85e636101fe5" + "versions.yml:md5,5ac05a1939c1be596bd4432881927766" ], "annotated_data_mqc": [ [ @@ -60,14 +60,14 @@ ] ], "versions": [ - "versions.yml:md5,43b871def59fb08862ac85e636101fe5" + "versions.yml:md5,5ac05a1939c1be596bd4432881927766" ] } ], "meta": { - "nf-test": "0.9.2", - "nextflow": "25.04.6" + "nf-test": "0.9.3", + "nextflow": "25.10.3" }, - "timestamp": "2025-08-27T15:19:33.731479441" + "timestamp": "2026-01-30T11:10:55.699389493" } } \ No newline at end of file diff --git a/modules/local/create_gene_heatmap/environment.yml b/modules/local/create_gene_heatmap/environment.yml index 57bb8d4..cf7d0d8 100644 --- a/modules/local/create_gene_heatmap/environment.yml +++ b/modules/local/create_gene_heatmap/environment.yml @@ -3,10 +3,10 @@ channels: - bioconda dependencies: - conda-forge::r-dplyr=1.1.4 - - conda-forge::r-ggplot2=3.4.4 - - conda-forge::r-rlang=1.1.1 - - conda-forge::r-fs=1.6.4 - - bioconda::bioconductor-complexheatmap=2.14.0 - - conda-forge::r-circlize=0.4.15 - - conda-forge::r-yaml=2.3.8 - - conda-forge::r-ragg=1.3.1 + - conda-forge::r-ggplot2=4.0.1 + - conda-forge::r-rlang=1.1.7 + - conda-forge::r-fs=1.6.6 + - bioconda::bioconductor-complexheatmap=2.22.0 + - conda-forge::r-circlize=0.4.17 + - conda-forge::r-yaml=2.3.12 + - conda-forge::r-ragg=1.5.0 diff --git a/modules/local/create_gene_heatmap/main.nf b/modules/local/create_gene_heatmap/main.nf index a1e69c5..2a4cf6a 100644 --- a/modules/local/create_gene_heatmap/main.nf +++ b/modules/local/create_gene_heatmap/main.nf @@ -2,7 +2,9 @@ process CREATE_GENE_HEATMAP { label 'process_single' conda "${moduleDir}/environment.yml" - container "community.wave.seqera.io/library/bioconductor-complexheatmap_r-base_r-circlize_r-dplyr_pruned:58d1af3dbaeba617" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/6e/6ea309ef90d7c4bc0ca890540b6ca67e08099a52a88d5142570a55a7ffc0897b/data' : + 'community.wave.seqera.io/library/bioconductor-complexheatmap_r-circlize_r-dplyr_r-fs_pruned:c46ed9d375ae22ce' }" input: tuple val(meta), path(annotated_endo_data), path(normalized_counts) diff --git a/modules/local/create_gene_heatmap/tests/main.nf.test.snap b/modules/local/create_gene_heatmap/tests/main.nf.test.snap index f918465..28c1b59 100644 --- a/modules/local/create_gene_heatmap/tests/main.nf.test.snap +++ b/modules/local/create_gene_heatmap/tests/main.nf.test.snap @@ -5,14 +5,14 @@ "gene_heatmap_mqc.png" ], [ - "versions.yml:md5,ce9a0b1b0e476543f0a3ff5b93f6570b" + "versions.yml:md5,c3a817ba44cb542fd95b1d3fc8aac36d" ] ], "meta": { - "nf-test": "0.9.0", - "nextflow": "24.10.1" + "nf-test": "0.9.3", + "nextflow": "25.10.3" }, - "timestamp": "2024-11-27T17:18:59.654958996" + "timestamp": "2026-01-30T11:11:20.305619188" }, "stub": { "content": [ @@ -20,7 +20,7 @@ "gene_heatmap_mqc.png" ], [ - "versions.yml:md5,ce9a0b1b0e476543f0a3ff5b93f6570b" + "versions.yml:md5,c3a817ba44cb542fd95b1d3fc8aac36d" ] ], "meta": { @@ -35,7 +35,7 @@ "gene_heatmap_mqc.png" ], [ - "versions.yml:md5,ce9a0b1b0e476543f0a3ff5b93f6570b" + "versions.yml:md5,c3a817ba44cb542fd95b1d3fc8aac36d" ] ], "meta": { diff --git a/nextflow.config b/nextflow.config index be31553..abd2f16 100644 --- a/nextflow.config +++ b/nextflow.config @@ -310,7 +310,7 @@ manifest { mainScript = 'main.nf' defaultBranch = 'master' nextflowVersion = '!>=25.10.2' - version = '1.3.2' + version = '1.3.3' doi = '' } diff --git a/ro-crate-metadata.json b/ro-crate-metadata.json index 427db32..c818401 100644 --- a/ro-crate-metadata.json +++ b/ro-crate-metadata.json @@ -23,7 +23,7 @@ "@type": "Dataset", "creativeWorkStatus": "Stable", "datePublished": "2026-01-13T18:19:48+00:00", - "description": "

\n \n \n \"nf-core/nanostring\"\n \n

\n\n[![Open in GitHub Codespaces](https://img.shields.io/badge/Open_In_GitHub_Codespaces-black?labelColor=grey&logo=github)](https://github.com/codespaces/new/nf-core/nanostring)\n[![GitHub Actions CI Status](https://github.com/nf-core/nanostring/actions/workflows/nf-test.yml/badge.svg)](https://github.com/nf-core/nanostring/actions/workflows/nf-test.yml)\n[![GitHub Actions Linting Status](https://github.com/nf-core/nanostring/actions/workflows/linting.yml/badge.svg)](https://github.com/nf-core/nanostring/actions/workflows/linting.yml)[![AWS CI](https://img.shields.io/badge/CI%20tests-full%20size-FF9900?labelColor=000000&logo=Amazon%20AWS)](https://nf-co.re/nanostring/results)\n[![nf-test](https://img.shields.io/badge/unit_tests-nf--test-337ab7.svg)](https://www.nf-test.com)\n[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.8028303-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.8028303)\n\n[![Nextflow](https://img.shields.io/badge/version-%E2%89%A525.04.0-green?style=flat&logo=nextflow&logoColor=white&color=%230DC09D&link=https%3A%2F%2Fnextflow.io)](https://www.nextflow.io/)\n[![nf-core template version](https://img.shields.io/badge/nf--core_template-3.5.1-green?style=flat&logo=nfcore&logoColor=white&color=%2324B064&link=https%3A%2F%2Fnf-co.re)](https://github.com/nf-core/tools/releases/tag/3.5.1)\n[![run with conda](http://img.shields.io/badge/run%20with-conda-3EB049?labelColor=000000&logo=anaconda)](https://docs.conda.io/en/latest/)\n[![run with docker](https://img.shields.io/badge/run%20with-docker-0db7ed?labelColor=000000&logo=docker)](https://www.docker.com/)\n[![run with singularity](https://img.shields.io/badge/run%20with-singularity-1d355c.svg?labelColor=000000)](https://sylabs.io/docs/)\n[![Launch on Seqera Platform](https://img.shields.io/badge/Launch%20%F0%9F%9A%80-Seqera%20Platform-%234256e7)](https://cloud.seqera.io/launch?pipeline=https://github.com/nf-core/nanostring)\n\n[![Get help on Slack](http://img.shields.io/badge/slack-nf--core%20%23nanostring-4A154B?labelColor=000000&logo=slack)](https://nfcore.slack.com/channels/nanostring)[![Follow on Bluesky](https://img.shields.io/badge/bluesky-%40nf__core-1185fe?labelColor=000000&logo=bluesky)](https://bsky.app/profile/nf-co.re)[![Follow on Mastodon](https://img.shields.io/badge/mastodon-nf__core-6364ff?labelColor=FFFFFF&logo=mastodon)](https://mstdn.science/@nf_core)[![Watch on YouTube](http://img.shields.io/badge/youtube-nf--core-FF0000?labelColor=000000&logo=youtube)](https://www.youtube.com/c/nf-core)\n\n## Introduction\n\n**nf-core/nanostring** is a bioinformatics pipeline that can be used to analyze NanoString data. The performed analysis steps include quality control and data normalization.\n\nThe pipeline is built using [Nextflow](https://www.nextflow.io), a workflow tool to run tasks across multiple compute infrastructures in a very portable manner. It uses Docker/Singularity containers making installation trivial and results highly reproducible. The [Nextflow DSL2](https://www.nextflow.io/docs/latest/dsl2.html) implementation of this pipeline uses one container per process which makes it much easier to maintain and update software dependencies. Where possible, these processes have been submitted to and installed from [nf-core/modules](https://github.com/nf-core/modules) in order to make them available to all nf-core pipelines, and to everyone within the Nextflow community!\n\n## Pipeline summary\n\n1. Quality control with NACHO ([`NACHO`](https://github.com/mcanouil/NACHO/))\n2. Perform normalization with NACHO\n3. Create count tables with provided metadata\n4. Present QC for NanoString data ([`MultiQC`](http://multiqc.info/))\n\n## Pipeline tubemap\n\n![](./assets/nf-core_nanostring_tubemap.png)\n\n## Usage\n\n> [!NOTE]\n> If you are new to Nextflow and nf-core, please refer to [this page](https://nf-co.re/docs/usage/installation) on how\n> to set-up Nextflow. Make sure to [test your setup](https://nf-co.re/docs/usage/introduction#how-to-run-a-pipeline)\n> with `-profile test` before running the workflow on actual data.\n\nFirst, prepare a samplesheet with your input data that looks as follows:\n\n`samplesheet.csv`:\n\n```csv\nRCC_FILE,RCC_FILE_NAME,SAMPLE_ID\n/path/to/sample1.RCC,sample1.RCC,sample1\n/path/to/sample2.RCC,sample2.RCC,sample2\n```\n\nEach row represents a RCC file with counts.\n\nNow, you can run the pipeline using:\n\n```bash\nnextflow run nf-core/nanostring \\\n -profile \\\n --input samplesheet.csv \\\n --outdir \n```\n\n> [!WARNING]\n> Please provide pipeline parameters via the CLI or Nextflow `-params-file` option. Custom config files including those provided by the `-c` Nextflow option can be used to provide any configuration _**except for parameters**_; see [docs](https://nf-co.re/docs/usage/getting_started/configuration#custom-configuration-files).\n\nFor more details and further functionality, please refer to the [usage documentation](https://nf-co.re/nanostring/usage) and the [parameter documentation](https://nf-co.re/nanostring/parameters).\n\n## Pipeline output\n\nTo see the results of an example test run with a full size dataset refer to the [results](https://nf-co.re/nanostring/results) tab on the nf-core website pipeline page.\nFor more details about the output files and reports, please refer to the\n[output documentation](https://nf-co.re/nanostring/output).\n\n## Credits\n\nnf-core/nanostring was originally written by Peltzer, Alexander & Mohr, Christopher. Extensive support was provided from other co-authors on the scientific or technical input required for the pipeline:\n\n- Stadermann, Kai\n- Zwick, Matthias\n- Leparc, Germ\u00e1n\n- Schmid, Ramona\n\n## Contributions and Support\n\nIf you would like to contribute to this pipeline, please see the [contributing guidelines](.github/CONTRIBUTING.md).\n\nFor further information or help, don't hesitate to get in touch on the [Slack `#nanostring` channel](https://nfcore.slack.com/channels/nanostring) (you can join with [this invite](https://nf-co.re/join/slack)).\n\n## Citations\n\nIf you use nf-core/nanostring for your analysis, please cite the publication: [nf-core/nanostring Bioinformatics](https://academic.oup.com/bioinformatics/advance-article/doi/10.1093/bioinformatics/btae019/7517109) and the [Zenodo DOI](https://doi.org/10.5281/zenodo.8028303).\n\nAn extensive list of references for the tools used by the pipeline can be found in the [`CITATIONS.md`](CITATIONS.md) file.\n\nYou can cite the `nf-core` publication as follows:\n\n> **The nf-core framework for community-curated bioinformatics pipelines.**\n>\n> Philip Ewels, Alexander Peltzer, Sven Fillinger, Harshil Patel, Johannes Alneberg, Andreas Wilm, Maxime Ulysse Garcia, Paolo Di Tommaso & Sven Nahnsen.\n>\n> _Nat Biotechnol._ 2020 Feb 13. doi: [10.1038/s41587-020-0439-x](https://dx.doi.org/10.1038/s41587-020-0439-x).\n", + "description": "

\n \n \n \"nf-core/nanostring\"\n \n

\n\n[![Open in GitHub Codespaces](https://img.shields.io/badge/Open_In_GitHub_Codespaces-black?labelColor=grey&logo=github)](https://github.com/codespaces/new/nf-core/nanostring)\n[![GitHub Actions CI Status](https://github.com/nf-core/nanostring/actions/workflows/nf-test.yml/badge.svg)](https://github.com/nf-core/nanostring/actions/workflows/nf-test.yml)\n[![GitHub Actions Linting Status](https://github.com/nf-core/nanostring/actions/workflows/linting.yml/badge.svg)](https://github.com/nf-core/nanostring/actions/workflows/linting.yml)[![AWS CI](https://img.shields.io/badge/CI%20tests-full%20size-FF9900?labelColor=000000&logo=Amazon%20AWS)](https://nf-co.re/nanostring/results)\n[![nf-test](https://img.shields.io/badge/unit_tests-nf--test-337ab7.svg)](https://www.nf-test.com)\n[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.8028303-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.8028303)\n\n[![Nextflow](https://img.shields.io/badge/version-%E2%89%A525.10.2-green?style=flat&logo=nextflow&logoColor=white&color=%230DC09D&link=https%3A%2F%2Fnextflow.io)](https://www.nextflow.io/)\n[![nf-core template version](https://img.shields.io/badge/nf--core_template-3.5.1-green?style=flat&logo=nfcore&logoColor=white&color=%2324B064&link=https%3A%2F%2Fnf-co.re)](https://github.com/nf-core/tools/releases/tag/3.5.1)\n[![run with conda](http://img.shields.io/badge/run%20with-conda-3EB049?labelColor=000000&logo=anaconda)](https://docs.conda.io/en/latest/)\n[![run with docker](https://img.shields.io/badge/run%20with-docker-0db7ed?labelColor=000000&logo=docker)](https://www.docker.com/)\n[![run with singularity](https://img.shields.io/badge/run%20with-singularity-1d355c.svg?labelColor=000000)](https://sylabs.io/docs/)\n[![Launch on Seqera Platform](https://img.shields.io/badge/Launch%20%F0%9F%9A%80-Seqera%20Platform-%234256e7)](https://cloud.seqera.io/launch?pipeline=https://github.com/nf-core/nanostring)\n\n[![Get help on Slack](http://img.shields.io/badge/slack-nf--core%20%23nanostring-4A154B?labelColor=000000&logo=slack)](https://nfcore.slack.com/channels/nanostring)[![Follow on Bluesky](https://img.shields.io/badge/bluesky-%40nf__core-1185fe?labelColor=000000&logo=bluesky)](https://bsky.app/profile/nf-co.re)[![Follow on Mastodon](https://img.shields.io/badge/mastodon-nf__core-6364ff?labelColor=FFFFFF&logo=mastodon)](https://mstdn.science/@nf_core)[![Watch on YouTube](http://img.shields.io/badge/youtube-nf--core-FF0000?labelColor=000000&logo=youtube)](https://www.youtube.com/c/nf-core)\n\n## Introduction\n\n**nf-core/nanostring** is a bioinformatics pipeline that can be used to analyze NanoString data. The performed analysis steps include quality control and data normalization.\n\nThe pipeline is built using [Nextflow](https://www.nextflow.io), a workflow tool to run tasks across multiple compute infrastructures in a very portable manner. It uses Docker/Singularity containers making installation trivial and results highly reproducible. The [Nextflow DSL2](https://www.nextflow.io/docs/latest/dsl2.html) implementation of this pipeline uses one container per process which makes it much easier to maintain and update software dependencies. Where possible, these processes have been submitted to and installed from [nf-core/modules](https://github.com/nf-core/modules) in order to make them available to all nf-core pipelines, and to everyone within the Nextflow community!\n\n## Pipeline summary\n\n1. Quality control with NACHO ([`NACHO`](https://github.com/mcanouil/NACHO/))\n2. Perform normalization with NACHO\n3. Create count tables with provided metadata\n4. Present QC for NanoString data ([`MultiQC`](http://multiqc.info/))\n\n## Pipeline tubemap\n\n![](./assets/nf-core_nanostring_tubemap.png)\n\n## Usage\n\n> [!NOTE]\n> If you are new to Nextflow and nf-core, please refer to [this page](https://nf-co.re/docs/usage/installation) on how\n> to set-up Nextflow. Make sure to [test your setup](https://nf-co.re/docs/usage/introduction#how-to-run-a-pipeline)\n> with `-profile test` before running the workflow on actual data.\n\nFirst, prepare a samplesheet with your input data that looks as follows:\n\n`samplesheet.csv`:\n\n```csv\nRCC_FILE,RCC_FILE_NAME,SAMPLE_ID\n/path/to/sample1.RCC,sample1.RCC,sample1\n/path/to/sample2.RCC,sample2.RCC,sample2\n```\n\nEach row represents a RCC file with counts.\n\nNow, you can run the pipeline using:\n\n```bash\nnextflow run nf-core/nanostring \\\n -profile \\\n --input samplesheet.csv \\\n --outdir \n```\n\n> [!WARNING]\n> Please provide pipeline parameters via the CLI or Nextflow `-params-file` option. Custom config files including those provided by the `-c` Nextflow option can be used to provide any configuration _**except for parameters**_; see [docs](https://nf-co.re/docs/usage/getting_started/configuration#custom-configuration-files).\n\nFor more details and further functionality, please refer to the [usage documentation](https://nf-co.re/nanostring/usage) and the [parameter documentation](https://nf-co.re/nanostring/parameters).\n\n## Pipeline output\n\nTo see the results of an example test run with a full size dataset refer to the [results](https://nf-co.re/nanostring/results) tab on the nf-core website pipeline page.\nFor more details about the output files and reports, please refer to the\n[output documentation](https://nf-co.re/nanostring/output).\n\n## Credits\n\nnf-core/nanostring was originally written by Peltzer, Alexander & Mohr, Christopher. Extensive support was provided from other co-authors on the scientific or technical input required for the pipeline:\n\n- Stadermann, Kai\n- Zwick, Matthias\n- Leparc, Germ\u00e1n\n- Schmid, Ramona\n\n## Contributions and Support\n\nIf you would like to contribute to this pipeline, please see the [contributing guidelines](.github/CONTRIBUTING.md).\n\nFor further information or help, don't hesitate to get in touch on the [Slack `#nanostring` channel](https://nfcore.slack.com/channels/nanostring) (you can join with [this invite](https://nf-co.re/join/slack)).\n\n## Citations\n\nIf you use nf-core/nanostring for your analysis, please cite the publication: [nf-core/nanostring Bioinformatics](https://academic.oup.com/bioinformatics/advance-article/doi/10.1093/bioinformatics/btae019/7517109) and the [Zenodo DOI](https://doi.org/10.5281/zenodo.8028303).\n\nAn extensive list of references for the tools used by the pipeline can be found in the [`CITATIONS.md`](CITATIONS.md) file.\n\nYou can cite the `nf-core` publication as follows:\n\n> **The nf-core framework for community-curated bioinformatics pipelines.**\n>\n> Philip Ewels, Alexander Peltzer, Sven Fillinger, Harshil Patel, Johannes Alneberg, Andreas Wilm, Maxime Ulysse Garcia, Paolo Di Tommaso & Sven Nahnsen.\n>\n> _Nat Biotechnol._ 2020 Feb 13. doi: [10.1038/s41587-020-0439-x](https://dx.doi.org/10.1038/s41587-020-0439-x).\n", "hasPart": [ { "@id": "main.nf" diff --git a/subworkflows/local/compute_gene_scores_heatmap/tests/main.nf.test.snap b/subworkflows/local/compute_gene_scores_heatmap/tests/main.nf.test.snap index 018553a..65c8900 100644 --- a/subworkflows/local/compute_gene_scores_heatmap/tests/main.nf.test.snap +++ b/subworkflows/local/compute_gene_scores_heatmap/tests/main.nf.test.snap @@ -2,8 +2,8 @@ "versions": { "content": [ [ - "versions.yml:md5,63b539ddb8d646a7cf480f3de2e47100", - "versions.yml:md5,6fac9e04824b7752f45abd6b1d4ef0a9" + "versions.yml:md5,39e2f90ec64507b913750e195418b2f8", + "versions.yml:md5,fada5495f572217c5a3bf98050dba293" ] ], "meta": { @@ -16,8 +16,8 @@ "content": [ { "0": [ - "versions.yml:md5,63b539ddb8d646a7cf480f3de2e47100", - "versions.yml:md5,6fac9e04824b7752f45abd6b1d4ef0a9" + "versions.yml:md5,39e2f90ec64507b913750e195418b2f8", + "versions.yml:md5,fada5495f572217c5a3bf98050dba293" ], "1": [ [ @@ -38,8 +38,8 @@ ] ], "versions": [ - "versions.yml:md5,63b539ddb8d646a7cf480f3de2e47100", - "versions.yml:md5,6fac9e04824b7752f45abd6b1d4ef0a9" + "versions.yml:md5,39e2f90ec64507b913750e195418b2f8", + "versions.yml:md5,fada5495f572217c5a3bf98050dba293" ] } ], @@ -66,8 +66,8 @@ "versions_filter": { "content": [ [ - "versions.yml:md5,63b539ddb8d646a7cf480f3de2e47100", - "versions.yml:md5,6fac9e04824b7752f45abd6b1d4ef0a9" + "versions.yml:md5,39e2f90ec64507b913750e195418b2f8", + "versions.yml:md5,fada5495f572217c5a3bf98050dba293" ] ], "meta": { diff --git a/subworkflows/nf-core/utils_nfschema_plugin/main.nf b/subworkflows/nf-core/utils_nfschema_plugin/main.nf index acb3972..1df8b76 100644 --- a/subworkflows/nf-core/utils_nfschema_plugin/main.nf +++ b/subworkflows/nf-core/utils_nfschema_plugin/main.nf @@ -38,7 +38,7 @@ workflow UTILS_NFSCHEMA_PLUGIN { } log.info paramsHelp( help_options, - params.help instanceof String ? params.help : "", + (params.help instanceof String && params.help != "true") ? params.help : "", ) exit 0 } diff --git a/tests/default.nf.test.snap b/tests/default.nf.test.snap index 8185331..4a7c359 100644 --- a/tests/default.nf.test.snap +++ b/tests/default.nf.test.snap @@ -3,20 +3,20 @@ "content": [ { "CREATE_ANNOTATED_TABLES": { - "r-base": "4.3.3", + "r-base": "4.5.2", "r-ggplot2": null, "r-dplyr": "1.1.4", - "r-readr": "2.1.5" + "r-readr": "2.1.6" }, "CREATE_GENE_HEATMAP": { - "r-base": "4.2.3", + "r-base": "4.4.3", "r-dplyr": "1.1.4", - "r-ggplot2": "3.4.4", - "r-rlang": "1.1.1", - "bioconductor-ComplexHeatmap": "2.14.0", - "r-circlize": "0.4.15", - "r-yaml": "2.3.8", - "r-fs": "1.6.4" + "r-ggplot2": "4.0.1", + "r-rlang": "1.1.7", + "bioconductor-ComplexHeatmap": "2.22.0", + "r-circlize": "0.4.17", + "r-yaml": "2.3.12", + "r-fs": "1.6.6" }, "NACHO_NORMALIZE": { "r-base": "4.5.2", @@ -39,7 +39,7 @@ "r-optparse": "1.7.5" }, "Workflow": { - "nf-core/nanostring": "v1.3.2" + "nf-core/nanostring": "v1.3.3" } }, [ @@ -128,8 +128,8 @@ ], "meta": { "nf-test": "0.9.3", - "nextflow": "25.10.2" + "nextflow": "25.10.3" }, - "timestamp": "2026-01-14T18:49:35.271884067" + "timestamp": "2026-01-30T14:47:50.137973752" } } \ No newline at end of file diff --git a/tests/test.nf.test.snap b/tests/test.nf.test.snap index 18a743f..6afc022 100644 --- a/tests/test.nf.test.snap +++ b/tests/test.nf.test.snap @@ -4,20 +4,20 @@ 7, { "CREATE_ANNOTATED_TABLES": { - "r-base": "4.3.3", + "r-base": "4.5.2", "r-ggplot2": null, "r-dplyr": "1.1.4", - "r-readr": "2.1.5" + "r-readr": "2.1.6" }, "CREATE_GENE_HEATMAP": { - "r-base": "4.2.3", + "r-base": "4.4.3", "r-dplyr": "1.1.4", - "r-ggplot2": "3.4.4", - "r-rlang": "1.1.1", - "bioconductor-ComplexHeatmap": "2.14.0", - "r-circlize": "0.4.15", - "r-yaml": "2.3.8", - "r-fs": "1.6.4" + "r-ggplot2": "4.0.1", + "r-rlang": "1.1.7", + "bioconductor-ComplexHeatmap": "2.22.0", + "r-circlize": "0.4.17", + "r-yaml": "2.3.12", + "r-fs": "1.6.6" }, "NACHO_NORMALIZE": { "r-base": "4.5.2", @@ -40,7 +40,7 @@ "r-optparse": "1.7.5" }, "Workflow": { - "nf-core/nanostring": "v1.3.2" + "nf-core/nanostring": "v1.3.3" } }, [ @@ -129,8 +129,8 @@ ], "meta": { "nf-test": "0.9.3", - "nextflow": "25.10.2" + "nextflow": "25.10.3" }, - "timestamp": "2026-01-14T17:35:43.587464854" + "timestamp": "2026-01-30T14:49:00.042189829" } } \ No newline at end of file diff --git a/tests/test_samples.nf.test.snap b/tests/test_samples.nf.test.snap index def6260..fbde55b 100644 --- a/tests/test_samples.nf.test.snap +++ b/tests/test_samples.nf.test.snap @@ -4,20 +4,20 @@ 7, { "CREATE_ANNOTATED_TABLES": { - "r-base": "4.3.3", + "r-base": "4.5.2", "r-ggplot2": null, "r-dplyr": "1.1.4", - "r-readr": "2.1.5" + "r-readr": "2.1.6" }, "CREATE_GENE_HEATMAP": { - "r-base": "4.2.3", + "r-base": "4.4.3", "r-dplyr": "1.1.4", - "r-ggplot2": "3.4.4", - "r-rlang": "1.1.1", - "bioconductor-ComplexHeatmap": "2.14.0", - "r-circlize": "0.4.15", - "r-yaml": "2.3.8", - "r-fs": "1.6.4" + "r-ggplot2": "4.0.1", + "r-rlang": "1.1.7", + "bioconductor-ComplexHeatmap": "2.22.0", + "r-circlize": "0.4.17", + "r-yaml": "2.3.12", + "r-fs": "1.6.6" }, "NACHO_NORMALIZE": { "r-base": "4.5.2", @@ -40,7 +40,7 @@ "r-optparse": "1.7.5" }, "Workflow": { - "nf-core/nanostring": "v1.3.2" + "nf-core/nanostring": "v1.3.3" } }, [ @@ -129,8 +129,8 @@ ], "meta": { "nf-test": "0.9.3", - "nextflow": "25.10.2" + "nextflow": "25.10.3" }, - "timestamp": "2026-01-14T17:36:55.213351086" + "timestamp": "2026-01-30T14:50:09.323462323" } } \ No newline at end of file diff --git a/tests/test_scores.nf.test.snap b/tests/test_scores.nf.test.snap index 6146c77..3140c2a 100644 --- a/tests/test_scores.nf.test.snap +++ b/tests/test_scores.nf.test.snap @@ -4,30 +4,30 @@ 8, { "COMPUTE_GENE_SCORES": { - "r-base": "4.2.3", + "r-base": "4.4.3", "r-dplyr": "1.1.4", - "r-tibble": "3.2.1", - "r-singscore": "1.18.0", - "r-GSVA": "1.46.0", - "r-yaml": "2.3.7", - "r-FactoMineR": 2.8, - "r-stringr": "1.5.0" + "r-tibble": "3.3.1", + "r-singscore": "1.26.0", + "r-GSVA": "2.0.0", + "r-yaml": "2.3.12", + "r-FactoMineR": 2.13, + "r-stringr": "1.6.0" }, "CREATE_ANNOTATED_TABLES": { - "r-base": "4.3.3", + "r-base": "4.5.2", "r-ggplot2": null, "r-dplyr": "1.1.4", - "r-readr": "2.1.5" + "r-readr": "2.1.6" }, "CREATE_GENE_HEATMAP": { - "r-base": "4.2.3", + "r-base": "4.4.3", "r-dplyr": "1.1.4", - "r-ggplot2": "3.4.4", - "r-rlang": "1.1.1", - "bioconductor-ComplexHeatmap": "2.14.0", - "r-circlize": "0.4.15", - "r-yaml": "2.3.8", - "r-fs": "1.6.4" + "r-ggplot2": "4.0.1", + "r-rlang": "1.1.7", + "bioconductor-ComplexHeatmap": "2.22.0", + "r-circlize": "0.4.17", + "r-yaml": "2.3.12", + "r-fs": "1.6.6" }, "NACHO_NORMALIZE": { "r-base": "4.5.2", @@ -50,7 +50,7 @@ "r-optparse": "1.7.5" }, "Workflow": { - "nf-core/nanostring": "v1.3.2" + "nf-core/nanostring": "v1.3.3" } }, [ @@ -152,8 +152,8 @@ ], "meta": { "nf-test": "0.9.3", - "nextflow": "25.10.2" + "nextflow": "25.10.3" }, - "timestamp": "2026-01-14T17:39:21.947737179" + "timestamp": "2026-01-30T14:51:51.372614059" } } \ No newline at end of file