{"slug":"alterlab-rnaseq-quant","title":"alterlab-rnaseq-quant","summary":"Quantifies bulk RNA-seq transcript abundance with salmon 2.x (the Rust rewrite; selective alignment or --sketch) and kallisto (v0.52.0, kb-python workflow), builds a decoy-aware gentrome index, runs quant with --gcBias -l A, then imports estimates via tximport/tximeta with a tx2g","platform":"Claude","tags":[],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-09-23T18:56:57.384951Z","repo":{"url":"https://github.com/AlterLab-IEU/AlterLab-Academic-Skills","stars":68,"forks":13,"license":"MIT","updatedAt":"2026-09-23T13:42:59Z"},"bodyHtml":"<hr>\n<h2>name: alterlab-rnaseq-quant\ndescription: Quantifies bulk RNA-seq transcript abundance with salmon 2.x (the Rust rewrite; selective alignment or --sketch) and kallisto (v0.52.0, kb-python workflow), builds a decoy-aware gentrome index, runs quant with --gcBias -l A, then imports estimates via tximport/tximeta with a tx2gene map and hands differential expression to alterlab-pydeseq2. Warns that salmon 2.0 cannot read C++/pufferfish indices (rebuild every index), that --validateMappings is now accepted-but-ignored, and that 'salmon alevin' was REMOVED (single-cell now uses piscem + alevin-fry). Use when quantifying RNA-seq transcript abundance, running salmon or kallisto, building a decoy-aware index, or wiring tximport to DESeq2; for differential expression use alterlab-pydeseq2, for FASTQ-to-VCF variant calling use alterlab-nf-core-sarek. Part of the AlterLab Academic Skills suite.\nlicense: MIT\nallowed-tools: Read Write Edit Bash(python:<em>) Bash(uv:</em>) Bash(salmon:<em>) Bash(kallisto:</em>) Bash(kb:*)\ncompatibility: \"Requires the salmon and/or kallisto CLI on PATH (conda/bioconda or a container). This skill targets salmon 2.x (bioconda 2.7.0 as of 2026-09) and kallisto v0.52.0; salmon 2.x is a single portable Rust binary with no Boost/compiler dependency. The tximport/tx2gene helper runs under <code>uv run python</code> with pure stdlib (no pandas needed). No API key or account required; all work is local.\"\nmetadata:\nskill-author: AlterLab\nversion: \"1.2.0\"\nlast_updated: \"2026-09-23\"\ndepends_on: \"alterlab-pydeseq2 (downstream differential expression)\"</h2>\n<h1>RNA-seq Quantification — salmon &amp; kallisto Transcript Abundance</h1>\n<p>The command-line quantification entry point for bulk RNA-seq: take raw FASTQ\nreads plus a reference transcriptome and produce transcript-level abundance\nestimates (counts + TPM) with <strong>salmon</strong> (selective alignment) or <strong>kallisto</strong>\n(pseudoalignment via kb-python), then aggregate to the gene level with\n<code>tximport</code>/<code>tximeta</code> and hand off to <code>alterlab-pydeseq2</code> for differential\nexpression. It is the raw-data-to-count-matrix pipeline that the repo's Python\nanalysis skills assume already ran.</p>\n<h2>Quick Start</h2>\n<pre><code>Quantify these RNA-seq FASTQs with salmon and a decoy-aware index\nBuild a salmon gentrome index from this transcriptome + genome\nRun kallisto / kb count on my paired-end reads\nTurn my salmon quant.sf files into a gene-level count matrix for DESeq2\n</code></pre>\n<p>→ Build a <strong>decoy-aware</strong> index once, run <code>salmon quant</code> (or <code>kb count</code>) per\nsample, then run <code>scripts/build_tx2gene.py</code> + <code>scripts/import_quant.py</code> to make\nthe <code>tximport</code> gene matrix and route it to <code>alterlab-pydeseq2</code>.</p>\n<hr>\n<h2>When to Use This Skill</h2>\n<p>Use this skill when the request is about <strong>getting from FASTQ to transcript or\ngene abundance</strong> with a lightweight quantifier:</p>\n<ul>\n<li>\"Quantify my RNA-seq with salmon / kallisto.\"</li>\n<li>\"Build a decoy-aware salmon index (gentrome + decoys.txt).\"</li>\n<li>\"Run selective alignment with <code>--gcBias</code>.\" / \"Use salmon's <code>--sketch</code> mode.\"</li>\n<li>\"I have <code>quant.sf</code> files — make me a gene-level count matrix for DESeq2.\"</li>\n<li>\"Set up <code>tximport</code> / <code>tximeta</code> with a tx2gene map.\"</li>\n<li>\"Use kb-python / <code>kb count</code> to pseudoalign these reads.\"</li>\n</ul>\n<h3>Does NOT Trigger — route these to the right sibling</h3>\n<table>\n<thead>\n<tr>\n<th>The request is really about…</th>\n<th>Route to</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Differential expression on a <strong>count matrix</strong> (DESeq2 Wald tests, FDR, volcano)</td>\n<td><code>alterlab-pydeseq2</code></td>\n</tr>\n<tr>\n<td><strong>Single-cell</strong> RNA-seq quantification (was <code>salmon alevin</code>)</td>\n<td>piscem + alevin-fry — see <a href=\"references/single_cell_alevin.md\">references/single_cell_alevin.md</a>; downstream → <code>alterlab-scanpy</code> / <code>alterlab-scvi-tools</code></td>\n</tr>\n<tr>\n<td><strong>FASTQ-to-VCF</strong> germline/somatic variant calling</td>\n<td><code>alterlab-nf-core-sarek</code></td>\n</tr>\n<tr>\n<td><strong>16S/ITS amplicon</strong> (microbiome) FASTQ-to-feature-table</td>\n<td><code>alterlab-qiime2-amplicon</code></td>\n</tr>\n<tr>\n<td><strong>Spatial</strong> transcriptomics (Visium/Xenium) neighborhood analysis</td>\n<td><code>alterlab-squidpy-spatial</code></td>\n</tr>\n<tr>\n<td>Loading/manipulating the resulting matrix as an <strong>AnnData</strong> object</td>\n<td><code>alterlab-anndata</code></td>\n</tr>\n<tr>\n<td>BLAST/DIAMOND <strong>sequence similarity search</strong></td>\n<td><code>alterlab-blast</code></td>\n</tr>\n<tr>\n<td>Quick gene/transcript <strong>ID lookups &amp; reference fetch</strong> (Ensembl/RefSeq)</td>\n<td><code>alterlab-gget</code></td>\n</tr>\n<tr>\n<td>Aligned <strong>BAM</strong> manipulation, coverage, read counting from alignments</td>\n<td><code>alterlab-pysam</code></td>\n</tr>\n</tbody>\n</table>\n<p>This skill stops at the <strong>count/abundance matrix</strong>. It does not call DEGs, does\nnot handle single-cell barcodes, and does not align to a genome for variant\ncalling.</p>\n<hr>\n<h2>Two Critical Correctness Traps (read before quantifying)</h2>\n<p>These are the three failures most outdated RNA-seq instructions get wrong now that\nsalmon has moved to <strong>2.x</strong> (a from-scratch Rust rewrite; bioconda 2.7.0). All are\nconfirmed in the upstream <code>MIGRATION.md</code> (see\n<a href=\"references/tool_versions.md\">references/tool_versions.md</a>).</p>\n<ol>\n<li><p><strong>salmon 2.0 cannot read a C++ (pufferfish) index.</strong> The index format changed\nwith the rewrite. Loading an old index — or pointing old salmon at a 2.x index —\nis detected and rejected with a clear error, so this fails loudly rather than\nsilently. <strong>Rebuild every index</strong> with the same salmon version you quantify with.\n<code>quant.sf</code> and the bootstrap/Gibbs outputs are unchanged, so tximport, tximeta,\nfishpond and swish keep working on 2.x output with no changes.</p>\n</li>\n<li><p><strong><code>--validateMappings</code> no longer does anything.</strong> Selective alignment is the\ndefault (and only) alignment mode in 2.x, so the flag parses and logs a warning.\nCopying it from a 2019-era tutorial is harmless but misleading; drop it. Several\nother C++ flags now <strong>error out</strong>: <code>--mimicBT2</code>, <code>--mimicStrictBT2</code>,\n<code>--minAssignedFrags</code>, <code>--numBiasSamples</code>, <code>--alternativeInitMode</code>.</p>\n</li>\n<li><p><strong><code>salmon alevin</code> was REMOVED.</strong> Single-cell quantification is no longer part of\nsalmon; <code>salmon alevin …</code> prints a redirect and exits. Use the\n<strong>piscem + alevin-fry</strong> ecosystem instead. If the user has single-cell / droplet\ndata, route per the table above and see\n<a href=\"references/single_cell_alevin.md\">references/single_cell_alevin.md</a>.</p>\n</li>\n</ol>\n<p>If you genuinely need the old behavior, the final C++ release (salmon 1.12.0) lives\non the upstream <code>cpp</code> branch and is packaged separately as <code>salmon-cpp</code>.</p>\n<hr>\n<h2>Pipeline (salmon, the default path)</h2>\n<h3>1. Build a decoy-aware gentrome index (once per reference)</h3>\n<p>A <strong>decoy-aware</strong> index lets salmon distinguish reads that align better to the\ngenome than the transcriptome, reducing spurious assignments. You build a\n\"gentrome\" = transcripts FASTA <strong>concatenated with the genome FASTA</strong>, plus a\n<code>decoys.txt</code> listing the genome sequence names as decoys.</p>\n<pre><code># 1. decoys.txt = the genome's sequence (chromosome) names, one per line\ngrep \"^&gt;\" genome.fa | sed 's/^&gt;//; s/ .*//' &gt; decoys.txt\n\n# 2. gentrome = transcripts FIRST, then genome (order matters)\ncat transcripts.fa genome.fa &gt; gentrome.fa\n\n# 3. build the index (rebuild under salmon 2.x — see trap #1)\nsalmon index \\\n  -t gentrome.fa \\\n  -d decoys.txt \\\n  -i salmon_index \\\n  -k 31 \\\n  -p 8\n</code></pre>\n<ul>\n<li><code>-k 31</code> is the default k-mer; lower it only for very short reads.</li>\n<li>The helper <code>scripts/make_decoys.py</code> writes <code>decoys.txt</code> and <code>gentrome.fa</code> for\nyou and refuses to proceed if the genome names are absent from the transcript\nFASTA (a common silent mistake). See <a href=\"references/decoy_index.md\">references/decoy_index.md</a>.</li>\n</ul>\n<h3>2. Quantify each sample</h3>\n<pre><code>salmon quant \\\n  -i salmon_index \\\n  -l A \\\n  -1 sampleA_R1.fastq.gz -2 sampleA_R2.fastq.gz \\\n  --gcBias \\\n  -p 8 \\\n  -o quants/sampleA\n</code></pre>\n<ul>\n<li><strong><code>-l A</code></strong> — auto-detect library type (strandedness). Let salmon infer it\nunless you have a documented protocol; verify the inferred type in\n<code>lib_format_counts.json</code>.</li>\n<li><strong>Selective alignment is the default</strong> in salmon 2.x — there is no flag to turn\nit on (<code>--validateMappings</code> is accepted and ignored). <code>--sketch</code> opts <em>out</em> of it\ninto faster alignment-free pseudoalignment; prefer the default when the\nquantification feeds differential expression, and reserve <code>--sketch</code> for very\nlarge screens where speed dominates.</li>\n<li><strong><code>--gcBias</code></strong> — corrects fragment-level GC bias; recommended for DE and cheap\nto enable. Add <code>--seqBias</code> for 5'/3' sequence-specific bias if needed.</li>\n<li><strong><code>--ignoreTxVersion</code></strong> (new in 2.x) — with <code>-g/--geneMap</code>, matches transcript\nIDs ignoring the trailing <code>.N</code>, the way tximport's option of the same name does.\nNeeded for an Ensembl cDNA index against an Ensembl GTF. When transcripts fail to\nmatch the gene map, 2.x writes the offending names to\n<code>aux_info/genemap_unmatched_txps.json</code> and warns once with a count, instead of\nC++ salmon's one warning per transcript — check for that file after any run that\nused <code>-g</code>.</li>\n<li>For single-end reads, pass <code>-r reads.fastq.gz</code> instead of <code>-1/-2</code>.</li>\n</ul>\n<p>Each sample produces <code>quants/&lt;sample&gt;/quant.sf</code> (transcript-level estimates) and\n<code>quants/&lt;sample&gt;/lib_format_counts.json</code> (the inferred library type). See\n<a href=\"references/salmon_quant.md\">references/salmon_quant.md</a> for the full flag map\nand per-sample QC checks.</p>\n<h3>3. Aggregate to gene level with tximport</h3>\n<p>Build a transcript→gene map (<code>tx2gene</code>) from your annotation, then summarize the\nper-sample <code>quant.sf</code> files into a gene-level matrix that <code>pydeseq2</code> consumes.</p>\n<pre><code># tx2gene from a GTF/GFF3 (transcript_id -&gt; gene_id)\nuv run python scripts/build_tx2gene.py annotation.gtf --out tx2gene.tsv\n\n# import + summarize to gene level (tximport \"lengthScaledTPM\" counts)\nuv run python scripts/import_quant.py \\\n  --quants quants \\\n  --tx2gene tx2gene.tsv \\\n  --out-counts gene_counts.tsv \\\n  --out-tpm gene_tpm.tsv\n</code></pre>\n<p><code>import_quant.py</code> produces an integer-rounded gene × sample count matrix plus a\ngene × sample TPM matrix, the inputs <code>alterlab-pydeseq2</code> expects. It implements\ntximport's <code>makeCountsFromAbundance(..., \"lengthScaledTPM\")</code> at the transcript\nlevel (scale each transcript's TPM by its sample-averaged effective length, then\nrescale each sample column back to its mapped-read library size) and sums to\ngenes — so the counts are length-corrected and library-size-scaled, <strong>not</strong> raw\nsummed <code>NumReads</code>. The canonical R route is the <code>tximport</code> / <code>tximeta</code>\nBioconductor packages with <code>countsFromAbundance = \"lengthScaledTPM\"</code>; the Python\nhelper here reproduces that computation so you can stay in <code>uv</code> (differing only\nin integer rounding and the absence of <code>tximeta</code> provenance). See\n<a href=\"references/tximport_handoff.md\">references/tximport_handoff.md</a> for the exact\nsemantics, the <code>tximeta</code> linkedTxome metadata option, and when to prefer the R\npath.</p>\n<h3>4. Hand off to differential expression</h3>\n<p>Pass <code>gene_counts.tsv</code> (+ a sample/condition sheet) to <strong><code>alterlab-pydeseq2</code></strong>.\nThis skill does not call DEGs — that is pydeseq2's job (size-factor\nnormalization, dispersion, Wald tests, BH-FDR, volcano/MA plots).</p>\n<hr>\n<h2>Pipeline (kallisto, the pseudoalignment path)</h2>\n<p><code>kallisto</code> (standalone <strong>v0.52.0</strong>) and the <strong>kb-python</strong> wrapper (<code>kb</code>) give a\nfaster pseudoalignment route. kb-python drives <code>kallisto | bustools</code> and writes\ntidy outputs.</p>\n<pre><code># build a kallisto index from the transcriptome\nkallisto index -i kallisto_index.idx transcripts.fa\n\n# quantify a paired-end sample\nkallisto quant -i kallisto_index.idx -o quants_kallisto/sampleA \\\n  sampleA_R1.fastq.gz sampleA_R2.fastq.gz\n\n# OR the kb-python workflow (bulk)\n# -f1 is the cDNA FASTA kb WRITES; trailing positionals are genome FASTA THEN GTF\nkb ref -i index.idx -g t2g.txt -f1 cdna.fa genome.fa annotation.gtf\nkb count -i index.idx -g t2g.txt -x bulk -o quants_kb/sampleA \\\n  sampleA_R1.fastq.gz sampleA_R2.fastq.gz\n</code></pre>\n<ul>\n<li>kallisto outputs <code>abundance.tsv</code> / <code>abundance.h5</code>; feed these to <code>tximport</code>\n(<code>type=\"kallisto\"</code>) the same way as salmon's <code>quant.sf</code>.</li>\n<li><strong>Long reads:</strong> kb-python exposes <strong>lr-kallisto</strong> via the <code>--long</code> flag (and\n<code>k&gt;31</code> k-mers) — use it for ONT/PacBio cDNA. See\n<a href=\"references/kallisto_kb.md\">references/kallisto_kb.md</a>.</li>\n<li>kallisto does not use the decoy/gentrome construction; that is salmon-specific.</li>\n</ul>\n<hr>\n<h2>Turnkey alternative — nf-core/rnaseq</h2>\n<p>For an end-to-end, provenance-tracked pipeline (trimming → alignment →\nquantification → QC), <strong>nf-core/rnaseq v3.26.0</strong> runs <code>--aligner star_salmon</code> by\ndefault: STAR maps to the genome, projects onto the transcriptome, and Salmon\ndoes the quantification. Reach for it when the user wants a reproducible\nNextflow pipeline rather than hand-run commands; this skill covers the\ndirect-salmon/kallisto path and the tximport handoff. See\n<a href=\"references/tool_versions.md\">references/tool_versions.md</a>.</p>\n<hr>\n<h2>Offload note</h2>\n<p>Indexing and per-sample quantification are CPU/IO-heavy but fully offline. On a\nlocal workstation these are good candidates to run directly (e.g. overnight)\nrather than streaming large FASTQs through an API session. Build the index once;\nquantify samples in a loop.</p>\n<hr>\n<h2>Self-Check Before Reporting</h2>\n<ul>\n<li>Did you <strong>rebuild</strong> the salmon index with the same 2.x binary you quantified with\n(trap #1)? A C++/pufferfish index is rejected outright.</li>\n<li>Is the index <strong>decoy-aware</strong> (gentrome + <code>decoys.txt</code>) for salmon? Confirm the\ngenome names made it into <code>decoys.txt</code>.</li>\n<li>Did you let <code>-l A</code> infer strandedness, and did you sanity-check the inferred\ntype in <code>lib_format_counts.json</code>?</li>\n<li>Is the data actually <strong>single-cell</strong>? If so you must NOT use this path —\n<code>salmon alevin</code> is gone; route to piscem + alevin-fry (trap #2).</li>\n<li>Did you stop at the <strong>count matrix</strong> and hand DE off to <code>alterlab-pydeseq2</code>\nrather than calling DEGs here?</li>\n</ul>\n<hr>\n<h2>References</h2>\n<ul>\n<li><a href=\"references/tool_versions.md\">references/tool_versions.md</a> — pinned versions\n(salmon 2.x, kallisto v0.52.0, kb-python, nf-core/rnaseq) and the upstream\nrelease-note facts (the 2.0 rewrite and index break, alevin removal).</li>\n<li><a href=\"references/decoy_index.md\">references/decoy_index.md</a> — decoy-aware gentrome\nindex construction, gotchas, and the <code>make_decoys.py</code> helper.</li>\n<li><a href=\"references/salmon_quant.md\">references/salmon_quant.md</a> — <code>salmon quant</code> flag\nmap, library-type inference, and per-sample QC.</li>\n<li><a href=\"references/kallisto_kb.md\">references/kallisto_kb.md</a> — kallisto / kb-python\nworkflow, <code>--long</code> (lr-kallisto), and output handling.</li>\n<li><a href=\"references/tximport_handoff.md\">references/tximport_handoff.md</a> — tximport /\ntximeta aggregation, tx2gene, <code>countsFromAbundance</code>, and the pydeseq2 handoff.</li>\n<li><a href=\"references/single_cell_alevin.md\">references/single_cell_alevin.md</a> — why\n<code>salmon alevin</code> is removed and the piscem + alevin-fry replacement.</li>\n</ul>\n<p>Part of the AlterLab Academic Skills suite.</p>\n","files":[{"path":"evals/evals.json","sizeBytes":6110,"isText":true},{"path":"references/decoy_index.md","sizeBytes":2361,"isText":true},{"path":"references/kallisto_kb.md","sizeBytes":2462,"isText":true},{"path":"references/salmon_quant.md","sizeBytes":3091,"isText":true},{"path":"references/single_cell_alevin.md","sizeBytes":1757,"isText":true},{"path":"references/tool_versions.md","sizeBytes":4610,"isText":true},{"path":"references/tximport_handoff.md","sizeBytes":3565,"isText":true},{"path":"scripts/build_tx2gene.py","sizeBytes":4276,"isText":true},{"path":"scripts/import_quant.py","sizeBytes":9500,"isText":true},{"path":"scripts/make_decoys.py","sizeBytes":4724,"isText":true},{"path":"SKILL.md","sizeBytes":14469,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"clean","suspicious":0,"notes":0,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-09-23T18:57:55.412364Z","sha256":"D67EBE0F55CF4AC9550AB1848925333B8ABA3F12280ED42539055B2861F474CE","sizeBytes":24601},"review":null,"source":{"repositoryUrl":"https://github.com/AlterLab-IEU/AlterLab-Academic-Skills","path":"skills/bioinformatics/alterlab-rnaseq-quant","license":"MIT","commit":"e4836c08a20da195a11f30f203a8cf23ec30aa95","subtreeSha":"57ADDDA34EA41E79154EB676D5C337AC72335E2A90B502D84005F8B22EC04A86","lastSyncedAt":"2026-09-23T18:56:52.297238Z"},"reviewedAt":"2026-09-23T18:59:30.24884Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/AlterLab-IEU/AlterLab-Academic-Skills/tree/main/skills/bioinformatics/alterlab-rnaseq-quant"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install alterlab-ieu-alterlab-academic-skills@llmmart"},{"target":"git","command":"git clone https://github.com/AlterLab-IEU/AlterLab-Academic-Skills.git"}]}