From 138b2c45dde12b2075d6fe15a5d374b48d166d4a Mon Sep 17 00:00:00 2001 From: Suzanne Jin Date: Wed, 11 Dec 2024 16:08:07 +0100 Subject: [PATCH 1/3] update module propr/grea (#7198) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * delete previous snapshot * [propr/grea] update container and conda environment * [propr/grea] update main.nf output * [propr/grea] copied the updated template from differentialabundance dev-ratio * [propr/grea] added some comments and checks * [propr/grea] add option to round digits * [propr/grea] updated tests, need to solve some problems with gmt filtering * [propr/grea] updated snapshots, but need to check single cpu vs multiprocessor discrepancies * [propr/grea] update snapshots * [propr/grea] update meta.yml * [propr/grea] remove unnecesary config * [propr/grea] solve linting * Apply suggestions from code review Co-authored-by: Júlia Mir Pedrol --------- Co-authored-by: Júlia Mir Pedrol --- modules/nf-core/propr/grea/environment.yml | 4 +- modules/nf-core/propr/grea/main.nf | 14 +- modules/nf-core/propr/grea/meta.yml | 42 ++-- modules/nf-core/propr/grea/templates/grea.R | 223 +++++++++++------- .../nf-core/propr/grea/tests/grea_test.config | 14 +- modules/nf-core/propr/grea/tests/main.nf.test | 54 +++-- .../propr/grea/tests/main.nf.test.snap | 33 ++- 7 files changed, 224 insertions(+), 160 deletions(-) diff --git a/modules/nf-core/propr/grea/environment.yml b/modules/nf-core/propr/grea/environment.yml index 2bb015a1047..9744dab906b 100644 --- a/modules/nf-core/propr/grea/environment.yml +++ b/modules/nf-core/propr/grea/environment.yml @@ -1,5 +1,7 @@ channels: - conda-forge - bioconda + dependencies: - - conda-forge::r-propr=5.0.4 + - bioconda::bioconductor-limma=3.58.1 + - conda-forge::r-propr=5.1.5 diff --git a/modules/nf-core/propr/grea/main.nf b/modules/nf-core/propr/grea/main.nf index d2e1ee6de9a..fd727208d94 100644 --- a/modules/nf-core/propr/grea/main.nf +++ b/modules/nf-core/propr/grea/main.nf @@ -1,20 +1,20 @@ process PROPR_GREA { tag "$meta.id" - label 'process_single' + label 'process_high' conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/r-propr:5.0.4': - 'biocontainers/r-propr:5.0.4' }" + 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/b6/b65f7192866fbd9a947df15b104808abb720e7a224bbe3ca8f7f8f680f52c97a/data' : + 'community.wave.seqera.io/library/bioconductor-limma_r-propr:f52f1d4fea746393' }" input: - tuple val(meta), path(adj) + tuple val(meta), path(adjacency) tuple val(meta2), path(gmt) output: - tuple val(meta), path("*.go.tsv"), emit: enrichedGO - path "versions.yml", emit: versions - path "*.R_sessionInfo.log", emit: session_info + tuple val(meta), path("*.grea.tsv"), emit: results + path "versions.yml", emit: versions + path "*.R_sessionInfo.log", emit: session_info when: task.ext.when == null || task.ext.when diff --git a/modules/nf-core/propr/grea/meta.yml b/modules/nf-core/propr/grea/meta.yml index 58f73fc4d86..cc0613d3dab 100644 --- a/modules/nf-core/propr/grea/meta.yml +++ b/modules/nf-core/propr/grea/meta.yml @@ -2,12 +2,12 @@ name: "propr_grea" description: Perform Gene Ratio Enrichment Analysis keywords: - - logratio - - differential - propr - grea - - enrichment - - expression + - logratio + - differential expression + - functional enrichment + - functional analysis tools: - "grea": description: "Gene Ratio Enrichment Analysis" @@ -21,34 +21,42 @@ input: - - meta: type: map description: | - Groovy Map containing sample information. + Groovy Map containing data information. This can be used at the workflow level to pass optional parameters to the module. [id: 'test', ...] - - adj: + - adjacency: type: file - description: adjacency matrix for gene ratio proportionality/differential proportionality + description: | + Adjacency matrix representing the graph connections (ie. 1 for edges, 0 otherwise). + This can be the adjacency matrix output from gene ratio approaches like propr/propd. pattern: "*.{csv,tsv}" - - meta2: type: map description: | - Groovy map containing study-wide metadata related to the knowledge database + Groovy Map containing data information. + This can be used at the workflow level to pass optional parameters to the module. + [id: 'test', ...] - gmt: type: file - description: relational database containing genes and GO terms (generated by - mygene module) + description: | + A tab delimited file format that describes gene sets. The first column is the + concept id (eg. GO term, pathway, etc), the second column is the concept description, and the + rest are nodes (eg. genes) that is associated to the given concept. pattern: "*.{gmt}" output: - - enrichedGO: + - results: - meta: - type: map + type: file description: | Groovy Map containing sample information. This can be used at the workflow level to pass optional parameters to the module. [id: 'test', ...] - - "*.go.tsv": + - "*.grea.tsv": type: file - description: File containing GO terms and their enrichment values - pattern: "*.{csv}" + description: | + Output file containing the information about the tested concepts (ie. gene sets) + and enrichment statistics. + pattern: "*.grea.tsv" - versions: - versions.yml: type: file @@ -57,9 +65,11 @@ output: - session_info: - "*.R_sessionInfo.log": type: file - description: R session log + description: dump of R SessionInfo pattern: "*.R_sessionInfo.log" authors: - "@caraiz2001" + - "@suzannejin" maintainers: - "@caraiz2001" + - "@suzannejin" diff --git a/modules/nf-core/propr/grea/templates/grea.R b/modules/nf-core/propr/grea/templates/grea.R index 2d568b70330..3b761f89b1c 100644 --- a/modules/nf-core/propr/grea/templates/grea.R +++ b/modules/nf-core/propr/grea/templates/grea.R @@ -51,66 +51,44 @@ read_delim_flexible <- function(file, header = TRUE, row.names = 1, check.names ) } -#' Converts the .gmt file into a df +#' Loads the .gmt file and converts it into a knowledge database #' -#' @param file_gmt_path path of the .gmt file provided by mygene module. -#' @return output dataframe a Dataframe: 1st column = GOterm, 2nd = Description, 3d to end = genes. -process_gmt_file <- function(file_gmt_path) { - - lines <- readLines(file_gmt_path) - data_list <- list() - - for (line in lines) { - fields <- strsplit(line, "\\t")[[1]] # Split the line based on the tab character - go_term <- fields[1] # Extract the GO term - - # Create a data frame with the GO term in the first column - # Fill in missing values with NA to ensure consistent column lengths - data_list[[go_term]] <- data.frame(GOterm = go_term, - Description = fields[2], - GeneIDs = c(fields[3:length(fields)], rep(NA, max(0, 3 - length(fields))))) +#' @param filename path of the .gmt file +#' @param nodes vector of node (eg. gene) names. Note that this set should be as +#' complete as possible. So it should not only contain the target genes but also +#' the background genes. +#' @return a list with: +#' `db` A knowledge database (matrix) where each row is a graph node (eg. gene) +#' and each column is a concept (eg. GO term, pathway, etc). +#' `description` A list of descriptions for each concept. +load_gmt <- function(filename, nodes) { + + # read gmt file + gmt <- readLines(filename) + gmt <- strsplit(gmt, "\\t") + + # initialize database matrix + db <- matrix(0, nrow = length(nodes), ncol = length(gmt)) + rownames(db) <- nodes + colnames(db) <- sapply(gmt, function(entry) entry[[1]]) + + # description of the concepts + description <- list() + + # for concept in gmt + for (i in 1:length(gmt)) { + + # get concept and description + concept <- gmt[[i]][[1]] + description[[concept]] <- gmt[[i]][[2]] + + # fill 1 if gene is in concept + nodes_in_concept <- gmt[[i]][-c(1, 2)] + nodes_in_concept <- nodes_in_concept[nodes_in_concept %in% nodes] + db[nodes_in_concept, i] <- 1 } - gmt_df <- do.call(rbind, data_list) # Combine all data frames into a single data frame - gmt_df\$GeneIDs <- as.character(gmt_df\$GeneIDs) # Convert gene IDs to character to avoid coercion - - return(gmt_df) -} - -#' Converts the .gmt data frame into a knowledge matrix (contingency table) -#' -#' @param gmt_df .gmt df created by process_gmt_file -#' @return output dataframe. A knowledge database where each row is a graph node (gene) -#' and each column is a concept (GO term). -gmt_to_K<- function(gmt_df){ - - summ_df <- as.data.frame(gmt_df\$GeneIDs) - summ_df <- cbind(summ_df, as.data.frame(gmt_df\$GOterm)) - colnames(summ_df)<- c("GeneIDs", "GOterm") - summ_df<- unique(summ_df) - - summ_df\$value <- 1 - - K <- table(summ_df\$GeneIDs, summ_df\$GOterm) - K <- as.data.frame.matrix(K) - - return(K) -} - -#' Expands knowledge matrix with missing genes to ensure same number of rows for A and K -#' -#' @param adjacency_matrix gene x gene correlation or proportionality adjacency matrix (output propr/propd) -#' @return output dataframe. A knowledge database where each row is a graph node (gene) -#' and each column is a concept (GO term). -add_missing <- function(adjacency_matrix, knowledge_matrix){ - - missing_genes <- setdiff(rownames(adjacency_matrix), rownames(knowledge_matrix)) - extra_rows <- data.frame(matrix(0, nrow = length(missing_genes), ncol = ncol(knowledge_matrix))) - rownames(extra_rows) <- missing_genes - colnames(extra_rows) <- colnames(knowledge_matrix) - - knowledge_matrix <- rbind(knowledge_matrix, extra_rows) - return(knowledge_matrix) + return(list(db = db, description = description)) } ################################################ @@ -119,53 +97,70 @@ add_missing <- function(adjacency_matrix, knowledge_matrix){ ################################################ ################################################ +# Set defaults and classes + opt <- list( - adj = '$adj', - gmt = '$gmt', prefix = ifelse('$task.ext.prefix' == 'null', '$meta.id', '$task.ext.prefix'), - permutation = 100, - fixseed = TRUE, + + # input data + adj = '$adjacency', # adjacency matrix + gmt = '$gmt', # knowledge database .gmt file + + # parameters for gene sets + set_min = 15, # minimum number of genes in a set + set_max = 500, # maximum number of genes in a set + + # parameters for permutation test + permutation = 100, # number of permutations to perform + + # other parameters + seed = NA, # seed for reproducibility + round_digits = NA, # number of digits to round results ncores = as.integer('$task.cpus') ) opt_types <- list( + prefix = 'character', adj = 'character', gmt = 'character', - prefix = 'character', + set_min = 'numeric', + set_max = 'numeric', permutation = 'numeric', - fixseed = 'logical', + seed = 'numeric', + round_digits = 'numeric', ncores = 'numeric' ) # Apply parameter overrides -args_opt <- parse_args('$task.ext.args') +args_ext <- ifelse('$task.ext.args' == 'null', '', '$task.ext.args') +args_opt <- parse_args(args_ext) for ( ao in names(args_opt)){ if (! ao %in% names(opt)){ stop(paste("Invalid option:", ao)) } else { # Preserve classes from defaults where possible - if (! is.null(opt[[ao]])){ - args_opt[[ao]] <- as(args_opt[[ao]], opt_types[[ao]]) - } - # set NA - if (args_opt[[ao]] %in% c('NA', NA, 'null')){ - args_opt[[ao]] <- NA - } + args_opt[[ao]] <- as(args_opt[[ao]], opt_types[[ao]]) + + # handle NA, and avoid errors when NA is provided by user as character + if (args_opt[[ao]] %in% c('NA', NA)) args_opt[[ao]] <- NA + + # replace values opt[[ao]] <- args_opt[[ao]] } } # Check if required parameters have been provided + required_opts <- c('adj', 'gmt') # defines a vector required_opts containing the names of the required parameters. missing <- required_opts[unlist(lapply(opt[required_opts], is.null)) | ! required_opts %in% names(opt)] if (length(missing) > 0){ stop(paste("Missing required options:", paste(missing, collapse=', '))) } - # Check file inputs are valid + for (file_input in c('adj', 'gmt')){ if (is.null(opt[[file_input]])) { stop(paste("Please provide", file_input), call. = FALSE) @@ -175,6 +170,12 @@ for (file_input in c('adj', 'gmt')){ } } +# check parameters are valid + +if (opt\$permutation < 0) { + stop('permutation should be a positive integer') +} + ################################################ ################################################ ## Finish loading libraries ## @@ -189,20 +190,63 @@ library(propr) ################################################ ################################################ -# Read gene x gene adjacency matrix -A <- read_delim_flexible(opt\$adj, header = TRUE, row.names = 1, check.names = TRUE) +# set seed when required + +if (!is.na(opt\$seed)) { + warning('Setting seed ', opt\$seed, ' for reproducibility') + set.seed(opt\$seed) +} + +# load adjacency matrix +# this matrix should have gene x gene dimensions + +message("Loading input data") + +adj <- as.matrix(read_delim_flexible( + opt\$adj, + header = TRUE, + row.names = 1, + check.names = FALSE +)) +if (nrow(adj) != ncol(adj)) { + stop('Adjacency matrix should be a squared matrix that reflects the connections between all the nodes') +} +if (!all(rownames(adj) == colnames(adj))) { + stop('Adjacency matrix row names are not equal to column names') +} + +# load and process knowledge database + +gmt <- load_gmt( + opt\$gmt, + rownames(adj) # adj should contain all the nodes (target and background) +) -# Read and process gene x GO term matrix -gmt_df <- process_gmt_file(opt\$gmt) -K <- gmt_to_K(gmt_df) +# filter gene sets +# gene sets with less than set_min or more than set_max genes are removed -# Ensure same number of rows (genes) -if (nrow(A) != nrow(K)){ - K <- add_missing(A, K) +idx <- which(colSums(gmt\$db) > opt\$set_min & colSums(gmt\$db) < opt\$set_max) +if (length(idx) == 0){ + stop("No gene set pass the filter of set_min=", opt\$set_min, " and set_max=", opt\$set_max) } +gmt\$db <- gmt\$db[, idx] +gmt\$description <- gmt\$description[idx] -# Run Graflex -G <- runGraflex(A, K, opt\$permutation, opt\$fixseed) +# run GREA +# Basically, it calculates the odds ratio of the graph being enriched in each concept, +# and the FDR of the odds ratio through permutation tests + +message("Running GREA") + +odds <- runGraflex( + adj, + gmt\$db, + p=opt\$permutation, + ncores=opt\$ncores +) +odds\$Description <- sapply(odds\$Concept, function(concept) + gmt\$description[[concept]] +) ################################################ ################################################ @@ -210,14 +254,19 @@ G <- runGraflex(A, K, opt\$permutation, opt\$fixseed) ################################################ ################################################ +if (!is.na(opt\$round_digits)) { + for (col in c('Odds', 'LogOR', 'FDR.under', 'FDR.over')){ + odds[,col] <- round(odds[,col], opt\$round_digits) + } +} + write.table( - G, - file = paste0(opt\$prefix, '.go.tsv'), + odds, + file = paste0(opt\$prefix, '.grea.tsv'), col.names = TRUE, - row.names = TRUE, + row.names = FALSE, sep = '\\t', quote = FALSE - ) ################################################ @@ -236,13 +285,11 @@ sink() ################################################ ################################################ -r.version <- strsplit(version[['version.string']], ' ')[[1]][3] propr.version <- as.character(packageVersion('propr')) writeLines( c( '"${task.process}":', - paste(' r-base:', r.version), paste(' r-propr:', propr.version) ), 'versions.yml') diff --git a/modules/nf-core/propr/grea/tests/grea_test.config b/modules/nf-core/propr/grea/tests/grea_test.config index 8d0d229a76d..7d354c013a5 100644 --- a/modules/nf-core/propr/grea/tests/grea_test.config +++ b/modules/nf-core/propr/grea/tests/grea_test.config @@ -1,8 +1,14 @@ process { - withName: "PROPR_PROPR"{ - ext.args = { "--adjacency true --permutation 5 --fixseed true --cutoff_min 0.05 --cutoff_max 0.95 --cutoff_interval 0.05"} + // set single core for reproducibility + // NOTE this method relies on parallelization and permutation tests + // The permutations are done within each node, which makes set.seed not working properly when + // different nodes are starting/ending depending on the case + cpus = 1 + + withName: "PROPR_PROPD"{ + ext.args = { "--round_digits 5 --save_adjacency true --features_id_col gene_name"} } withName: "PROPR_GREA"{ - ext.args = { "--permutation 5 --fixseed true"} + ext.args = { "--permutation 10 --set_min 10 --seed 123 --round_digits 5"} } -} \ No newline at end of file +} diff --git a/modules/nf-core/propr/grea/tests/main.nf.test b/modules/nf-core/propr/grea/tests/main.nf.test index dd442b43459..38a015e4b8c 100644 --- a/modules/nf-core/propr/grea/tests/main.nf.test +++ b/modules/nf-core/propr/grea/tests/main.nf.test @@ -8,34 +8,34 @@ nextflow_process { tag "modules_nfcore" tag "propr" tag "propr/grea" - tag "mygene" - tag "propr/propr" + tag "propr/propd" - test("grea chained to propr using default options") { + test("test grea chained to propd") { tag "default" config "./grea_test.config" setup { - run("PROPR_PROPR") { - script "../../propr/main.nf" + run("PROPR_PROPD") { + script "../../propd/main.nf" process { """ - input[0] = [ - [ id:'test' ], - file("https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/genomics/mus_musculus/rnaseq_expression/SRP254919.salmon.merged.gene_counts.top1000cov.tsv") - ] - """ - } - } - run("MYGENE") { - script "../../../mygene/main.nf" - process { - """ - input[0] = [ - [id : 'test'], - file("https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/genomics/mus_musculus/rnaseq_expression/SRP254919.gene_meta.tsv") + expression_test_data_dir = params.modules_testdata_base_path + 'genomics/mus_musculus/rnaseq_expression/' + + ch_contrasts = Channel.fromPath(file(expression_test_data_dir + 'SRP254919.contrasts.csv', checkIfExists: true)) + .splitCsv ( header:true, sep:',' ) + .map{ + tuple(it, it.variable, it.reference, it.target) + } + .first() + ch_matrix = [ + [id: 'test'], + file(expression_test_data_dir + 'SRP254919.samplesheet.csv', checkIfExists: true), + file(expression_test_data_dir + 'SRP254919.salmon.merged.gene_counts.top1000cov.tsv', checkIfExists: true) ] + + input[0] = ch_contrasts + input[1] = ch_matrix """ } } @@ -44,8 +44,11 @@ nextflow_process { when { process { """ - input[0] = PROPR_PROPR.out.adj.collect{ meta, adj -> adj }.map{ adj -> [[ id: 'test_adj'], adj]} - input[1] = MYGENE.out.gmt.collect{ meta, gmt -> gmt }.map{ gmt -> [[ id: 'test_gmt'], gmt]} + input[0] = PROPR_PROPD.out.adjacency + input[1] = [ + [id: 'test'], + file(params.modules_testdata_base_path + 'genomics/mus_musculus/gene_set_analysis/mh.all.v2022.1.Mm.symbols.gmt', checkIfExists: true) + ] """ } } @@ -53,10 +56,11 @@ nextflow_process { then { assertAll( { assert process.success }, - { assert snapshot(process.out.enrichedGO).match("grea chained to propr using default options - enrichedGO") }, - { assert snapshot(process.out.versions).match("versions") } - + { assert snapshot( + process.out.results, + process.out.versions + ).match()} ) } } -} \ No newline at end of file +} diff --git a/modules/nf-core/propr/grea/tests/main.nf.test.snap b/modules/nf-core/propr/grea/tests/main.nf.test.snap index 2db674fc5ae..6c5dd533ed8 100644 --- a/modules/nf-core/propr/grea/tests/main.nf.test.snap +++ b/modules/nf-core/propr/grea/tests/main.nf.test.snap @@ -1,31 +1,26 @@ { - "versions": { - "content": [ - [ - "versions.yml:md5,222a7a8b79b5a2987637279847c609d1" - ] - ], - "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" - }, - "timestamp": "2024-04-29T10:45:07.582509" - }, - "grea chained to propr using default options - enrichedGO": { + "test grea chained to propd": { "content": [ [ [ { - "id": "test_adj" + "id": "treatment_mCherry_hND6_", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "" }, - "test_adj.go.tsv:md5,904e1fe3eed0f2dded8e5b64321a0269" + "treatment_mCherry_hND6_.grea.tsv:md5,786faeccf39926d2f7c980ef549a2697" ] + ], + [ + "versions.yml:md5,060fcd8ce4afc482e237fa75686a0aba" ] ], "meta": { - "nf-test": "0.8.4", - "nextflow": "24.04.3" + "nf-test": "0.9.2", + "nextflow": "24.10.2" }, - "timestamp": "2024-08-03T16:06:25.669444" + "timestamp": "2024-12-11T13:00:02.026244403" } -} \ No newline at end of file +} From 7b32b09fe7787c0fc6924e7b6f223a0b1daf0d2f Mon Sep 17 00:00:00 2001 From: Daniel Lundin Date: Wed, 11 Dec 2024 16:57:50 +0100 Subject: [PATCH 2/3] Correct non-gzipped output from clustalo/align (#7197) --- modules/nf-core/clustalo/align/main.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nf-core/clustalo/align/main.nf b/modules/nf-core/clustalo/align/main.nf index e55ca172e78..c220afd7bd8 100644 --- a/modules/nf-core/clustalo/align/main.nf +++ b/modules/nf-core/clustalo/align/main.nf @@ -31,7 +31,7 @@ process CLUSTALO_ALIGN { def fhmm_batch = hmm_batch ? "--hmm-batch=${hmm_batch}" : "" def fprofile1 = profile1 ? "--profile1=${profile1}" : "" def fprofile2 = profile2 ? "--profile2=${profile2}" : "" - def write_output = compress ? "--force -o >(pigz -cp ${task.cpus} > ${prefix}.aln.gz)" : "> ${prefix}.aln" + def write_output = compress ? "--force -o >(pigz -cp ${task.cpus} > ${prefix}.aln.gz)" : "-o ${prefix}.aln" // using >() is necessary to preserve the return value, // so nextflow knows to display an error when it failed // the --force -o is necessary, as clustalo expands the commandline input, From 784b3a38aeafe9bb7e38f1369704dd5e6bcaf9f0 Mon Sep 17 00:00:00 2001 From: Jonathan Manning Date: Wed, 11 Dec 2024 16:41:43 +0000 Subject: [PATCH 3/3] Facilitate iteration in differential subworkflow (#7115) * illustrate differential iteration * Combine matrix metas with contrast metas in differential modules * Propose different iteration approach * Flip input order * Add multi-input test * Update module snaps * update meta.yml * Update module meta.ymls * See if different output prefix for limma_norm helps * [skip ci] move fc to iterable channel * update snapshots * Fix metas * update meta handling * Put modules back to the way they were * Try publishing logic fixes * Try disabling R object save * Revert "Try disabling R object save" This reverts commit 71f804dd967ab21e794c44860cb563a9f35ceb0f. * try fresh docker image URI * Tidy up configs * Don't repeat normalisation ops * Add correct blocking var consideration to configs * Add correct blocking var consideration to configs (snapshots) * remove blank lines * Corrections to channel structure, config * Adding author/ maintainer block pending other author suggestions * Add additional coauthors --- modules/nf-core/limma/differential/main.nf | 2 +- .../abundance_differential_filter/main.nf | 188 +++--- .../abundance_differential_filter/meta.yml | 179 ++++-- .../tests/deseq2_basic.config | 5 + .../tests/deseq2_limmavoom_basic.config | 24 + .../tests/limma_basic_microarray.config | 7 +- .../tests/limma_voom.config | 8 +- .../tests/main.nf.test | 216 +++++-- .../tests/main.nf.test.snap | 600 +++++++++++++++--- 9 files changed, 907 insertions(+), 322 deletions(-) create mode 100644 subworkflows/nf-core/abundance_differential_filter/tests/deseq2_limmavoom_basic.config diff --git a/modules/nf-core/limma/differential/main.nf b/modules/nf-core/limma/differential/main.nf index be6c72ecef3..b7106c397b6 100644 --- a/modules/nf-core/limma/differential/main.nf +++ b/modules/nf-core/limma/differential/main.nf @@ -5,7 +5,7 @@ process LIMMA_DIFFERENTIAL { conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/af/afd9579a0ff62890ff451d82b360d85e82a0d61a3da40736ee0eee4e45926269/data' : - 'community.wave.seqera.io/library/bioconductor-limma_bioconductor-edger:54dd09078d5db3b3' }" + 'community.wave.seqera.io/library/bioconductor-edger_bioconductor-limma:176c202c82450990' }" input: tuple val(meta), val(contrast_variable), val(reference), val(target) diff --git a/subworkflows/nf-core/abundance_differential_filter/main.nf b/subworkflows/nf-core/abundance_differential_filter/main.nf index f7ca8787512..6d587d58dab 100644 --- a/subworkflows/nf-core/abundance_differential_filter/main.nf +++ b/subworkflows/nf-core/abundance_differential_filter/main.nf @@ -8,113 +8,111 @@ include { DESEQ2_DIFFERENTIAL } from '../../../modules/nf-core/d include { DESEQ2_DIFFERENTIAL as DESEQ2_NORM } from '../../../modules/nf-core/deseq2/differential/main' include { CUSTOM_FILTERDIFFERENTIALTABLE } from '../../../modules/nf-core/custom/filterdifferentialtable/main' +// Combine meta maps, including merging non-identical values of shared keys (e.g. 'id') +def mergeMaps(meta, meta2){ + (meta + meta2).collectEntries { k, v -> + meta[k] && meta[k] != v ? [k, "${meta[k]}_${v}"] : [k, v] + } +} + workflow ABUNDANCE_DIFFERENTIAL_FILTER { take: - ch_abundance // [ meta_exp, counts ] with meta keys: method, args_diff - ch_transcript_lengths // [ meta_exp, transcript_lengths] - ch_control_features // [ meta_exp, control_features] - ch_samplesheet // [ meta_exp, samplesheet ] - ch_contrasts // [ meta_contrast, contrast_variable, reference, target ] - differential_method // limma, deseq2, propd - FC_threshold // float - padj_threshold // float - - main: - - // initialize empty results channels - ch_normalised_matrix = Channel.empty() - ch_variance_stabilised_matrix = Channel.empty() - ch_model = Channel.empty() - - ch_results_genewise = Channel.empty() - ch_results_genewise_filtered = Channel.empty() - ch_versions = Channel.empty() - - // Derive some commonly used derived channels - ch_samples_and_matrix = ch_samplesheet.join(ch_abundance).first() - - logFC_column = null - padj_column = null - - // ---------------------------------------------------- - // Perform differential analysis with DESeq2 - // ---------------------------------------------------- - - if (differential_method == 'deseq2') { - - logFC_column = "log2FoldChange" - padj_column = "padj" - - // Run the module once to generate a normalised matrix. We can't just - // use e.g. the first run of DESEQ_DIFFERENTIAL, because it may remove - // some samples - - DESEQ2_NORM ( - ch_contrasts.first(), - ch_samples_and_matrix, - ch_control_features, - ch_transcript_lengths - ) + // Things we may need to iterate + ch_input // [[meta_input], counts, analysis method, fc_threshold, padj_threshold] - ch_normalised_matrix = DESEQ2_NORM.out.normalised_counts - ch_variance_stabilised_matrix = DESEQ2_NORM.out.rlog_counts.concat(DESEQ2_NORM.out.vst_counts) + // Workflow-wide things, we don't need to iterate + ch_samplesheet // [ meta_exp, samplesheet ] + ch_transcript_lengths // [ meta_exp, transcript_lengths] + ch_control_features // [meta_exp, control_features] + ch_contrasts // [[ meta_contrast, contrast_variable, reference, target ]] - // Run the DESeq differential module - - DESEQ2_DIFFERENTIAL ( - ch_contrasts, - ch_samples_and_matrix, - ch_control_features, - ch_transcript_lengths - ) - - ch_results_genewise = DESEQ2_DIFFERENTIAL.out.results - ch_model = DESEQ2_DIFFERENTIAL.out.model - - ch_versions = ch_versions - .mix(DESEQ2_DIFFERENTIAL.out.versions) - - } else if (differential_method == 'limma'){ - - logFC_column = "logFC" - padj_column = "adj.P.Val" - - // Run the module once to generate a normalised matrix. We can't just - // use e.g. the first run of LIMMA_DIFFERENTIAL, because it may remove - // some samples + main: - LIMMA_NORM ( - ch_contrasts.first(), - ch_samples_and_matrix - ) + // Set up how the channels crossed below will be used to generate channels for processing + def criteria = multiMapCriteria { meta_input, abundance, analysis_method, fc_threshold, padj_threshold, meta_exp, samplesheet, meta_contrasts, variable, reference, target -> + samples_and_matrix: + meta_map = meta_input + [ 'method': analysis_method ] + [meta_map, samplesheet, abundance] + contrasts: + meta_map = mergeMaps(meta_contrasts, meta_input) + [ 'method': analysis_method ] + [ meta_map, variable, reference, target ] + filter_params: + meta_map = mergeMaps(meta_contrasts, meta_input) + [ 'method': analysis_method ] + [meta_map, [ 'fc_threshold': fc_threshold, 'padj_threshold': padj_threshold ]] + } - ch_normalised_matrix = LIMMA_NORM.out.normalised_counts + // For differential we need to cross the things we're iterating so we run + // differential analysis for every combination of matrix and contrast + inputs = ch_input + .combine(ch_samplesheet) + .combine(ch_contrasts) + .multiMap(criteria) + + // We only need a normalised matrix from one contrast. The reason we don't + //just use the output from the first differential is that the methods can + // subset matrices + norm_inputs = ch_input + .combine(ch_samplesheet) + .combine(ch_contrasts.first()) // Just taking the first contrast + .multiMap(criteria) + + // Perform normalization and differential analysis + DESEQ2_NORM( + norm_inputs.contrasts.filter{it[0].method == 'deseq2'}.first(), + norm_inputs.samples_and_matrix.filter{it[0].method == 'deseq2'}, + ch_control_features.first(), + ch_transcript_lengths.first() + ) - LIMMA_DIFFERENTIAL ( - ch_contrasts, - ch_samples_and_matrix - ) - ch_results_genewise = LIMMA_DIFFERENTIAL.out.results - ch_model = LIMMA_DIFFERENTIAL.out.model + LIMMA_NORM( + norm_inputs.contrasts.filter{it[0].method == 'limma'}.first(), + norm_inputs.samples_and_matrix.filter{it[0].method == 'limma'} + ) - ch_versions = ch_versions - .mix(LIMMA_DIFFERENTIAL.out.versions) + DESEQ2_DIFFERENTIAL( + inputs.contrasts.filter{it[0].method == 'deseq2'}, + inputs.samples_and_matrix.filter{it[0].method == 'deseq2'}, + ch_control_features.first(), + ch_transcript_lengths.first() + ) - } + LIMMA_DIFFERENTIAL( + inputs.contrasts.filter{it[0].method == 'limma'}, + inputs.samples_and_matrix.filter { it[0].method == 'limma' } + ) + // Combine results + ch_results = DESEQ2_DIFFERENTIAL.out.results.mix(LIMMA_DIFFERENTIAL.out.results) + ch_normalised_matrix = DESEQ2_NORM.out.normalised_counts.mix(LIMMA_NORM.out.normalised_counts) + ch_model = DESEQ2_DIFFERENTIAL.out.model.mix(LIMMA_DIFFERENTIAL.out.model) + ch_versions = DESEQ2_DIFFERENTIAL.out.versions + .mix(LIMMA_DIFFERENTIAL.out.versions) + + // Extract the fc and pval filters from the metamap we stashed them in + ch_diff_filter_params = ch_results + .join(inputs.filter_params) + .multiMap { meta, results, filter_meta -> + filter_input: [meta + filter_meta, results] + fc_column: meta.method == 'deseq2' ? 'log2FoldChange' : 'logFC' + padj_column: meta.method == 'deseq2' ? 'padj' : 'adj.P.Val' + fc_threshold: filter_meta.fc_threshold + padj_threshold: filter_meta.padj_threshold + } + + // Filter differential results CUSTOM_FILTERDIFFERENTIALTABLE( - ch_results_genewise, - logFC_column, - FC_threshold, - padj_column, - padj_threshold + ch_diff_filter_params.filter_input, + ch_diff_filter_params.fc_column, + ch_diff_filter_params.fc_threshold, + ch_diff_filter_params.padj_column, + ch_diff_filter_params.padj_threshold ) emit: - results_genewise = ch_results_genewise // channel: [ val(meta), path(results) ] - results_genewise_filtered = CUSTOM_FILTERDIFFERENTIALTABLE.out.filtered // channel: [ val(meta), path(filtered_results) ] - normalised_matrix = ch_normalised_matrix // channel: [ val(meta), path(normalised_matrix) ] - variance_stabilised_matrix = ch_variance_stabilised_matrix // channel: [ val(meta), path(variance_stabilised_matrix) ] (Optional) - model = ch_model // channel: [ val(meta), path(model) ] - versions = ch_versions // channel: [ path(versions.yml) ] + results_genewise = ch_results.map{meta, results -> [meta, results] } + results_genewise_filtered = CUSTOM_FILTERDIFFERENTIALTABLE.out.filtered + normalised_matrix = ch_normalised_matrix + variance_stabilised_matrix = DESEQ2_NORM.out.rlog_counts.mix(DESEQ2_NORM.out.vst_counts) + model = ch_model + versions = ch_versions } diff --git a/subworkflows/nf-core/abundance_differential_filter/meta.yml b/subworkflows/nf-core/abundance_differential_filter/meta.yml index 07c31cca254..8a2063e672e 100644 --- a/subworkflows/nf-core/abundance_differential_filter/meta.yml +++ b/subworkflows/nf-core/abundance_differential_filter/meta.yml @@ -1,83 +1,126 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/subworkflows/yaml-schema.json -name: "abundance_differential_filter" -description: Perform differential analysis and filtering on abundance data -keywords: - - differential expression - - RNA-seq - - DESeq2 - - limma - - filtering -components: - - deseq2/differential - - limma/differential - - custom/filterdifferentialtable input: - - ch_abundance: - type: file - description: | - Channel with abundance data and metadata - Structure: [ val(meta_exp), path(counts) ] - meta keys: method, args_diff + - ch_input: + description: Channel with input data for differential analysis + structure: + - meta_input: + type: map + description: Metadata map + - counts: + type: file + description: Count matrix file + - analysis_method: + type: value + description: Analysis method (deseq2 or limma) + - fc_threshold: + type: value + description: Fold change threshold for filtering + - padj_threshold: + type: value + description: Adjusted p-value threshold for filtering + - ch_samplesheet: + description: Channel with sample information + structure: + - meta_exp: + type: map + description: Experiment metadata map + - samplesheet: + type: file + description: Sample information file - ch_transcript_lengths: - type: file - description: | - Channel with transcript length information - Structure: [ val(meta_exp), path(transcript_lengths) ] + description: Channel with transcript length information + structure: + - meta_exp: + type: map + description: Experiment metadata map + - transcript_lengths: + type: file + description: Transcript length information file - ch_control_features: - type: file - description: | - Channel with control features information - Structure: [ val(meta_exp), path(control_features) ] - - ch_samplesheet: - type: file - description: | - Channel with sample information - Structure: [ val(meta_exp), path(samplesheet) ] + description: Channel with control features information + structure: + - meta_exp: + type: map + description: Experiment metadata map + - control_features: + type: file + description: Control features information file - ch_contrasts: - type: value - description: | - Channel with contrast information - Structure: [ val(meta_contrast), val(contrast_variable), val(reference), val(target) ] - - differential_method: - type: string - description: | - Method to use for differential analysis. Options: 'limma', 'deseq2' - - FC_threshold: - type: float - description: | - Fold change threshold for filtering differential results - - padj_threshold: - type: float - description: | - Adjusted p-value threshold for filtering differential results + description: Channel with contrast information + structure: + - meta_contrast: + type: map + description: Contrast metadata map + - contrast_variable: + type: value + description: Contrast variable + - reference: + type: value + description: Reference level + - target: + type: value + description: Target level output: + - versions: + description: Channel containing software versions file + structure: + - versions.yml: + type: file + description: File containing versions of the software used - results_genewise: - type: file - description: Unfiltered differential analysis results - pattern: "*.{csv,tsv}" + description: Channel containing unfiltered differential analysis results + structure: + - meta: + type: map + description: Metadata map + - results: + type: file + description: Unfiltered differential analysis results file + pattern: "*.{csv,tsv}" - results_genewise_filtered: - type: file - description: Filtered differential analysis results - pattern: "*.{csv,tsv}" + description: Channel containing filtered differential analysis results + structure: + - meta: + type: map + description: Metadata map + - filtered_results: + type: file + description: Filtered differential analysis results file + pattern: "*.{csv,tsv}" - normalised_matrix: - type: file - description: Normalised count matrix - pattern: "*.{csv,tsv}" + description: Channel containing normalised count matrix + structure: + - meta: + type: map + description: Metadata map + - matrix: + type: file + description: Normalised count matrix file + pattern: "*.{csv,tsv}" - variance_stabilised_matrix: - type: file - description: Variance stabilised count matrix (optional, DESeq2 only) - pattern: "*.{csv,tsv}" + description: Channel containing variance stabilised count matrix (DESeq2 only) + structure: + - meta: + type: map + description: Metadata map + - matrix: + type: file + description: Variance stabilised count matrix file + pattern: "*.{csv,tsv}" - model: - type: file - description: Statistical model object from differential analysis - pattern: "*.rds" - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" - + description: Channel containing statistical model object from differential analysis + structure: + - meta: + type: map + description: Metadata map + - model: + type: file + description: Statistical model object file + pattern: "*.rds" authors: - "@pinin4fjords" + - "@bjlang" + - "@araiz2001" + - "@suzannejin" maintainers: - "@pinin4fjords" diff --git a/subworkflows/nf-core/abundance_differential_filter/tests/deseq2_basic.config b/subworkflows/nf-core/abundance_differential_filter/tests/deseq2_basic.config index 0e4e63a13f7..508fa04aedc 100644 --- a/subworkflows/nf-core/abundance_differential_filter/tests/deseq2_basic.config +++ b/subworkflows/nf-core/abundance_differential_filter/tests/deseq2_basic.config @@ -1,5 +1,10 @@ process { withName: 'DESEQ2_DIFFERENTIAL' { ext.args = { "--round_digits 5 --blocking_variables $meta.blocking --vs_method rlog" } + ext.prefix = { "${meta.id}_${meta.method}" } + } + + withName: 'DESEQ2_NORM' { + ext.prefix = { "${meta.id}_${meta.method}_voom_norm" } } } diff --git a/subworkflows/nf-core/abundance_differential_filter/tests/deseq2_limmavoom_basic.config b/subworkflows/nf-core/abundance_differential_filter/tests/deseq2_limmavoom_basic.config new file mode 100644 index 00000000000..a2b6e9efbd9 --- /dev/null +++ b/subworkflows/nf-core/abundance_differential_filter/tests/deseq2_limmavoom_basic.config @@ -0,0 +1,24 @@ +process { + withName: 'DESEQ2_DIFFERENTIAL' { + ext.args = { "--round_digits 5 --blocking_variables $meta.blocking --vs_method rlog" } + ext.prefix = { "${meta.id}_${meta.method}" } + } + + withName: 'DESEQ2_NORM' { + ext.prefix = { "${meta.id}_${meta.method}_norm" } + } + + withName: 'LIMMA_DIFFERENTIAL' { + ext.args = { [ + "--blocking_variables $meta.blocking", + "--sample_id_col sample", + "--probe_id_col gene_id", + "--use_voom TRUE" + ].join(' ').trim() } + ext.prefix = { "${meta.id}_${meta.method}_voom" } + } + + withName: 'LIMMA_NORM' { + ext.prefix = { "${meta.id}_${meta.method}_voom_norm" } + } +} diff --git a/subworkflows/nf-core/abundance_differential_filter/tests/limma_basic_microarray.config b/subworkflows/nf-core/abundance_differential_filter/tests/limma_basic_microarray.config index 98c5e882164..59eddbc362a 100644 --- a/subworkflows/nf-core/abundance_differential_filter/tests/limma_basic_microarray.config +++ b/subworkflows/nf-core/abundance_differential_filter/tests/limma_basic_microarray.config @@ -1,12 +1,15 @@ process { - publishDir = { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" } - withName: 'LIMMA_DIFFERENTIAL' { ext.args = { [ "--sample_id_col name", "--blocking_variables $meta.blocking" ].join(' ').trim() } + ext.prefix = { "${meta.id}_${meta.method}" } + } + + withName: 'LIMMA_NORM' { + ext.prefix = { "${meta.id}_${meta.method}_norm" } } withName: 'AFFY_JUSTRMA' { diff --git a/subworkflows/nf-core/abundance_differential_filter/tests/limma_voom.config b/subworkflows/nf-core/abundance_differential_filter/tests/limma_voom.config index a1e505570e1..241679da0e5 100644 --- a/subworkflows/nf-core/abundance_differential_filter/tests/limma_voom.config +++ b/subworkflows/nf-core/abundance_differential_filter/tests/limma_voom.config @@ -1,13 +1,17 @@ process { - publishDir = { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" } - withName: 'LIMMA_DIFFERENTIAL' { ext.args = { [ + "--blocking_variables $meta.blocking", "--sample_id_col sample", "--probe_id_col gene_id", "--use_voom TRUE" ].join(' ').trim() } + + ext.prefix = { "${meta.id}_${meta.method}_voom" } } + withName: 'LIMMA_NORM' { + ext.prefix = { "${meta.id}_${meta.method}_voom_norm" } + } } diff --git a/subworkflows/nf-core/abundance_differential_filter/tests/main.nf.test b/subworkflows/nf-core/abundance_differential_filter/tests/main.nf.test index 144d61c87a7..26a956b2040 100644 --- a/subworkflows/nf-core/abundance_differential_filter/tests/main.nf.test +++ b/subworkflows/nf-core/abundance_differential_filter/tests/main.nf.test @@ -15,7 +15,7 @@ nextflow_workflow { test("deseq2 - mouse - basic") { config './deseq2_basic.config' - + when { workflow { """ @@ -28,24 +28,30 @@ nextflow_workflow { ] // Define inputs - input[0] = Channel.of([ - [ id:'test' ], - file(testData.expression_test_data_dir + testData.abundance_file) - ]) - input[1] = Channel.of([ [], [] ]) - input[2] = Channel.of([ [], [] ]) - input[3] = Channel.of([ + ch_samplesheet = Channel.of([ [ id:'test' ], file(testData.expression_test_data_dir + testData.samplesheet_file) ]) - input[4] = Channel.fromPath(file(testData.expression_test_data_dir + testData.contrasts_file)) + ch_transcript_lengths = Channel.of([ [], [] ]) + ch_control_features = Channel.of([ [], [] ]) + ch_contrasts = Channel.fromPath(file(testData.expression_test_data_dir + testData.contrasts_file)) .splitCsv ( header:true, sep:',' ) .map{ tuple(it, it.variable, it.reference, it.target) } - input[5] = 'deseq2' - input[6] = 1.5 - input[7] = 0.05 + ch_input = Channel.of([ + [ id:'test' ], + file(testData.expression_test_data_dir + testData.abundance_file), + 'deseq2', // analysis method + 1.5, // FC threshold + 0.05 // padj threshold + ]) + + input[0] = ch_input + input[1] = ch_samplesheet + input[2] = ch_transcript_lengths + input[3] = ch_control_features + input[4] = ch_contrasts """ } } @@ -66,7 +72,7 @@ nextflow_workflow { test("limma - basic - microarray") { config './limma_basic_microarray.config' - + setup { run("UNTAR") { script "../../../../modules/nf-core/untar/main.nf" @@ -106,20 +112,29 @@ nextflow_workflow { ] // Define inputs - input[0] = AFFY_JUSTRMA.out.expression - input[1] = Channel.of([ [], [] ]) - input[2] = Channel.of([ [], [] ]) - input[3] = Channel.of([ + ch_samplesheet = Channel.of([ [ id:'test' ], file(testData.expression_test_data_dir + testData.samplesheet_file) ]) - input[4] = Channel.of(['id': 'diagnosis_normal_uremia', 'variable': 'diagnosis', 'reference': 'normal', 'target': 'uremia']) + ch_transcript_lengths = Channel.of([ [], [] ]) + ch_control_features = Channel.of([ [], [] ]) + ch_contrasts = Channel.of(['id': 'diagnosis_normal_uremia', 'variable': 'diagnosis', 'reference': 'normal', 'target': 'uremia']) .map{ tuple(it, it.variable, it.reference, it.target) } - input[5] = 'limma' - input[6] = 1.5 - input[7] = 0.05 + ch_input = AFFY_JUSTRMA.out.expression.map{ meta, file -> [ + meta, + file, + 'limma', + 1.5, // FC threshold + 0.05 // padj threshold + ]} + + input[0] = ch_input + input[1] = ch_samplesheet + input[2] = ch_transcript_lengths + input[3] = ch_control_features + input[4] = ch_contrasts """ } } @@ -139,7 +154,7 @@ nextflow_workflow { test("limma - voom") { config './limma_voom.config' - + when { workflow { """ @@ -152,30 +167,37 @@ nextflow_workflow { ] // Define inputs - input[0] = Channel.of([ - [ id:'test' ], - file(testData.expression_test_data_dir + testData.abundance_file) - ]) - input[1] = Channel.of([ [], [] ]) - input[2] = Channel.of([ [], [] ]) - input[3] = Channel.of([ + ch_samplesheet = Channel.of([ [ id:'test' ], file(testData.expression_test_data_dir + testData.samplesheet_file) ]) - input[4] = Channel.of(['id': 'test', 'variable': 'treatment', 'reference': 'hND6', 'target': 'mCherry']) + ch_transcript_lengths = Channel.of([ [], [] ]) + ch_control_features = Channel.of([ [], [] ]) + ch_contrasts = Channel.fromPath(file(testData.expression_test_data_dir + testData.contrasts_file)) + .splitCsv ( header:true, sep:',' ) .map{ tuple(it, it.variable, it.reference, it.target) } - input[5] = 'limma' - input[6] = 1.5 - input[7] = 0.05 + ch_input = Channel.of([ + [ id:'test' ], + file(testData.expression_test_data_dir + testData.abundance_file), + 'limma', + 1.5, // FC threshold + 0.05 // padj threshold + ]) + + input[0] = ch_input + input[1] = ch_samplesheet + input[2] = ch_transcript_lengths + input[3] = ch_control_features + input[4] = ch_contrasts """ } } then { assertAll( { assert workflow.success }, - { assert path(workflow.out.results_genewise[0][1]).getText().contains("ENSMUSG00000023978\t-2.84055986312942") }, + { assert path(workflow.out.results_genewise[0][1]).getText().contains("ENSMUSG00000023978\t2.84055986312942") }, { assert snapshot( workflow.out.results_genewise_filtered, workflow.out.normalised_matrix, @@ -188,7 +210,7 @@ nextflow_workflow { test("deseq2 - with transcript lengths") { config './deseq2_basic.config' - + when { workflow { """ @@ -202,27 +224,99 @@ nextflow_workflow { ] // Define inputs - input[0] = Channel.of([ - [ id:'test' ], - file(testData.expression_test_data_dir + testData.abundance_file) + ch_samplesheet = Channel.of([ + [ id:'test' ], + file(testData.expression_test_data_dir + testData.samplesheet_file) ]) - input[1] = Channel.of([ + ch_transcript_lengths = Channel.of([ [ id:'test' ], file(testData.expression_test_data_dir + testData.lengths_file) ]) - input[2] = Channel.of([ [], [] ]) - input[3] = Channel.of([ + ch_control_features = Channel.of([ [], [] ]) + ch_contrasts = Channel.fromPath(file(testData.expression_test_data_dir + testData.contrasts_file)) + .splitCsv ( header:true, sep:',' ) + .map{ + tuple(it, it.variable, it.reference, it.target) + } + ch_input = Channel.of([ + [ id:'test' ], + file(testData.expression_test_data_dir + testData.abundance_file), + 'deseq2', + 1.5, // FC threshold + 0.05 // padj threshold + ]) + + input[0] = ch_input + input[1] = ch_samplesheet + input[2] = ch_transcript_lengths + input[3] = ch_control_features + input[4] = ch_contrasts + """ + } + } + then { + assertAll( + { assert workflow.success }, + { assert snapshot( + workflow.out.results_genewise, + workflow.out.results_genewise_filtered, + workflow.out.normalised_matrix, + workflow.out.variance_stabilised_matrix, + workflow.out.model, + workflow.out.versions + ).match() } + ) + } + } + + test("deseq2 and limma - mouse - basic") { + config './deseq2_limmavoom_basic.config' + + when { + workflow { + """ + // Define test data + def testData = [ + expression_test_data_dir: params.modules_testdata_base_path + 'genomics/mus_musculus/rnaseq_expression/', + contrasts_file: 'SRP254919.contrasts.csv', + abundance_file: 'SRP254919.salmon.merged.gene_counts.top1000cov.tsv', + samplesheet_file: 'SRP254919.samplesheet.csv' + ] + + // Define inputs + ch_samplesheet = Channel.of([ [ id:'test' ], file(testData.expression_test_data_dir + testData.samplesheet_file) ]) - input[4] = Channel.fromPath(file(testData.expression_test_data_dir + testData.contrasts_file)) + ch_transcript_lengths = Channel.value([ [], [] ]) + ch_control_features = Channel.value([ [], [] ]) + ch_contrasts = Channel.fromPath(file(testData.expression_test_data_dir + testData.contrasts_file)) .splitCsv ( header:true, sep:',' ) .map{ tuple(it, it.variable, it.reference, it.target) } - input[5] = 'deseq2' - input[6] = 1.5 - input[7] = 0.05 + ch_input = Channel.of( + [ + [ id:'test' ], + file(testData.expression_test_data_dir + testData.abundance_file), + 'limma', + 1.5, // FC threshold + 0.05 // padj threshold + ], + [ + [ id:'test' ], + file(testData.expression_test_data_dir + testData.abundance_file), + 'deseq2', + 1.5, // FC threshold + 0.05 // padj threshold + ] + ) + + input[0] = ch_input + input[1] = ch_samplesheet + input[2] = ch_transcript_lengths + input[3] = ch_control_features + input[4] = ch_contrasts """ } } @@ -243,9 +337,9 @@ nextflow_workflow { test("stub") { config './deseq2_basic.config' - + options "-stub" - + when { workflow { """ @@ -258,24 +352,30 @@ nextflow_workflow { ] // Define inputs - input[0] = Channel.of([ - [ id:'test' ], - file(testData.expression_test_data_dir + testData.abundance_file) - ]) - input[1] = Channel.of([ [], [] ]) - input[2] = Channel.of([ [], [] ]) - input[3] = Channel.of([ + ch_samplesheet = Channel.of([ [ id:'test' ], file(testData.expression_test_data_dir + testData.samplesheet_file) ]) - input[4] = Channel.fromPath(file(testData.expression_test_data_dir + testData.contrasts_file)) + ch_transcript_lengths = Channel.of([ [], [] ]) + ch_control_features = Channel.of([ [], [] ]) + ch_contrasts = Channel.fromPath(file(testData.expression_test_data_dir + testData.contrasts_file)) .splitCsv ( header:true, sep:',' ) .map{ tuple(it, it.variable, it.reference, it.target) } - input[5] = 'deseq2' - input[6] = 1.5 - input[7] = 0.05 + ch_input = Channel.of([ + [ id:'test' ], + file(testData.expression_test_data_dir + testData.abundance_file), + 'deseq2', + 1.5, // FC threshold + 0.05 // padj threshold + ]) + + input[0] = ch_input + input[1] = ch_samplesheet + input[2] = ch_transcript_lengths + input[3] = ch_control_features + input[4] = ch_contrasts """ } } diff --git a/subworkflows/nf-core/abundance_differential_filter/tests/main.nf.test.snap b/subworkflows/nf-core/abundance_differential_filter/tests/main.nf.test.snap index 5053ea3db63..e296cf3ebbb 100644 --- a/subworkflows/nf-core/abundance_differential_filter/tests/main.nf.test.snap +++ b/subworkflows/nf-core/abundance_differential_filter/tests/main.nf.test.snap @@ -4,23 +4,27 @@ [ [ { - "id": "diagnosis_normal_uremia", + "id": "diagnosis_normal_uremia_test", "variable": "diagnosis", "reference": "normal", - "target": "uremia" + "target": "uremia", + "method": "limma" }, - "diagnosis_normal_uremia.limma.results.tsv:md5,4f0944b54b3ab7aa6ded5dd4b4e82802" + "diagnosis_normal_uremia_test_limma.limma.results.tsv:md5,4f0944b54b3ab7aa6ded5dd4b4e82802" ] ], [ [ { - "id": "diagnosis_normal_uremia", + "id": "diagnosis_normal_uremia_test", "variable": "diagnosis", "reference": "normal", - "target": "uremia" + "target": "uremia", + "method": "limma", + "fc_threshold": 1.5, + "padj_threshold": 0.05 }, - "diagnosis_normal_uremia_filtered.tsv:md5,a971455ece4ae3c3ab902407b36fc6a5" + "diagnosis_normal_uremia_test_filtered.tsv:md5,a971455ece4ae3c3ab902407b36fc6a5" ] ], [ @@ -29,12 +33,13 @@ [ [ { - "id": "diagnosis_normal_uremia", + "id": "diagnosis_normal_uremia_test", "variable": "diagnosis", "reference": "normal", - "target": "uremia" + "target": "uremia", + "method": "limma" }, - "diagnosis_normal_uremia.limma.model.txt:md5,70b000f632b8bdba4917046362dd876b" + "diagnosis_normal_uremia_test_limma.limma.model.txt:md5,70b000f632b8bdba4917046362dd876b" ] ], [ @@ -42,55 +47,286 @@ ] ], "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.4" + "nf-test": "0.9.2", + "nextflow": "24.10.2" }, - "timestamp": "2024-11-13T09:33:03.414506" + "timestamp": "2024-12-07T16:33:03.091762783" + }, + "deseq2 and limma - mouse - basic": { + "content": [ + [ + [ + { + "id": "treatment_mCherry_hND6__test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "", + "method": "deseq2" + }, + "treatment_mCherry_hND6__test_deseq2.deseq2.results.tsv:md5,791cdba2615a445cded13cae95df73ef" + ], + [ + { + "id": "treatment_mCherry_hND6__test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "", + "method": "limma" + }, + "treatment_mCherry_hND6__test_limma_voom.limma.results.tsv:md5,ff36827b7869a8a3c3c905efedcafc93" + ], + [ + { + "id": "treatment_mCherry_hND6_sample_number_test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "sample_number", + "method": "deseq2" + }, + "treatment_mCherry_hND6_sample_number_test_deseq2.deseq2.results.tsv:md5,2438053a4bdc869f467a12d3c22c7ba7" + ], + [ + { + "id": "treatment_mCherry_hND6_sample_number_test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "sample_number", + "method": "limma" + }, + "treatment_mCherry_hND6_sample_number_test_limma_voom.limma.results.tsv:md5,5f34e79dbcb5ba1908d797548921d7fc" + ] + ], + [ + [ + { + "id": "treatment_mCherry_hND6__test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "", + "method": "deseq2", + "fc_threshold": 1.5, + "padj_threshold": 0.05 + }, + "treatment_mCherry_hND6__test_filtered.tsv:md5,7829ead408f4c9cad4277598a231c494" + ], + [ + { + "id": "treatment_mCherry_hND6__test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "", + "method": "limma", + "fc_threshold": 1.5, + "padj_threshold": 0.05 + }, + "treatment_mCherry_hND6__test_filtered.tsv:md5,0bfc9215edc6aad064c3ce6abc81bfce" + ], + [ + { + "id": "treatment_mCherry_hND6_sample_number_test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "sample_number", + "method": "deseq2", + "fc_threshold": 1.5, + "padj_threshold": 0.05 + }, + "treatment_mCherry_hND6_sample_number_test_filtered.tsv:md5,8b084475c9e7e1b34a510a73b613ff39" + ], + [ + { + "id": "treatment_mCherry_hND6_sample_number_test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "sample_number", + "method": "limma", + "fc_threshold": 1.5, + "padj_threshold": 0.05 + }, + "treatment_mCherry_hND6_sample_number_test_filtered.tsv:md5,0bfc9215edc6aad064c3ce6abc81bfce" + ] + ], + [ + [ + { + "id": "treatment_mCherry_hND6__test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "", + "method": "deseq2" + }, + "treatment_mCherry_hND6__test_deseq2_norm.normalised_counts.tsv:md5,46ab7200c626649ab6256ed797ef5071" + ], + [ + { + "id": "treatment_mCherry_hND6__test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "", + "method": "limma" + }, + "treatment_mCherry_hND6__test_limma_voom_norm.normalised_counts.tsv:md5,2aa4880ba5ae246a728b25f4316ca2ca" + ] + ], + [ + [ + { + "id": "treatment_mCherry_hND6__test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "", + "method": "deseq2" + }, + "treatment_mCherry_hND6__test_deseq2_norm.rlog.tsv:md5,b1adc1fba6bd0c8b55973608f4b97030" + ] + ], + [ + [ + { + "id": "treatment_mCherry_hND6__test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "", + "method": "deseq2" + }, + "treatment_mCherry_hND6__test_deseq2.deseq2.model.txt:md5,d2113d82b76046c319e6602da2ad74d6" + ], + [ + { + "id": "treatment_mCherry_hND6__test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "", + "method": "limma" + }, + "treatment_mCherry_hND6__test_limma_voom.limma.model.txt:md5,d2113d82b76046c319e6602da2ad74d6" + ], + [ + { + "id": "treatment_mCherry_hND6_sample_number_test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "sample_number", + "method": "deseq2" + }, + "treatment_mCherry_hND6_sample_number_test_deseq2.deseq2.model.txt:md5,fa05126a58cb67c107d45426b0bdea83" + ], + [ + { + "id": "treatment_mCherry_hND6_sample_number_test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "sample_number", + "method": "limma" + }, + "treatment_mCherry_hND6_sample_number_test_limma_voom.limma.model.txt:md5,3b96713b4e3f027b0347859f02a9038d" + ] + ], + [ + "versions.yml:md5,1ddaab440e2528c688c05a02dd066f12", + "versions.yml:md5,1ddaab440e2528c688c05a02dd066f12", + "versions.yml:md5,2c0576aefff8da32c7c0cfd8529aa4b5", + "versions.yml:md5,2c0576aefff8da32c7c0cfd8529aa4b5" + ] + ], + "meta": { + "nf-test": "0.9.2", + "nextflow": "24.10.2" + }, + "timestamp": "2024-12-07T16:34:54.548033825" }, "limma - voom": { "content": [ [ [ { - "id": "test", + "id": "treatment_mCherry_hND6__test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "", + "method": "limma", + "fc_threshold": 1.5, + "padj_threshold": 0.05 + }, + "treatment_mCherry_hND6__test_filtered.tsv:md5,0bfc9215edc6aad064c3ce6abc81bfce" + ], + [ + { + "id": "treatment_mCherry_hND6_sample_number_test", "variable": "treatment", - "reference": "hND6", - "target": "mCherry" + "reference": "mCherry", + "target": "hND6", + "blocking": "sample_number", + "method": "limma", + "fc_threshold": 1.5, + "padj_threshold": 0.05 }, - "test_filtered.tsv:md5,0bfc9215edc6aad064c3ce6abc81bfce" + "treatment_mCherry_hND6_sample_number_test_filtered.tsv:md5,0bfc9215edc6aad064c3ce6abc81bfce" ] ], [ [ { - "id": "test", + "id": "treatment_mCherry_hND6__test", "variable": "treatment", - "reference": "hND6", - "target": "mCherry" + "reference": "mCherry", + "target": "hND6", + "blocking": "", + "method": "limma" }, - "test.normalised_counts.tsv:md5,2aa4880ba5ae246a728b25f4316ca2ca" + "treatment_mCherry_hND6__test_limma_voom_norm.normalised_counts.tsv:md5,2aa4880ba5ae246a728b25f4316ca2ca" ] ], [ [ { - "id": "test", + "id": "treatment_mCherry_hND6__test", "variable": "treatment", - "reference": "hND6", - "target": "mCherry" + "reference": "mCherry", + "target": "hND6", + "blocking": "", + "method": "limma" }, - "test.limma.model.txt:md5,d2113d82b76046c319e6602da2ad74d6" + "treatment_mCherry_hND6__test_limma_voom.limma.model.txt:md5,d2113d82b76046c319e6602da2ad74d6" + ], + [ + { + "id": "treatment_mCherry_hND6_sample_number_test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "sample_number", + "method": "limma" + }, + "treatment_mCherry_hND6_sample_number_test_limma_voom.limma.model.txt:md5,3b96713b4e3f027b0347859f02a9038d" ] ], [ + "versions.yml:md5,1ddaab440e2528c688c05a02dd066f12", "versions.yml:md5,1ddaab440e2528c688c05a02dd066f12" ] ], "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.4" + "nf-test": "0.9.2", + "nextflow": "24.10.2" }, - "timestamp": "2024-11-12T12:09:18.770313" + "timestamp": "2024-12-07T16:33:26.97858478" }, "stub": { "content": [ @@ -98,279 +334,451 @@ "0": [ [ { - "id": "treatment_mCherry_hND6_", + "id": "treatment_mCherry_hND6__test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "", + "method": "deseq2" + }, + "treatment_mCherry_hND6__test.deseq2.results.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + ], + [ + { + "id": "treatment_mCherry_hND6_sample_number_test", "variable": "treatment", "reference": "mCherry", "target": "hND6", - "blocking": "" + "blocking": "sample_number", + "method": "deseq2" }, - "treatment_mCherry_hND6_.deseq2.results.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + "treatment_mCherry_hND6_sample_number_test.deseq2.results.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], "1": [ [ { - "id": "treatment_mCherry_hND6_", + "id": "treatment_mCherry_hND6__test", "variable": "treatment", "reference": "mCherry", "target": "hND6", - "blocking": "" + "blocking": "", + "method": "deseq2", + "fc_threshold": 1.5, + "padj_threshold": 0.05 }, - "treatment_mCherry_hND6__filtered.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + "treatment_mCherry_hND6__test_filtered.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + ], + [ + { + "id": "treatment_mCherry_hND6_sample_number_test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "sample_number", + "method": "deseq2", + "fc_threshold": 1.5, + "padj_threshold": 0.05 + }, + "treatment_mCherry_hND6_sample_number_test_filtered.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], "2": [ [ { - "id": "treatment_mCherry_hND6_", + "id": "treatment_mCherry_hND6__test", "variable": "treatment", "reference": "mCherry", "target": "hND6", - "blocking": "" + "blocking": "", + "method": "deseq2" }, - "treatment_mCherry_hND6_.normalised_counts.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + "treatment_mCherry_hND6__test.normalised_counts.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], "3": [ [ { - "id": "treatment_mCherry_hND6_", + "id": "treatment_mCherry_hND6__test", "variable": "treatment", "reference": "mCherry", "target": "hND6", - "blocking": "" + "blocking": "", + "method": "deseq2" }, - "treatment_mCherry_hND6_.rlog.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + "treatment_mCherry_hND6__test.rlog.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], "4": [ [ { - "id": "treatment_mCherry_hND6_", + "id": "treatment_mCherry_hND6__test", "variable": "treatment", "reference": "mCherry", "target": "hND6", - "blocking": "" + "blocking": "", + "method": "deseq2" }, - "treatment_mCherry_hND6_.deseq2.model.txt:md5,d41d8cd98f00b204e9800998ecf8427e" + "treatment_mCherry_hND6__test.deseq2.model.txt:md5,d41d8cd98f00b204e9800998ecf8427e" + ], + [ + { + "id": "treatment_mCherry_hND6_sample_number_test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "sample_number", + "method": "deseq2" + }, + "treatment_mCherry_hND6_sample_number_test.deseq2.model.txt:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], "5": [ + "versions.yml:md5,05e3901f6d78f8839a7e07f422e9bc03", "versions.yml:md5,05e3901f6d78f8839a7e07f422e9bc03" ], "model": [ [ { - "id": "treatment_mCherry_hND6_", + "id": "treatment_mCherry_hND6__test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "", + "method": "deseq2" + }, + "treatment_mCherry_hND6__test.deseq2.model.txt:md5,d41d8cd98f00b204e9800998ecf8427e" + ], + [ + { + "id": "treatment_mCherry_hND6_sample_number_test", "variable": "treatment", "reference": "mCherry", "target": "hND6", - "blocking": "" + "blocking": "sample_number", + "method": "deseq2" }, - "treatment_mCherry_hND6_.deseq2.model.txt:md5,d41d8cd98f00b204e9800998ecf8427e" + "treatment_mCherry_hND6_sample_number_test.deseq2.model.txt:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], "normalised_matrix": [ [ { - "id": "treatment_mCherry_hND6_", + "id": "treatment_mCherry_hND6__test", "variable": "treatment", "reference": "mCherry", "target": "hND6", - "blocking": "" + "blocking": "", + "method": "deseq2" }, - "treatment_mCherry_hND6_.normalised_counts.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + "treatment_mCherry_hND6__test.normalised_counts.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], "results_genewise": [ [ { - "id": "treatment_mCherry_hND6_", + "id": "treatment_mCherry_hND6__test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "", + "method": "deseq2" + }, + "treatment_mCherry_hND6__test.deseq2.results.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + ], + [ + { + "id": "treatment_mCherry_hND6_sample_number_test", "variable": "treatment", "reference": "mCherry", "target": "hND6", - "blocking": "" + "blocking": "sample_number", + "method": "deseq2" }, - "treatment_mCherry_hND6_.deseq2.results.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + "treatment_mCherry_hND6_sample_number_test.deseq2.results.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], "results_genewise_filtered": [ [ { - "id": "treatment_mCherry_hND6_", + "id": "treatment_mCherry_hND6__test", "variable": "treatment", "reference": "mCherry", "target": "hND6", - "blocking": "" + "blocking": "", + "method": "deseq2", + "fc_threshold": 1.5, + "padj_threshold": 0.05 }, - "treatment_mCherry_hND6__filtered.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + "treatment_mCherry_hND6__test_filtered.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + ], + [ + { + "id": "treatment_mCherry_hND6_sample_number_test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "sample_number", + "method": "deseq2", + "fc_threshold": 1.5, + "padj_threshold": 0.05 + }, + "treatment_mCherry_hND6_sample_number_test_filtered.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], "variance_stabilised_matrix": [ [ { - "id": "treatment_mCherry_hND6_", + "id": "treatment_mCherry_hND6__test", "variable": "treatment", "reference": "mCherry", "target": "hND6", - "blocking": "" + "blocking": "", + "method": "deseq2" }, - "treatment_mCherry_hND6_.rlog.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + "treatment_mCherry_hND6__test.rlog.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], "versions": [ + "versions.yml:md5,05e3901f6d78f8839a7e07f422e9bc03", "versions.yml:md5,05e3901f6d78f8839a7e07f422e9bc03" ] } ], "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.4" + "nf-test": "0.9.2", + "nextflow": "24.10.2" }, - "timestamp": "2024-11-12T12:09:58.505823" + "timestamp": "2024-12-07T16:35:23.476681768" }, "deseq2 - mouse - basic": { "content": [ [ [ { - "id": "treatment_mCherry_hND6_", + "id": "treatment_mCherry_hND6__test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "", + "method": "deseq2" + }, + "treatment_mCherry_hND6__test_deseq2.deseq2.results.tsv:md5,791cdba2615a445cded13cae95df73ef" + ], + [ + { + "id": "treatment_mCherry_hND6_sample_number_test", "variable": "treatment", "reference": "mCherry", "target": "hND6", - "blocking": "" + "blocking": "sample_number", + "method": "deseq2" }, - "treatment_mCherry_hND6_.deseq2.results.tsv:md5,791cdba2615a445cded13cae95df73ef" + "treatment_mCherry_hND6_sample_number_test_deseq2.deseq2.results.tsv:md5,2438053a4bdc869f467a12d3c22c7ba7" ] ], [ [ { - "id": "treatment_mCherry_hND6_", + "id": "treatment_mCherry_hND6__test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "", + "method": "deseq2", + "fc_threshold": 1.5, + "padj_threshold": 0.05 + }, + "treatment_mCherry_hND6__test_filtered.tsv:md5,7829ead408f4c9cad4277598a231c494" + ], + [ + { + "id": "treatment_mCherry_hND6_sample_number_test", "variable": "treatment", "reference": "mCherry", "target": "hND6", - "blocking": "" + "blocking": "sample_number", + "method": "deseq2", + "fc_threshold": 1.5, + "padj_threshold": 0.05 }, - "treatment_mCherry_hND6__filtered.tsv:md5,7829ead408f4c9cad4277598a231c494" + "treatment_mCherry_hND6_sample_number_test_filtered.tsv:md5,8b084475c9e7e1b34a510a73b613ff39" ] ], [ [ { - "id": "treatment_mCherry_hND6_", + "id": "treatment_mCherry_hND6__test", "variable": "treatment", "reference": "mCherry", "target": "hND6", - "blocking": "" + "blocking": "", + "method": "deseq2" }, - "treatment_mCherry_hND6_.normalised_counts.tsv:md5,46ab7200c626649ab6256ed797ef5071" + "treatment_mCherry_hND6__test_deseq2_voom_norm.normalised_counts.tsv:md5,46ab7200c626649ab6256ed797ef5071" ] ], [ [ { - "id": "treatment_mCherry_hND6_", + "id": "treatment_mCherry_hND6__test", "variable": "treatment", "reference": "mCherry", "target": "hND6", - "blocking": "" + "blocking": "", + "method": "deseq2" }, - "treatment_mCherry_hND6_.rlog.tsv:md5,b1adc1fba6bd0c8b55973608f4b97030" + "treatment_mCherry_hND6__test_deseq2_voom_norm.rlog.tsv:md5,b1adc1fba6bd0c8b55973608f4b97030" ] ], [ [ { - "id": "treatment_mCherry_hND6_", + "id": "treatment_mCherry_hND6__test", "variable": "treatment", "reference": "mCherry", "target": "hND6", - "blocking": "" + "blocking": "", + "method": "deseq2" }, - "treatment_mCherry_hND6_.deseq2.model.txt:md5,d2113d82b76046c319e6602da2ad74d6" + "treatment_mCherry_hND6__test_deseq2.deseq2.model.txt:md5,d2113d82b76046c319e6602da2ad74d6" + ], + [ + { + "id": "treatment_mCherry_hND6_sample_number_test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "sample_number", + "method": "deseq2" + }, + "treatment_mCherry_hND6_sample_number_test_deseq2.deseq2.model.txt:md5,fa05126a58cb67c107d45426b0bdea83" ] ], [ + "versions.yml:md5,2c0576aefff8da32c7c0cfd8529aa4b5", "versions.yml:md5,2c0576aefff8da32c7c0cfd8529aa4b5" ] ], "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.4" + "nf-test": "0.9.2", + "nextflow": "24.10.2" }, - "timestamp": "2024-11-12T12:08:02.729948" + "timestamp": "2024-12-07T16:32:17.301539736" }, "deseq2 - with transcript lengths": { "content": [ [ [ { - "id": "treatment_mCherry_hND6_", + "id": "treatment_mCherry_hND6__test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "", + "method": "deseq2" + }, + "treatment_mCherry_hND6__test_deseq2.deseq2.results.tsv:md5,944176b73455aa7c8de3ec32c03edef6" + ], + [ + { + "id": "treatment_mCherry_hND6_sample_number_test", "variable": "treatment", "reference": "mCherry", "target": "hND6", - "blocking": "" + "blocking": "sample_number", + "method": "deseq2" }, - "treatment_mCherry_hND6_.deseq2.results.tsv:md5,944176b73455aa7c8de3ec32c03edef6" + "treatment_mCherry_hND6_sample_number_test_deseq2.deseq2.results.tsv:md5,6a9bc76c9d54034c90fa159372f97516" ] ], [ [ { - "id": "treatment_mCherry_hND6_", + "id": "treatment_mCherry_hND6__test", "variable": "treatment", "reference": "mCherry", "target": "hND6", - "blocking": "" + "blocking": "", + "method": "deseq2", + "fc_threshold": 1.5, + "padj_threshold": 0.05 }, - "treatment_mCherry_hND6__filtered.tsv:md5,a48241fb5f24d961d3cce208f060b624" + "treatment_mCherry_hND6__test_filtered.tsv:md5,a48241fb5f24d961d3cce208f060b624" + ], + [ + { + "id": "treatment_mCherry_hND6_sample_number_test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "sample_number", + "method": "deseq2", + "fc_threshold": 1.5, + "padj_threshold": 0.05 + }, + "treatment_mCherry_hND6_sample_number_test_filtered.tsv:md5,04d6911dce789f284c929694aa3d99b0" ] ], [ [ { - "id": "treatment_mCherry_hND6_", + "id": "treatment_mCherry_hND6__test", "variable": "treatment", "reference": "mCherry", "target": "hND6", - "blocking": "" + "blocking": "", + "method": "deseq2" }, - "treatment_mCherry_hND6_.normalised_counts.tsv:md5,7050f44c460cc13e3f101d048d503527" + "treatment_mCherry_hND6__test_deseq2_voom_norm.normalised_counts.tsv:md5,7050f44c460cc13e3f101d048d503527" ] ], [ [ { - "id": "treatment_mCherry_hND6_", + "id": "treatment_mCherry_hND6__test", "variable": "treatment", "reference": "mCherry", "target": "hND6", - "blocking": "" + "blocking": "", + "method": "deseq2" }, - "treatment_mCherry_hND6_.rlog.tsv:md5,22a4b117246b2317e0f4daf7919703f2" + "treatment_mCherry_hND6__test_deseq2_voom_norm.rlog.tsv:md5,22a4b117246b2317e0f4daf7919703f2" ] ], [ [ { - "id": "treatment_mCherry_hND6_", + "id": "treatment_mCherry_hND6__test", + "variable": "treatment", + "reference": "mCherry", + "target": "hND6", + "blocking": "", + "method": "deseq2" + }, + "treatment_mCherry_hND6__test_deseq2.deseq2.model.txt:md5,d2113d82b76046c319e6602da2ad74d6" + ], + [ + { + "id": "treatment_mCherry_hND6_sample_number_test", "variable": "treatment", "reference": "mCherry", "target": "hND6", - "blocking": "" + "blocking": "sample_number", + "method": "deseq2" }, - "treatment_mCherry_hND6_.deseq2.model.txt:md5,d2113d82b76046c319e6602da2ad74d6" + "treatment_mCherry_hND6_sample_number_test_deseq2.deseq2.model.txt:md5,fa05126a58cb67c107d45426b0bdea83" ] ], [ + "versions.yml:md5,2c0576aefff8da32c7c0cfd8529aa4b5", "versions.yml:md5,2c0576aefff8da32c7c0cfd8529aa4b5" ] ], "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.4" + "nf-test": "0.9.2", + "nextflow": "24.10.2" }, - "timestamp": "2024-11-12T12:09:42.281946" + "timestamp": "2024-12-07T16:34:05.584119298" } } \ No newline at end of file