diff --git a/.github/workflows/nf-test.yml b/.github/workflows/nf-test.yml index 9d51ba5f..b3ffe5b7 100644 --- a/.github/workflows/nf-test.yml +++ b/.github/workflows/nf-test.yml @@ -72,12 +72,12 @@ jobs: profile: [conda, docker, singularity] isMain: - ${{ github.base_ref == 'master' || github.base_ref == 'main' }} - # Exclude conda and ~singularity~ on dev + # Exclude conda and singularity on dev exclude: - isMain: false profile: "conda" - # - isMain: false - # profile: "singularity" + - isMain: false + profile: "singularity" NXF_VER: - 26.04.0 - latest-everything diff --git a/CHANGELOG.md b/CHANGELOG.md index c4b5431d..88e8f786 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - #272 - Add a `test_leafcutter` profile and a pipeline level nf-test for `--leafcutter`, which had no test coverage at all (by @piplus2) - #272 - Add the `STRAND_JUNCTIONS` module, which gives the junctions of an unstranded sample a strand from the splice motif and the annotation (by @piplus2) - #264 - Add an nf-test for the `DEXSEQ_DTU` module, which had no test coverage (by @piplus2) +- #281 - Add an nf-test for the `ISOFORMSWITCHANALYZER` module, which had no test coverage (by @piplus2) +- #281 - Redraw the pipeline metro map with [nf-metro](https://github.com/seqeralabs/nf-metro), now covering LeafCutter, the `--source` inputs and the coverage tracks. The `.mmd` source lives next to the SVG in `docs/images/` (by @piplus2) ### Changed @@ -53,6 +55,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - #268 - Move the `EDGER_DEU` subworkflow to the nf-core subworkflow template. It now emits the `EDGER_EXON` results and the featureCounts tables, which it ran but discarded (by @piplus2) - #270 - Move the `LEAFCUTTER` subworkflow to the nf-core subworkflow template. Its `juncs` output is now the per sample `[ meta, junc ]` tuples, instead of a single list flattening the meta maps in with the paths (by @piplus2) - #272 - `--leafcutter` now works with `--source genome_bam`, whatever the strandedness, since the junction strand no longer has to come from the alignment. For unstranded libraries the clusters differ slightly from LeafCutter's documented STAR route, see the LeafCutter section of `docs/usage.md` (by @piplus2) +- #281 - Refactor `ISOFORMSWITCHANALYZER` to the nf-core module template and update `IsoformSwitchAnalyzeR` 2.2.0 -> 2.12.0 (R 4.3 -> 4.5). `bin/run_isoformswitchanalyzer.R` is now a module template (by @piplus2) ### Fixed @@ -93,6 +96,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - #272 - Fix `--leafcutter` clustering nothing. regtools now takes the junction strand from the samplesheet `strandedness`, and the junctions of an unstranded sample are stranded afterwards from the splice motif and the annotation, so the BAM files the other analyses read are left alone (by @piplus2) - #275 - Fix missing yaml from singularity container in `GTFGENEFILTER` (reported by @srira25, fix by @piplus2) - #277 - Fix missing pyyaml from singularity container in `STRAND_JUNCTIONS`, `CLUSTERGROUPS` and `MISOPY_SETTINGS` (by @piplus2) +- #281 - Fix `ISOFORMSWITCHANALYZER` writing `common_switch_consequences.pdf` next to the `results` directory instead of inside it, so it was never published (by @piplus2) ## v1.0.5 - 2024-11-03 diff --git a/README.md b/README.md index 3abf3182..57c69c26 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ **nf-core/rnasplice** is a bioinformatics pipeline for alternative splicing analysis of RNA sequencing data obtained from organisms with a reference genome and annotation. -![nf-core/rnasplice metro map](docs/rnasplice_map.png) +![nf-core/rnasplice metro map](docs/images/nf-core-rnasplice_metro_map.svg) 1. Merge re-sequenced FastQ files ([`cat`](http://www.linfo.org/cat.html)) 2. Read QC ([`FastQC`](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/)) diff --git a/docs/images/nf-core-rnasplice_metro_map.mmd b/docs/images/nf-core-rnasplice_metro_map.mmd new file mode 100644 index 00000000..e7fbef3b --- /dev/null +++ b/docs/images/nf-core-rnasplice_metro_map.mmd @@ -0,0 +1,115 @@ +%%metro title: nf-core/rnasplice +%%metro logo: nf-core-rnasplice_logo_light.png | nf-core-rnasplice_logo_dark.png +%%metro style: nfcore +%%metro center_ports: true +%%metro compact_offsets: true +%%metro files: fastq_in | FASTQ | Reads +%%metro file: genome_bam_in | BAM | Genome +%%metro file: transcriptome_bam_in | BAM | Transcriptome +%%metro dir: salmon_in | Salmon | Quant +%%metro file: bigwig_out | BW | Coverage +%%metro file: report_out | HTML | MultiQC +%%metro off_track: genome_bam_in, transcriptome_bam_in, salmon_in +%%metro line: deu | Differential exon usage (DEU) | #e63946 +%%metro line: dtu | Differential transcript usage (DTU) | #4361ee +%%metro line: event | Event-based splicing | #f39c12 +%%metro line: qc | QC & visualisation | #2db572 +%%metro legend: bl +%%metro grid: preprocessing | 0,0 +%%metro grid: alignment | 1,0 +%%metro grid: quantification | 1,1 +%%metro grid: exon_analysis | 2,0 +%%metro grid: transcript_analysis | 2,1 +%%metro grid: reporting | 3,0 + +graph LR + subgraph preprocessing [Pre-processing] + fastq_in[ ] + cat_fastq[cat] + fastqc_raw[FastQC] + trimgalore[Trim Galore!] + fastqc_trim[FastQC] + + fastq_in -->|deu,dtu,event,qc| cat_fastq + cat_fastq -->|deu,dtu,event,qc| fastqc_raw + fastqc_raw -->|deu,dtu,event,qc| trimgalore + trimgalore -->|deu,dtu,event,qc| fastqc_trim + end + + subgraph alignment [Genome alignment] + genome_bam_in[ ] + star[STAR] + samtools[SAMtools] + + star -->|deu,event,qc| samtools + genome_bam_in -->|deu,event,qc| samtools + end + + subgraph quantification [Transcript quantification] + transcriptome_bam_in[ ] + salmon_in[ ] + salmon[Salmon] + tximport[tximport] + + transcriptome_bam_in -->|dtu,event| salmon + salmon -->|dtu,event| tximport + salmon_in -->|dtu,event| tximport + end + + subgraph exon_analysis [Exon & junction based analysis] + htseq[HTSeq] + dexseq_exon[DEXSeq] + featurecounts[featureCounts] + edger[edgeR] + rmats[rMATS] + regtools[regtools] + leafcutter[LeafCutter] + + htseq -->|deu| dexseq_exon + featurecounts -->|deu| edger + regtools -->|event| leafcutter + end + + subgraph transcript_analysis [Transcript based analysis] + drimseq[DRIMSeq] + dexseq_dtu[DEXSeq] + stager[stageR] + isar[IsoformSwitchAnalyzeR] + suppa_events[SUPPA events] + suppa_psi[SUPPA PSI] + suppa_diff[SUPPA diffSplice] + suppa_cluster[SUPPA cluster] + + drimseq -->|dtu| dexseq_dtu + dexseq_dtu -->|dtu| stager + suppa_events -->|event| suppa_psi + suppa_psi -->|event| suppa_diff + suppa_diff -->|event| suppa_cluster + end + + subgraph reporting [Visualisation & reporting] + miso[MISO sashimi] + bedtools[BEDTools] + bigwig[bedGraphToBigWig] + multiqc[MultiQC] + bigwig_out[ ] + report_out[ ] + + bedtools -->|qc| bigwig + bigwig -->|qc| bigwig_out + multiqc -->|qc| report_out + end + + %% Inter-section edges + fastqc_trim -->|deu,dtu,event,qc| star + star -->|dtu,event| salmon + samtools -->|deu| htseq + samtools -->|deu| featurecounts + samtools -->|event| rmats + samtools -->|event| regtools + samtools -->|qc| miso + samtools -->|qc| bedtools + samtools -->|qc| multiqc + tximport -->|dtu| drimseq + tximport -->|event| suppa_events + salmon -->|dtu| isar diff --git a/docs/images/nf-core-rnasplice_metro_map.svg b/docs/images/nf-core-rnasplice_metro_map.svg new file mode 100644 index 00000000..7d92bffd --- /dev/null +++ b/docs/images/nf-core-rnasplice_metro_map.svg @@ -0,0 +1,337 @@ + + + + +{"groups":[{"color":"#e63946","id":"deu","label":"Differential exon usage (DEU)"},{"color":"#4361ee","id":"dtu","label":"Differential transcript usage (DTU)"},{"color":"#f39c12","id":"event","label":"Event-based splicing"},{"color":"#2db572","id":"qc","label":"QC & visualisation"}],"height":999,"match":{"flags":"i","target":"fqProcessName","type":"regex"},"nodes":[{"groups":["deu","dtu","event","qc"],"h":22.0,"id":"fastq_in","label":"fastq_in","patterns":[],"r":5.0,"region":"preprocessing","rx":5.0,"w":10.0,"x":80.0,"y":164.4},{"groups":["deu","dtu","event","qc"],"h":22.0,"id":"cat_fastq","label":"cat","patterns":[],"r":5.0,"region":"preprocessing","rx":5.0,"w":10.0,"x":140.0,"y":164.4},{"groups":["deu","dtu","event","qc"],"h":22.0,"id":"fastqc_raw","label":"FastQC","patterns":[],"r":5.0,"region":"preprocessing","rx":5.0,"w":10.0,"x":200.0,"y":164.4},{"groups":["deu","dtu","event","qc"],"h":22.0,"id":"trimgalore","label":"Trim Galore!","patterns":[],"r":5.0,"region":"preprocessing","rx":5.0,"w":10.0,"x":260.0,"y":164.4},{"groups":["deu","dtu","event","qc"],"h":22.0,"id":"fastqc_trim","label":"FastQC","patterns":[],"r":5.0,"region":"preprocessing","rx":5.0,"w":10.0,"x":320.0,"y":164.4},{"groups":["deu","event","qc"],"h":18.0,"id":"genome_bam_in","label":"genome_bam_in","patterns":[],"r":5.0,"region":"alignment","rx":5.0,"w":10.0,"x":472.0,"y":104.0},{"groups":["deu","dtu","event","qc"],"h":22.0,"id":"star","label":"STAR","patterns":[],"r":5.0,"region":"alignment","rx":5.0,"w":10.0,"x":472.0,"y":164.4},{"groups":["deu","event","qc"],"h":22.0,"id":"samtools","label":"SAMtools","patterns":[],"r":5.0,"region":"alignment","rx":5.0,"w":10.0,"x":592.0,"y":164.4},{"groups":["dtu","event"],"h":14.0,"id":"transcriptome_bam_in","label":"transcriptome_bam_in","patterns":[],"r":5.0,"region":"quantification","rx":5.0,"w":10.0,"x":472.0,"y":602.0},{"groups":["dtu","event"],"h":14.0,"id":"salmon_in","label":"salmon_in","patterns":[],"r":5.0,"region":"quantification","rx":5.0,"w":10.0,"x":472.0,"y":543.6},{"groups":["dtu","event"],"h":14.0,"id":"salmon","label":"Salmon","patterns":[],"r":5.0,"region":"quantification","rx":5.0,"w":10.0,"x":568.0,"y":660.4},{"groups":["dtu","event"],"h":14.0,"id":"tximport","label":"tximport","patterns":[],"r":5.0,"region":"quantification","rx":5.0,"w":10.0,"x":664.0,"y":660.4},{"groups":["deu"],"h":10.0,"id":"htseq","label":"HTSeq","patterns":[],"r":5.0,"region":"exon_analysis","rx":5.0,"w":10.0,"x":910.0,"y":158.4},{"groups":["deu"],"h":10.0,"id":"dexseq_exon","label":"DEXSeq","patterns":[],"r":5.0,"region":"exon_analysis","rx":5.0,"w":10.0,"x":970.0,"y":158.4},{"groups":["deu"],"h":10.0,"id":"featurecounts","label":"featureCounts","patterns":[],"r":5.0,"region":"exon_analysis","rx":5.0,"w":10.0,"x":910.0,"y":216.8},{"groups":["deu"],"h":10.0,"id":"edger","label":"edgeR","patterns":[],"r":5.0,"region":"exon_analysis","rx":5.0,"w":10.0,"x":970.0,"y":216.8},{"groups":["event"],"h":10.0,"id":"rmats","label":"rMATS","patterns":[],"r":5.0,"region":"exon_analysis","rx":5.0,"w":10.0,"x":910.0,"y":333.6},{"groups":["event"],"h":10.0,"id":"regtools","label":"regtools","patterns":[],"r":5.0,"region":"exon_analysis","rx":5.0,"w":10.0,"x":910.0,"y":275.2},{"groups":["event"],"h":10.0,"id":"leafcutter","label":"LeafCutter","patterns":[],"r":5.0,"region":"exon_analysis","rx":5.0,"w":10.0,"x":970.0,"y":275.2},{"groups":["dtu"],"h":10.0,"id":"drimseq","label":"DRIMSeq","patterns":[],"r":5.0,"region":"transcript_analysis","rx":5.0,"w":10.0,"x":862.5,"y":658.4},{"groups":["dtu"],"h":10.0,"id":"dexseq_dtu","label":"DEXSeq","patterns":[],"r":5.0,"region":"transcript_analysis","rx":5.0,"w":10.0,"x":922.5,"y":658.4},{"groups":["dtu"],"h":10.0,"id":"stager","label":"stageR","patterns":[],"r":5.0,"region":"transcript_analysis","rx":5.0,"w":10.0,"x":982.5,"y":658.4},{"groups":["dtu"],"h":10.0,"id":"isar","label":"IsoformSwitchAnalyzeR","patterns":[],"r":5.0,"region":"transcript_analysis","rx":5.0,"w":10.0,"x":862.5,"y":775.2},{"groups":["event"],"h":10.0,"id":"suppa_events","label":"SUPPA events","patterns":[],"r":5.0,"region":"transcript_analysis","rx":5.0,"w":10.0,"x":862.5,"y":716.8},{"groups":["event"],"h":10.0,"id":"suppa_psi","label":"SUPPA PSI","patterns":[],"r":5.0,"region":"transcript_analysis","rx":5.0,"w":10.0,"x":922.5,"y":716.8},{"groups":["event"],"h":10.0,"id":"suppa_diff","label":"SUPPA diffSplice","patterns":[],"r":5.0,"region":"transcript_analysis","rx":5.0,"w":10.0,"x":982.5,"y":716.8},{"groups":["event"],"h":10.0,"id":"suppa_cluster","label":"SUPPA cluster","patterns":[],"r":5.0,"region":"transcript_analysis","rx":5.0,"w":10.0,"x":1042.5,"y":716.8},{"groups":["qc"],"h":10.0,"id":"miso","label":"MISO sashimi","patterns":[],"r":5.0,"region":"reporting","rx":5.0,"w":10.0,"x":1241.0,"y":333.6},{"groups":["qc"],"h":10.0,"id":"bedtools","label":"BEDTools","patterns":[],"r":5.0,"region":"reporting","rx":5.0,"w":10.0,"x":1241.0,"y":216.8},{"groups":["qc"],"h":10.0,"id":"bigwig","label":"bedGraphToBigWig","patterns":[],"r":5.0,"region":"reporting","rx":5.0,"w":10.0,"x":1301.0,"y":216.8},{"groups":["qc"],"h":10.0,"id":"multiqc","label":"MultiQC","patterns":[],"r":5.0,"region":"reporting","rx":5.0,"w":10.0,"x":1241.0,"y":275.2},{"groups":["qc"],"h":10.0,"id":"bigwig_out","label":"bigwig_out","patterns":[],"r":5.0,"region":"reporting","rx":5.0,"w":10.0,"x":1361.0,"y":216.8},{"groups":["qc"],"h":10.0,"id":"report_out","label":"report_out","patterns":[],"r":5.0,"region":"reporting","rx":5.0,"w":10.0,"x":1301.0,"y":275.2}],"regions":[{"id":"preprocessing","label":"Pre-processing"},{"id":"alignment","label":"Genome alignment"},{"id":"quantification","label":"Transcript quantification"},{"id":"exon_analysis","label":"Exon & junction based analysis"},{"id":"transcript_analysis","label":"Transcript based analysis"},{"id":"reporting","label":"Visualisation & reporting"}],"title":"nf-core/rnasplice","version":"1.0","width":1479} + + + + +1 +Pre-processing + + +2 +Genome alignment + + +5 +Transcript quantification + + +3 +Exon & junction based analysis + + +6 +Transcript based analysis + + +4 +Visualisation &reporting + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +FASTQ +Reads + + + + + + + + + + + + + + + + + + + + +BAM +Genome + + + + + + + + + + + + + + +BAM +Transcriptome + + + + + + + +Salmon +Quant + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +BW +Coverage + + + + + + + + +HTML +MultiQC + + + +HTSeq + +STAR + +featureCounts + +DRIMSeq + +regtools + +SUPPA events + +rMATS + +BEDTools + +IsoformSwitchAnalyzeR + +MultiQC + +MISO sashimi + +DEXSeq + +cat + +SAMtools + +edgeR + +Salmon + +DEXSeq + +LeafCutter + +SUPPA PSI + +bedGraphToBigWig + +FastQC + +tximport + +stageR + +SUPPA diffSplice + +Trim Galore! + +SUPPA cluster + +FastQC + + + + + +Differential exon usage (DEU) + +Differential transcript usage (DTU) + +Event-based splicing + +QC & visualisation +created with nf-metro v2.0.0+dev + diff --git a/docs/output.md b/docs/output.md index 86c1b9f5..28ba7560 100644 --- a/docs/output.md +++ b/docs/output.md @@ -413,13 +413,14 @@ Finally, this portion of the pipeline will run [stageR](https://bioconductor.org - `02_switch_plot_gene5.pdf` - `03_switch_plot_gene6.pdf` - `...` + - `common_switch_consequences.pdf` - `isoformswitchanalyzer_isoformfeatures.csv` - `isoformswitchanalyzer_summary.csv` - `switchlist.rds` -If [IsoformSwitchAnalyzeR](https://www.bioconductor.org/packages/release/bioc/html/IsoformSwitchAnalyzeR.html) finds genes with isoform switches, it produces plots visualizing these switches. A separate set of switch plots is created for each contrast if that contrast contains significant switches. Further two .csv files are created, a summary and the main results. The .rds of the main R list is also returned. +If [IsoformSwitchAnalyzeR](https://www.bioconductor.org/packages/release/bioc/html/IsoformSwitchAnalyzeR.html) finds genes with isoform switches, it produces plots visualizing these switches. A separate set of switch plots is created for each contrast if that contrast contains significant switches, and `common_switch_consequences.pdf` summarises the consequences of the switches across all contrasts. Further two .csv files are created, a summary and the main results. The .rds of the main R list is also returned. ## Event-based analysis diff --git a/docs/rnasplice_map.png b/docs/rnasplice_map.png deleted file mode 100644 index ef4855dd..00000000 Binary files a/docs/rnasplice_map.png and /dev/null differ diff --git a/modules/local/isoformswitchanalyzer/environment.yml b/modules/local/isoformswitchanalyzer/environment.yml new file mode 100644 index 00000000..e42fad17 --- /dev/null +++ b/modules/local/isoformswitchanalyzer/environment.yml @@ -0,0 +1,7 @@ +--- +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/environment-schema.json +channels: + - conda-forge + - bioconda +dependencies: + - "bioconda::bioconductor-isoformswitchanalyzer=2.12.0" diff --git a/modules/local/isoformswitchanalyzer/main.nf b/modules/local/isoformswitchanalyzer/main.nf index 92249de8..4f2d9d0d 100644 --- a/modules/local/isoformswitchanalyzer/main.nf +++ b/modules/local/isoformswitchanalyzer/main.nf @@ -1,37 +1,47 @@ process ISOFORMSWITCHANALYZER { label 'process_medium' - conda "bioconda::bioconductor-isoformswitchanalyzer==2.2.0" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/bioconductor-isoformswitchanalyzer:2.2.0--r43ha9d7317_0' : - 'biocontainers/bioconductor-isoformswitchanalyzer:2.2.0--r43ha9d7317_0' }" + conda "${moduleDir}/environment.yml" + container "${workflow.containerEngine in ['singularity', 'apptainer'] && !task.ext.singularity_pull_docker_container + ? 'https://depot.galaxyproject.org/singularity/bioconductor-isoformswitchanalyzer:2.12.0--r45hd2fad28_0' + : 'biocontainers/bioconductor-isoformswitchanalyzer:2.12.0--r45hd2fad28_0'}" input: - path salmon_output - path gtf // path to gtf file - path transcript_sequences // path to isoform nt sequences fasta - path samplesheet // path samplesheet - path contrastsheet // path contrastsheet - val alpha // alpha value for differential isoform expression - val dIF // dIF cutoff value for differential isoform expression + path salmon_output // path: one Salmon quant directory per sample + path gtf // path: /path/to/genes.gtf + path transcript_sequences // path: /path/to/transcripts.fa (isoform nucleotide sequences) + path samplesheet // path: /path/to/samplesheet.csv + path contrastsheet // path: /path/to/contrastsheet.csv + val alpha // val: FDR cutoff for the isoform switch test + val dIF // val: minimum absolute difference in isoform fraction output: - path "isoformswitchanalyzer_summary.csv" , emit: isoformswitchanalyzer_summary - path "isoformswitchanalyzer_isoformfeatures.csv" , emit: isoformswitchanalyzer_isoformFeatures - path "switchlist.rds" , emit: switchlist_rds - path "results" , emit: results - tuple val("${task.process}"), val('r-base'), eval('R --version 2>&1 | head -n 1 | sed "s/^.*version //; s/ .*$//"'), topic: versions, emit: versions_R - tuple val("${task.process}"), val('bioconductor-isoformswitchanalyzer'), eval('Rscript -e "library(IsoformSwitchAnalyzeR); cat(as.character(packageVersion(\'IsoformSwitchAnalyzeR\')))"'), topic: versions, emit: versions_isoformswitchanalyzer - + path "isoformswitchanalyzer_summary.csv" , emit: isoformswitchanalyzer_summary + path "isoformswitchanalyzer_isoformfeatures.csv", emit: isoformswitchanalyzer_isoformFeatures + path "switchlist.rds" , emit: switchlist_rds + path "results" , emit: results + path "versions.yml" , topic: versions, emit: versions_isoformswitchanalyzer when: task.ext.when == null || task.ext.when script: + template 'run_isoformswitchanalyzer.R' + + stub: def args = task.ext.args ?: '' """ + echo ${args} + mkdir -p results + touch isoformswitchanalyzer_summary.csv + touch isoformswitchanalyzer_isoformfeatures.csv + touch switchlist.rds - run_isoformswitchanalyzer.R ${gtf} ${transcript_sequences} ${samplesheet} ${contrastsheet} ${alpha} ${dIF} ${args} + cat <<-END_VERSIONS > versions.yml + "${task.process}": + r-base: \$(R --version 2>&1 | head -n 1 | sed 's/^.*version //; s/ .*\$//') + bioconductor-isoformswitchanalyzer: \$(Rscript -e "library(IsoformSwitchAnalyzeR); cat(as.character(packageVersion('IsoformSwitchAnalyzeR')))") + END_VERSIONS """ } diff --git a/modules/local/isoformswitchanalyzer/meta.yml b/modules/local/isoformswitchanalyzer/meta.yml new file mode 100644 index 00000000..3e4f35fe --- /dev/null +++ b/modules/local/isoformswitchanalyzer/meta.yml @@ -0,0 +1,116 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/meta-schema.json +name: "isoformswitchanalyzer" +description: Identify isoform switches between conditions from Salmon quantifications + with IsoformSwitchAnalyzeR, predict their functional consequences and plot the top + switching genes of every contrast +keywords: + - IsoformSwitchAnalyzeR + - isoform switch + - differential transcript usage + - alternative splicing + - salmon +tools: + - "IsoformSwitchAnalyzeR": + description: "Analysis of alternative splicing and isoform switches with predicted + functional consequences (e.g. gain/loss of protein domains etc.) from quantification + of all types of RNASeq by tools such as Kallisto, Salmon, StringTie, Cufflinks/Cuffdiff + etc." + homepage: "https://bioconductor.org/packages/IsoformSwitchAnalyzeR" + documentation: "https://bioconductor.org/packages/release/bioc/vignettes/IsoformSwitchAnalyzeR/inst/doc/IsoformSwitchAnalyzeR.html" + tool_dev_url: "https://github.com/kvittingseerup/IsoformSwitchAnalyzeR" + doi: "10.1093/bioinformatics/btz247" + licence: ["GPL-2.0-or-later"] + identifier: biotools:isoformswitchanalyzer + +input: + - salmon_output: + type: directory + description: One Salmon quant directory per sample, each holding a `quant.sf`. + The directory names must match the `sample` column of the samplesheet + pattern: "*" + - gtf: + type: file + description: GTF annotation the transcripts were quantified against + pattern: "*.gtf" + ontologies: + - edam: "http://edamontology.org/format_2306" # GTF + - transcript_sequences: + type: file + description: Nucleotide sequences of the annotated transcripts, named by + transcript id as in the GTF + pattern: "*.{fa,fasta}" + ontologies: + - edam: "http://edamontology.org/format_1929" # FASTA + - samplesheet: + type: file + description: Comma-separated sample sheet with at least `sample` and `condition` + column headers + pattern: "*.csv" + ontologies: + - edam: "http://edamontology.org/format_3752" # CSV + - contrastsheet: + type: file + description: Comma-separated contrast sheet with `treatment` and `control` + column headers naming the conditions to compare. Every pair of conditions + is compared when the file does not exist + pattern: "*.csv" + ontologies: + - edam: "http://edamontology.org/format_3752" # CSV + - alpha: + type: float + description: FDR cutoff below which an isoform switch is called significant + - dIF: + type: float + description: Minimum absolute difference in isoform fraction between the two + conditions for an isoform switch to be called + +output: + isoformswitchanalyzer_summary: + - "isoformswitchanalyzer_summary.csv": + type: file + description: Number of switching isoforms, switches and genes per contrast + and combined. Holds the error message instead when no switch was found + pattern: "isoformswitchanalyzer_summary.csv" + ontologies: + - edam: "http://edamontology.org/format_3752" # CSV + isoformswitchanalyzer_isoformFeatures: + - "isoformswitchanalyzer_isoformfeatures.csv": + type: file + description: The `isoformFeatures` table of the switchAnalyzeRlist with the + expression, isoform fraction, switch q-value and consequences of every + isoform in every contrast. Empty when no switch was found + pattern: "isoformswitchanalyzer_isoformfeatures.csv" + ontologies: + - edam: "http://edamontology.org/format_3752" # CSV + switchlist_rds: + - "switchlist.rds": + type: file + description: Serialised switchAnalyzeRlist object + pattern: "switchlist.rds" + results: + - "results": + type: directory + description: One subdirectory per contrast with a switch plot PDF per switching + gene, plus the common switch consequences PDF + pattern: "results" + versions_isoformswitchanalyzer: + - "versions.yml": + type: file + description: File containing software versions + pattern: "versions.yml" + ontologies: + - edam: "http://edamontology.org/format_3750" # YAML + +topics: + versions: + - "versions.yml": + type: file + description: File containing software versions + pattern: "versions.yml" + ontologies: + - edam: "http://edamontology.org/format_3750" # YAML +authors: + - "@bensouthgate" + - "@piplus2" +maintainers: + - "@piplus2" diff --git a/bin/run_isoformswitchanalyzer.R b/modules/local/isoformswitchanalyzer/templates/run_isoformswitchanalyzer.R old mode 100755 new mode 100644 similarity index 79% rename from bin/run_isoformswitchanalyzer.R rename to modules/local/isoformswitchanalyzer/templates/run_isoformswitchanalyzer.R index d0d29013..b6e256a9 --- a/bin/run_isoformswitchanalyzer.R +++ b/modules/local/isoformswitchanalyzer/templates/run_isoformswitchanalyzer.R @@ -1,4 +1,5 @@ #!/usr/bin/env Rscript + # Scripts adjusted from Bioconductor and IsoformSwitchAnalyzeR source code # Please see following for details: # https://bioconductor.org/packages/devel/bioc/vignettes/IsoformSwitchAnalyzeR/inst/doc/IsoformSwitchAnalyzeR.html @@ -6,23 +7,29 @@ # https://github.com/kvittingseerup/IsoformSwitchAnalyzeR # respectivly -# Parse command arguments +# NOTE: This file is a Nextflow template. Nextflow interpolates the process +# variables and treats the backslash as an escape character, therefore +# R's list/data.frame operator must be written as a backslash followed +# by a dollar sign, and every literal backslash must be doubled. + -argv <- commandArgs(trailingOnly = TRUE) +# Read the process inputs -salmon_output <- getwd() +salmon_output <- getwd() # every Salmon quant directory is staged here -gtf <- argv[1] +gtf <- '$gtf' -transcript_sequences <- argv[2] +transcript_sequences <- '$transcript_sequences' -samplesheet <- argv[3] +samplesheet <- '$samplesheet' -contrastsheet <- argv[4] +contrastsheet <- '$contrastsheet' -alpha <- as.numeric(argv[5]) # Must be >= 0 and <= 1 +alpha <- as.numeric('$alpha') # Must be >= 0 and <= 1 -dIFcutoff <- as.numeric(argv[6]) # Must be >= 0 and <= 1 +dIFcutoff <- as.numeric('$dIF') # Must be >= 0 and <= 1 + +pathToOutput <- 'results' # Attach required packages @@ -42,7 +49,7 @@ isoformSwitchAnalysisPart1 <- function( alpha = NULL, dIFcutoff = NULL ) { - isConditional <- switchAnalyzeRlist$sourceId != 'preDefinedSwitches' + isConditional <- switchAnalyzeRlist\$sourceId != 'preDefinedSwitches' # preFilter if(isConditional) { @@ -60,7 +67,7 @@ isoformSwitchAnalysisPart1 <- function( # Test isoform switches if(isConditional) { - if(any( switchAnalyzeRlist$conditions$nrReplicates > 5)) { + if(any( switchAnalyzeRlist\$conditions\$nrReplicates > 5)) { switchAnalyzeRlist <- isoformSwitchTestSatuRn( switchAnalyzeRlist, @@ -81,7 +88,7 @@ isoformSwitchAnalysisPart1 <- function( ) } - if (nrow(switchAnalyzeRlist$isoformSwitchAnalysis) == 0) { + if (nrow(switchAnalyzeRlist\$isoformSwitchAnalysis) == 0) { stop('No isoform switches were identified with the current cutoffs.') } } @@ -89,7 +96,7 @@ isoformSwitchAnalysisPart1 <- function( # Predict ORF - if ( is.null(switchAnalyzeRlist$orfAnalysis) ) { + if ( is.null(switchAnalyzeRlist\$orfAnalysis) ) { # Add known annoation @@ -101,7 +108,7 @@ isoformSwitchAnalysisPart1 <- function( # Predict novel once (if any are missing) - if ( any( switchAnalyzeRlist$orfAnalysis$orf_origin == 'not_annotated_yet' )) { + if ( any( switchAnalyzeRlist\$orfAnalysis\$orf_origin == 'not_annotated_yet' )) { switchAnalyzeRlist <- analyzeNovelIsoformORF( switchAnalyzeRlist = switchAnalyzeRlist, analysisAllIsoformsWithoutORF = TRUE, @@ -182,11 +189,7 @@ isoformSwitchAnalysisPart2 <- function( # Make overall consequences pdf( - file = paste( - pathToOutput, - 'common_switch_consequences.pdf', - sep = '' - ), + file = file.path(pathToOutput, 'common_switch_consequences.pdf'), width = 10, height = 7 ) @@ -249,6 +252,11 @@ isoformSwitchAnalysisCombined <- function( } +# Create the output directory + +dir.create(pathToOutput, showWarnings = FALSE) + + # Load Salmon output salmonQuant <- importIsoformExpression( @@ -287,8 +295,8 @@ if (file.exists(contrastsheet)) { # Build swtich list SwitchList <- importRdata( - isoformCountMatrix = salmonQuant$counts, - isoformRepExpression = salmonQuant$abundance, + isoformCountMatrix = salmonQuant\$counts, + isoformRepExpression = salmonQuant\$abundance, designMatrix = design, isoformExonAnnoation = gtf, isoformNtFasta = transcript_sequences, @@ -305,7 +313,7 @@ tryCatch({ SwitchList <- isoformSwitchAnalysisCombined( SwitchList, pathToGTF = gtf, - pathToOutput = "results", + pathToOutput = pathToOutput, alpha = alpha, dIFcutoff = dIFcutoff ) @@ -322,7 +330,7 @@ tryCatch({ # Save isoformFeatures as csv - write.csv(SwitchList$isoformFeatures, "isoformswitchanalyzer_isoformfeatures.csv") + write.csv(SwitchList\$isoformFeatures, "isoformswitchanalyzer_isoformfeatures.csv") }, error = function(e) { @@ -348,10 +356,20 @@ tryCatch({ saveRDS(SwitchList, "switchlist.rds") -#################################### -########### Session info ########### -#################################### +# Save the software versions to disk + +writeLines( + c( + '"${task.process}":', + paste0(" r-base: ", paste(R.version[["major"]], R.version[["minor"]], sep = ".")), + paste0(" bioconductor-isoformswitchanalyzer: ", as.character(packageVersion("IsoformSwitchAnalyzeR"))) + ), + "versions.yml" +) + + +# Print session information -# Print sessioninfo to standard out citation("IsoformSwitchAnalyzeR") + sessionInfo() diff --git a/modules/local/isoformswitchanalyzer/tests/main.nf.test b/modules/local/isoformswitchanalyzer/tests/main.nf.test new file mode 100644 index 00000000..8005b520 --- /dev/null +++ b/modules/local/isoformswitchanalyzer/tests/main.nf.test @@ -0,0 +1,165 @@ +// nf-core modules test isoformswitchanalyzer +nextflow_process { + + name "Test Process ISOFORMSWITCHANALYZER" + script "../main.nf" + process "ISOFORMSWITCHANALYZER" + config "./nextflow.config" + + tag "modules" + tag "modules_local" + tag "isoformswitchanalyzer" + tag "untar" + tag "gunzip" + tag "gffread" + + // The salmon_quant tarball holds one quant directory per sample (ERR188383, + // ERR188428, ERR188454, ERR204916), quantified against reference/genes_chrX.gtf. + // The test-datasets carry no transcript fasta, so it is extracted from the + // chrX genome with gffread -w, as the pipeline does with rsem-prepare-reference + setup { + run("UNTAR", alias: "UNTAR_SALMON_QUANT") { + script '../../../nf-core/untar/main.nf' + process { + """ + input[0] = [ + [ id: 'test' ], + file(params.pipelines_testdata_base_path + 'testdata/salmon_quant/salmon_quant.tar.gz', checkIfExists: true) + ] + """ + } + } + + run("GUNZIP") { + script '../../../nf-core/gunzip/main.nf' + process { + """ + input[0] = [ + [ id: 'X' ], + file(params.pipelines_testdata_base_path + 'reference/X.fa.gz', checkIfExists: true) + ] + """ + } + } + + run("GFFREAD") { + script '../../../nf-core/gffread/main.nf' + process { + """ + input[0] = [ + [ id: 'genes_chrX' ], + file(params.pipelines_testdata_base_path + 'reference/genes_chrX.gtf', checkIfExists: true) + ] + input[1] = GUNZIP.out.gunzip.map { _meta, fasta -> fasta } + """ + } + } + } + + test("human chrX - salmon quant") { + + when { + process { + """ + input[0] = UNTAR_SALMON_QUANT.out.untar + .map { _meta, dir -> file(dir.toString() + '/ERR*', type: 'dir', checkIfExists: true) } + input[1] = file(params.pipelines_testdata_base_path + 'reference/genes_chrX.gtf', checkIfExists: true) + input[2] = GFFREAD.out.gffread_fasta.map { _meta, fasta -> fasta } + input[3] = file(params.pipelines_testdata_base_path + 'samplesheet/samplesheet.csv', checkIfExists: true) + input[4] = file(params.pipelines_testdata_base_path + 'samplesheet/contrastsheet.csv', checkIfExists: true) + input[5] = 0.05 + input[6] = 0.1 + """ + } + } + + then { + // The switch summary only holds counts, so it is snapshotted by content. + // The isoform features table carries the DEXSeq statistics, which are not + // reproducible across machines, so it is snapshotted by shape: header and + // row count. The plots are listed by name, one per switching gene and + // contrast, and the binary object only by name + assert process.success + assertAll( + { assert snapshot( + process.out.isoformswitchanalyzer_summary, + process.out.isoformswitchanalyzer_isoformFeatures.collect { csv -> + def lines = path(csv).readLines() + [ file(csv).name, lines[0], lines.size() ] + }, + process.out.switchlist_rds.collect { rds -> file(rds).name }, + process.out.results.collect { dir -> + getAllFilesFromDir(dir, relative: true) + }, + process.out.findAll { key, _val -> key.startsWith('versions') } + ).match() } + ) + } + + } + + test("human chrX - salmon quant - no switches") { + + // A dIF cutoff no isoform reaches: the analysis stops after the switch test, + // the summary holds the error message and the features table is empty + when { + process { + """ + input[0] = UNTAR_SALMON_QUANT.out.untar + .map { _meta, dir -> file(dir.toString() + '/ERR*', type: 'dir', checkIfExists: true) } + input[1] = file(params.pipelines_testdata_base_path + 'reference/genes_chrX.gtf', checkIfExists: true) + input[2] = GFFREAD.out.gffread_fasta.map { _meta, fasta -> fasta } + input[3] = file(params.pipelines_testdata_base_path + 'samplesheet/samplesheet.csv', checkIfExists: true) + input[4] = file(params.pipelines_testdata_base_path + 'samplesheet/contrastsheet.csv', checkIfExists: true) + input[5] = 0.05 + input[6] = 0.99 + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + process.out.isoformswitchanalyzer_summary, + process.out.isoformswitchanalyzer_isoformFeatures, + process.out.switchlist_rds.collect { rds -> file(rds).name }, + process.out.results.collect { dir -> + getAllFilesFromDir(dir, relative: true) + }, + process.out.findAll { key, _val -> key.startsWith('versions') } + ).match() } + ) + } + + } + + test("human chrX - salmon quant - stub") { + + options "-stub" + + when { + process { + """ + input[0] = UNTAR_SALMON_QUANT.out.untar + .map { _meta, dir -> file(dir.toString() + '/ERR*', type: 'dir', checkIfExists: true) } + input[1] = file(params.pipelines_testdata_base_path + 'reference/genes_chrX.gtf', checkIfExists: true) + input[2] = GFFREAD.out.gffread_fasta.map { _meta, fasta -> fasta } + input[3] = file(params.pipelines_testdata_base_path + 'samplesheet/samplesheet.csv', checkIfExists: true) + input[4] = file(params.pipelines_testdata_base_path + 'samplesheet/contrastsheet.csv', checkIfExists: true) + input[5] = 0.05 + input[6] = 0.1 + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot(process.out).match() } + ) + } + + } + +} diff --git a/modules/local/isoformswitchanalyzer/tests/main.nf.test.snap b/modules/local/isoformswitchanalyzer/tests/main.nf.test.snap new file mode 100644 index 00000000..3435fbea --- /dev/null +++ b/modules/local/isoformswitchanalyzer/tests/main.nf.test.snap @@ -0,0 +1,109 @@ +{ + "human chrX - salmon quant - stub": { + "content": [ + { + "0": [ + "isoformswitchanalyzer_summary.csv:md5,d41d8cd98f00b204e9800998ecf8427e" + ], + "1": [ + "isoformswitchanalyzer_isoformfeatures.csv:md5,d41d8cd98f00b204e9800998ecf8427e" + ], + "2": [ + "switchlist.rds:md5,d41d8cd98f00b204e9800998ecf8427e" + ], + "3": [ + [ + + ] + ], + "4": [ + "versions.yml:md5,a912a375db2f9c6bc21da0dd256d253f" + ], + "isoformswitchanalyzer_isoformFeatures": [ + "isoformswitchanalyzer_isoformfeatures.csv:md5,d41d8cd98f00b204e9800998ecf8427e" + ], + "isoformswitchanalyzer_summary": [ + "isoformswitchanalyzer_summary.csv:md5,d41d8cd98f00b204e9800998ecf8427e" + ], + "results": [ + [ + + ] + ], + "switchlist_rds": [ + "switchlist.rds:md5,d41d8cd98f00b204e9800998ecf8427e" + ], + "versions_isoformswitchanalyzer": [ + "versions.yml:md5,a912a375db2f9c6bc21da0dd256d253f" + ] + } + ], + "timestamp": "2026-09-14T09:18:56.519609154", + "meta": { + "nf-test": "0.9.5", + "nextflow": "26.04.4" + } + }, + "human chrX - salmon quant": { + "content": [ + [ + "isoformswitchanalyzer_summary.csv:md5,8cb2a864dbf6d26df508842d83a25bdb" + ], + [ + [ + "isoformswitchanalyzer_isoformfeatures.csv", + "\"\",\"iso_ref\",\"gene_ref\",\"isoform_id\",\"gene_id\",\"condition_1\",\"condition_2\",\"gene_name\",\"gene_biotype\",\"iso_biotype\",\"gene_overall_mean\",\"gene_value_1\",\"gene_value_2\",\"gene_stderr_1\",\"gene_stderr_2\",\"gene_log2_fold_change\",\"gene_q_value\",\"iso_overall_mean\",\"iso_value_1\",\"iso_value_2\",\"iso_stderr_1\",\"iso_stderr_2\",\"iso_log2_fold_change\",\"iso_q_value\",\"IF_overall\",\"IF1\",\"IF2\",\"dIF\",\"isoform_switch_q_value\",\"gene_switch_q_value\",\"PTC\",\"IR\",\"switchConsequencesGene\"", + 185 + ] + ], + [ + "switchlist.rds" + ], + [ + [ + "GBR_vs_YRI/1_switch_plot_RPL10_aka_RPL10.pdf", + "YRI_vs_GBR/1_switch_plot_RPL10_aka_RPL10.pdf", + "common_switch_consequences.pdf" + ] + ], + { + "versions_isoformswitchanalyzer": [ + "versions.yml:md5,a912a375db2f9c6bc21da0dd256d253f" + ] + } + ], + "timestamp": "2026-09-14T09:17:49.937748396", + "meta": { + "nf-test": "0.9.5", + "nextflow": "26.04.4" + } + }, + "human chrX - salmon quant - no switches": { + "content": [ + [ + "isoformswitchanalyzer_summary.csv:md5,4b2ab1b387ded8f479d7cfa96893e93b" + ], + [ + "isoformswitchanalyzer_isoformfeatures.csv:md5,a227d42afbcc590b4e949075cde4a5b6" + ], + [ + "switchlist.rds" + ], + [ + [ + + ] + ], + { + "versions_isoformswitchanalyzer": [ + "versions.yml:md5,a912a375db2f9c6bc21da0dd256d253f" + ] + } + ], + "timestamp": "2026-09-14T09:18:40.687092536", + "meta": { + "nf-test": "0.9.5", + "nextflow": "26.04.4" + } + } +} \ No newline at end of file diff --git a/modules/local/isoformswitchanalyzer/tests/nextflow.config b/modules/local/isoformswitchanalyzer/tests/nextflow.config new file mode 100644 index 00000000..e7860e26 --- /dev/null +++ b/modules/local/isoformswitchanalyzer/tests/nextflow.config @@ -0,0 +1,11 @@ +process { + withName: ISOFORMSWITCHANALYZER { + memory = 6.GB + } + + // Extract the spliced transcript sequences from the genome, which is what + // ISOFORMSWITCHANALYZER needs as isoform nucleotide fasta + withName: GFFREAD { + ext.args = '-w' + } +} diff --git a/tests/.nftignore b/tests/.nftignore index 24a41cc0..a41d3916 100644 --- a/tests/.nftignore +++ b/tests/.nftignore @@ -131,3 +131,6 @@ trimgalore/*fastq.gz_trimming_report.txt **/*.miso **/*.pickle **/*.shelve.* + +# IsoformSwitchAnalyzeR +isoformswitchanalyzer/isoformswitchanalyzer_isoformfeatures.csv diff --git a/tests/default.nf.test.snap b/tests/default.nf.test.snap index b88dd93e..7506684b 100644 --- a/tests/default.nf.test.snap +++ b/tests/default.nf.test.snap @@ -61,8 +61,8 @@ "gunzip": 1.13 }, "ISOFORMSWITCHANALYZER": { - "bioconductor-isoformswitchanalyzer": "2.2.0", - "r-base": "4.3.2" + "bioconductor-isoformswitchanalyzer": "2.12.0", + "r-base": "4.5.3" }, "MAKE_TRANSCRIPTS_FASTA": { "rsem": "1.3.1", @@ -194,6 +194,11 @@ "isoformswitchanalyzer/isoformswitchanalyzer_isoformfeatures.csv", "isoformswitchanalyzer/isoformswitchanalyzer_summary.csv", "isoformswitchanalyzer/results", + "isoformswitchanalyzer/results/GBR_vs_YRI", + "isoformswitchanalyzer/results/GBR_vs_YRI/1_switch_plot_RPL10_aka_RPL10.pdf", + "isoformswitchanalyzer/results/YRI_vs_GBR", + "isoformswitchanalyzer/results/YRI_vs_GBR/1_switch_plot_RPL10_aka_RPL10.pdf", + "isoformswitchanalyzer/results/common_switch_consequences.pdf", "isoformswitchanalyzer/switchlist.rds", "mergeevents", "mergeevents/genes_chrX.ioe", @@ -4703,8 +4708,7 @@ "trimgalore/fastqc/ERR204916_2_val_2_fastqc.zip" ], [ - "isoformswitchanalyzer_isoformfeatures.csv:md5,a227d42afbcc590b4e949075cde4a5b6", - "isoformswitchanalyzer_summary.csv:md5,4b2ab1b387ded8f479d7cfa96893e93b", + "isoformswitchanalyzer_summary.csv:md5,8cb2a864dbf6d26df508842d83a25bdb", "genes_chrX_genes.gff3:md5,1c1ece4ee637e317c0ae6528a253a83f", "genes.gff:md5,0ef855fcf03bcb7d49afde3e63577465", "miso_settings.txt:md5,c092cc3e878a299674a99c508b6b2e72", @@ -4823,10 +4827,10 @@ "genes_chrX.ioi:md5,6c9ee3567554e3f74186b4c17ec777d6" ] ], - "timestamp": "2026-08-25T14:33:14.412269311", + "timestamp": "2026-09-14T09:39:10.533078728", "meta": { "nf-test": "0.9.5", - "nextflow": "26.04.3" + "nextflow": "26.04.4" } } } diff --git a/tests/salmon_results.nf.test.snap b/tests/salmon_results.nf.test.snap index 6a590278..e5ecdf73 100644 --- a/tests/salmon_results.nf.test.snap +++ b/tests/salmon_results.nf.test.snap @@ -38,8 +38,8 @@ "gunzip": 1.13 }, "ISOFORMSWITCHANALYZER": { - "bioconductor-isoformswitchanalyzer": "2.2.0", - "r-base": "4.3.2" + "bioconductor-isoformswitchanalyzer": "2.12.0", + "r-base": "4.5.3" }, "MAKE_TRANSCRIPTS_FASTA": { "rsem": "1.3.1", @@ -92,6 +92,11 @@ "isoformswitchanalyzer/isoformswitchanalyzer_isoformfeatures.csv", "isoformswitchanalyzer/isoformswitchanalyzer_summary.csv", "isoformswitchanalyzer/results", + "isoformswitchanalyzer/results/GBR_vs_YRI", + "isoformswitchanalyzer/results/GBR_vs_YRI/1_switch_plot_RPL10_aka_RPL10.pdf", + "isoformswitchanalyzer/results/YRI_vs_GBR", + "isoformswitchanalyzer/results/YRI_vs_GBR/1_switch_plot_RPL10_aka_RPL10.pdf", + "isoformswitchanalyzer/results/common_switch_consequences.pdf", "isoformswitchanalyzer/switchlist.rds", "mergeevents", "mergeevents/genes_chrX.ioe", @@ -273,15 +278,14 @@ "salmon/tximport/tximport.tx2gene.tsv" ], [ - "isoformswitchanalyzer_isoformfeatures.csv:md5,a227d42afbcc590b4e949075cde4a5b6", - "isoformswitchanalyzer_summary.csv:md5,4b2ab1b387ded8f479d7cfa96893e93b", + "isoformswitchanalyzer_summary.csv:md5,8cb2a864dbf6d26df508842d83a25bdb", "genes_chrX.ioi:md5,6c9ee3567554e3f74186b4c17ec777d6" ] ], - "timestamp": "2026-09-07T19:55:52.390384017", + "timestamp": "2026-09-14T09:33:02.530765961", "meta": { "nf-test": "0.9.5", - "nextflow": "26.04.1" + "nextflow": "26.04.4" } } } \ No newline at end of file