Install any skill in seconds. Free to start, no credit card required.
Get Started Free →End-to-end RNA-seq workflow from FASTQ files to differential expression results. Covers QC, quantification (Salmon or STAR+featureCounts), and DESeq2 analysis with visualization. Use when running RNA-seq from FASTQ to DE results.
.claude/skills/bio-workflows-rnaseq-to-de/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 29% | 0% |
| case-18 | ✓→✓ | = Same ✓ | 493% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 84% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 75% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 103% | 0% |
<!--
#
#
-->
Complete pipeline from raw FASTQ files to differential expression results.
FASTQ files
|
v
[1. QC & Trimming] -----> fastp
|
v
[2. Quantification] ----> Salmon (recommended) or STAR + featureCounts
|
v
[3. Import to R] -------> tximport (for Salmon) or direct counts
|
v
[4. DE Analysis] -------> DESeq2
|
v
[5. Visualization] -----> Volcano, MA, heatmaps
|
v
Significant gene listbash# Single sample fastp -i sample_R1.fastq.gz -I sample_R2.fastq.gz \ -o sample_R1.trimmed.fq.gz -O sample_R2.trimmed.fq.gz \ --detect_adapter_for_pe \ --qualified_quality_phred 20 \ --length_required 35 \ --html sample_fastp.html # Batch processing for sample in sample1 sample2 sample3; do fastp -i ${sample}_R1.fastq.gz -I ${sample}_R2.fastq.gz \ -o trimmed/${sample}_R1.fq.gz -O trimmed/${sample}_R2.fq.gz \ --detect_adapter_for_pe \ --html qc/${sample}_fastp.html done
QC Checkpoint 1: Check fastp reports
bash# Build index (once per transcriptome) salmon index -t transcriptome.fa -i salmon_index -k 31 # Quantify each sample for sample in sample1 sample2 sample3; do salmon quant -i salmon_index \ -l A \ -1 trimmed/${sample}_R1.fq.gz \ -2 trimmed/${sample}_R2.fq.gz \ -o quants/${sample} \ --validateMappings \ --gcBias \ --seqBias \ -p 8 done
QC Checkpoint 2: Check Salmon logs
rlibrary(tximport) library(DESeq2) # Create tx2gene mapping (Ensembl example) tx2gene <- read.csv('tx2gene.csv') # columns: TXNAME, GENEID # List quantification files samples <- c('sample1', 'sample2', 'sample3', 'sample4', 'sample5', 'sample6') files <- file.path('quants', samples, 'quant.sf') names(files) <- samples # Import transcript-level estimates txi <- tximport(files, type = 'salmon', tx2gene = tx2gene) # Create sample metadata coldata <- data.frame( condition = factor(c('control', 'control', 'control', 'treated', 'treated', 'treated')), row.names = samples )
r# Create DESeqDataSet from tximport dds <- DESeqDataSetFromTximport(txi, colData = coldata, design = ~ condition) # Pre-filter low count genes keep <- rowSums(counts(dds)) >= 10 dds <- dds[keep,] # Set reference level dds$condition <- relevel(dds$condition, ref = 'control') # Run DESeq2 dds <- DESeq(dds) # Get results with shrinkage res <- lfcShrink(dds, coef = 'condition_treated_vs_control', type = 'apeglm') # Summary summary(res)
QC Checkpoint 3: Check DESeq2 diagnostics
rlibrary(ggplot2) library(pheatmap) library(ggrepel) # Volcano plot res_df <- as.data.frame(res) res_df$gene <- rownames(res_df) res_df$significant <- res_df$padj < 0.05 & abs(res_df$log2FoldChange) > 1 ggplot(res_df, aes(x = log2FoldChange, y = -log10(pvalue), color = significant)) + geom_point(alpha = 0.5) + scale_color_manual(values = c('grey', 'red')) + theme_minimal() + labs(title = 'Volcano Plot', x = 'Log2 Fold Change', y = '-Log10 P-value') # Heatmap of top genes vsd <- vst(dds, blind = FALSE) top_genes <- head(order(res$padj), 50) pheatmap(assay(vsd)[top_genes,], scale = 'row', show_rownames = FALSE) # Export significant genes sig_genes <- subset(res, padj < 0.05 & abs(log2FoldChange) > 1) write.csv(as.data.frame(sig_genes), 'significant_genes.csv')
bash# Build STAR index (once) STAR --runMode genomeGenerate \ --genomeDir star_index \ --genomeFastaFiles genome.fa \ --sjdbGTFfile genes.gtf \ --sjdbOverhang 100 \ --runThreadN 8 # Align each sample for sample in sample1 sample2 sample3; do STAR --genomeDir star_index \ --readFilesIn trimmed/${sample}_R1.fq.gz trimmed/${sample}_R2.fq.gz \ --readFilesCommand zcat \ --outFileNamePrefix aligned/${sample}_ \ --outSAMtype BAM SortedByCoordinate \ --quantMode GeneCounts \ --runThreadN 8 done
bash# Count reads per gene featureCounts -T 8 -p --countReadPairs \ -a genes.gtf \ -o counts.txt \ aligned/*_Aligned.sortedByCoord.out.bam
r# Load featureCounts output counts <- read.table('counts.txt', header = TRUE, row.names = 1, skip = 1) counts <- counts[, 6:ncol(counts)] # Remove annotation columns colnames(counts) <- gsub('_Aligned.sortedByCoord.out.bam', '', colnames(counts)) # Create DESeqDataSet directly dds <- DESeqDataSetFromMatrix(countData = counts, colData = coldata, design = ~ condition)
| Step | Parameter | Recommendation | |------|-----------|----------------| | fastp | --qualified_quality_phred | 20 (standard) | | fastp | --length_required | 35 for 2x100, 50 for 2x150 | | Salmon | -l | A (auto-detect library type) | | Salmon | --gcBias | Enable for better accuracy | | STAR | --sjdbOverhang | read_length - 1 | | featureCounts | -s | 0=unstranded, 1=stranded, 2=reversely stranded | | DESeq2 | lfcShrink type | apeglm (recommended) | | DESeq2 | alpha | 0.05 (standard significance) |
| Issue | Likely Cause | Solution | |-------|--------------|----------| | Low mapping rate (<50%) | Wrong reference, contamination | Check species, run FastQ Screen | | High duplication | Low complexity library, over-sequencing | Check library prep, may be normal for low-input | | No DE genes | Low power, batch effects | Add replicates, include batch in design | | All genes DE | Normalization issue, sample swap | Check sample metadata, rerun normalization | | Outlier samples | Technical failure, sample swap | Remove or investigate, check PCA |
bash#!/bin/bash set -e THREADS=8 SAMPLES="sample1 sample2 sample3 sample4 sample5 sample6" SALMON_INDEX="salmon_index" OUTDIR="results" mkdir -p ${OUTDIR}/{trimmed,quants,qc} # Step 1: QC and trim for sample in $SAMPLES; do fastp -i ${sample}_R1.fastq.gz -I ${sample}_R2.fastq.gz \ -o ${OUTDIR}/trimmed/${sample}_R1.fq.gz \ -O ${OUTDIR}/trimmed/${sample}_R2.fq.gz \ --detect_adapter_for_pe \ --html ${OUTDIR}/qc/${sample}_fastp.html \ -w ${THREADS} done # Step 2: Quantify for sample in $SAMPLES; do salmon quant -i ${SALMON_INDEX} -l A \ -1 ${OUTDIR}/trimmed/${sample}_R1.fq.gz \ -2 ${OUTDIR}/trimmed/${sample}_R2.fq.gz \ -o ${OUTDIR}/quants/${sample} \ --validateMappings --gcBias -p ${THREADS} done echo "Quantification complete. Run R script for DE analysis."
rlibrary(tximport) library(DESeq2) library(apeglm) library(ggplot2) library(pheatmap) # Configuration samples <- c('sample1', 'sample2', 'sample3', 'sample4', 'sample5', 'sample6') conditions <- c('control', 'control', 'control', 'treated', 'treated', 'treated') quant_dir <- 'results/quants' # Import tx2gene <- read.csv('tx2gene.csv') files <- file.path(quant_dir, samples, 'quant.sf') names(files) <- samples txi <- tximport(files, type = 'salmon', tx2gene = tx2gene) # DESeq2 coldata <- data.frame(condition = factor(conditions), row.names = samples) dds <- DESeqDataSetFromTximport(txi, colData = coldata, design = ~ condition) dds <- dds[rowSums(counts(dds)) >= 10,] dds$condition <- relevel(dds$condition, ref = 'control') dds <- DESeq(dds) # Results res <- lfcShrink(dds, coef = 'condition_treated_vs_control', type = 'apeglm') sig <- subset(res, padj < 0.05 & abs(log2FoldChange) > 1) cat('Significant genes:', nrow(sig), '\n') write.csv(as.data.frame(sig), 'significant_genes.csv')
<!-- AUTHOR_SIGNATURE: 9a7f3c2e-MD-BABU-MIA-2026-MSSM-SECURE -->
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 20,574 | 12,342 | -40% | 1 | 1 | 0% | 4,336 | 5,613 | +29% | 0 | 0 | — |
case-18 | pass→pass | 3,395 | 2,306 | -32% | 1 | 1 | 0% | 531 | 3,151 | +493% | 0 | 0 | — |
case-02 | pass→pass | 10,784 | 4,125 | -62% | 1 | 1 | 0% | 1,982 | 3,640 | +84% | 0 | 0 | — |
case-03 | pass→pass | 26,882 | 7,074 | -74% | 1 | 1 | 0% | 2,386 | 4,186 | +75% | 0 | 0 | — |
case-04 | pass→pass | 9,942 | 4,059 | -59% | 1 | 1 | 0% | 1,751 | 3,553 | +103% | 0 | 0 | — |
case-05 | pass→pass | 5,278 | 2,435 | -54% | 1 | 1 | 0% | 842 | 3,240 | +285% | 0 | 0 | — |
case-06 | pass→pass | 4,990 | 3,817 | -24% | 1 | 1 | 0% | 874 | 3,530 | +304% | 0 | 0 | — |
case-07 | pass→pass | 12,837 | 6,868 | -46% | 1 | 1 | 0% | 2,247 | 4,179 | +86% | 0 | 0 | — |
case-08 | pass→pass | 4,057 | 3,780 | -7% | 1 | 1 | 0% | 635 | 3,492 | +450% | 0 | 0 | — |
case-09 | pass→pass | 8,632 | 5,326 | -38% | 1 | 1 | 0% | 1,689 | 3,778 | +124% | 0 | 0 | — |
case-10 | pass→pass | 6,447 | 4,045 | -37% | 1 | 1 | 0% | 1,195 | 3,561 | +198% | 0 | 0 | — |
case-11 | pass→pass | 3,415 | 3,371 | -1% | 1 | 1 | 0% | 517 | 3,375 | +553% | 0 | 0 | — |
case-12 | pass→pass | 9,252 | 6,246 | -32% | 1 | 1 | 0% | 1,735 | 4,095 | +136% | 0 | 0 | — |
case-13 | pass→pass | 17,373 | 1,985 | -89% | 1 | 1 | 0% | 1,447 | 3,157 | +118% | 0 | 0 | — |
case-14 | pass→pass | 12,938 | 6,251 | -52% | 1 | 1 | 0% | 2,115 | 3,855 | +82% | 0 | 0 | — |
case-15 | pass→pass | 14,591 | 12,152 | -17% | 1 | 1 | 0% | 2,317 | 4,805 | +107% | 0 | 0 | — |
case-16 | pass→pass | 13,307 | 7,059 | -47% | 1 | 1 | 0% | 2,481 | 4,257 | +72% | 0 | 0 | — |
case-17 | pass→pass | 4,205 | 3,069 | -27% | 1 | 1 | 0% | 801 | 3,422 | +327% | 0 | 0 | — |
case-19 | pass→pass | 3,905 | 3,009 | -23% | 1 | 1 | 0% | 502 | 3,274 | +552% | 0 | 0 | — |
case-20 | pass→pass | 10,430 | 9,986 | -4% | 1 | 1 | 0% | 2,248 | 4,928 | +119% | 0 | 0 | — |
case-21 | pass→pass | 12,657 | 14,985 | +18% | 1 | 1 | 0% | 2,685 | 5,865 | +118% | 0 | 0 | — |
case-22 | pass→pass | 16,139 | 15,564 | -4% | 1 | 1 | 0% | 3,029 | 5,839 | +93% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +5 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
| Model | Method | Date | Lift |
|---|---|---|---|
| gemini-3.6-flash | verified | 7/26/2026 | — |
Other measured skills in the registry, with their headline benchmark lift.