agentsclimarketplace

Bulk rnaseq counts to de deseq2

Skill hossainlab/omics-skills/skills/bulk-rnaseq-counts-to-de-deseq2

Run differential expression analysis on bulk RNA-seq count data with DESeq2 (R). Covers DESeqDataSet construction from a count matrix, tximport (Salmon/Kallisto), featureCounts, or SummarizedExperiment; pre-filtering; design formulas (simple, batch, paired, interaction, multi-factor, LRT); result extraction by coefficient or contrast; log-fold-change shrinkage (apeglm/ashr); VST/rlog transformations; and exporting significant genes. Use when the user has RNA-seq counts and wants differential expression, DE genes, volcano/MA inputs, or a DESeq2 workflow.From its SKILL.md

Install
npx -y skills add hossainlab/omics-skills --skill bulk-rnaseq-counts-to-de-deseq2

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • 23 days oldThe repository was created 23 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

9.4 KB, ~2.5k tokens by cl100k_base, as published. Nobody here has run it

DESeq2 Comprehensive Reference

Complete code patterns for DESeq2 differential expression analysis. Adapt these examples to your experimental design.

Decision-making: see decision-guide.md | Errors: see troubleshooting.md

Complete Standard Workflow

library(DESeq2)
library(apeglm)

# 1. Create DESeqDataSet
dds <- DESeqDataSetFromMatrix(countData = counts, colData = coldata, design = ~ condition)

# 2. Pre-filter low counts
keep <- rowSums(counts(dds)) >= 10
dds <- dds[keep,]

# 3. Set reference level
dds$condition <- relevel(dds$condition, ref = 'control')

# 4. Run DESeq2 pipeline
dds <- DESeq(dds)

# 5. Extract results
res <- results(dds)

# 6. Apply LFC shrinkage
resLFC <- lfcShrink(dds, coef = 'condition_treated_vs_control', type = 'apeglm')

# 7. Get significant genes
sig <- subset(res, padj < 0.05 & abs(log2FoldChange) > 1)

Design Formulas

Simple Two-Group

design = ~ condition

Use: Single factor, no batch effects, most common starting point.

Batch Correction

design = ~ batch + condition

Use: Multiple sequencing runs, PCA shows batch clustering. Requirement: each condition must have samples in each batch (not confounded).

Paired Samples

design = ~ individual + condition

Use: Before/after treatment, tumor vs normal from same patient. Benefit: controls individual variation, increases power.

Interaction

design = ~ genotype * treatment
# Expands to: ~ genotype + treatment + genotype:treatment

Use: Test if treatment effect differs by genotype/sex/age.

Extract results:

res_interaction <- results(dds, name = "genotypeMutant.treatmentdrug")
res_treatment_WT <- results(dds, name = "treatment_drug_vs_control")

Multi-Factor

design = ~ sex + age_group + treatment

Use: Multiple confounders to adjust for. Requirement: ≥3 samples per coefficient, variables not confounded.

No-Intercept

design = ~ 0 + group

Use: Direct comparisons between any groups.

Changing Design

design(dds) <- ~ batch + condition
dds <- DESeq(dds)

Best practices:

  • Put condition of interest last.
  • Check confounding: table(coldata$batch, coldata$condition).
  • Use PCA first to identify batch effects.
  • Keep simple — only include factors explaining substantial variation.

Extracting Results

By Coefficient Name

resultsNames(dds)  # See available coefficients
res <- results(dds, name = 'condition_treated_vs_control')

Format: factor_level_vs_reference.

By Contrast

res <- results(dds, contrast = c('condition', 'treated', 'control'))
# Format: c(factor_name, numerator, denominator)

Advantages: explicit comparison, any two levels, works for complex designs.

By Numeric Vector (Advanced)

resultsNames(dds)
contrast_vector <- c(0, 0, 1, 0.5)
res <- results(dds, contrast = contrast_vector)

Setting Reference Level

dds$condition <- relevel(dds$condition, ref = "control")
dds <- DESeq(dds)

Critical: always set explicitly before DESeq().

Log Fold Change Shrinkage

When to use:

Use CaseShrunkUnshrunk
MA/volcano plots
Gene ranking
GSEA input
Hypothesis testing

apeglm (Recommended)

library(apeglm)
coef_name <- resultsNames(dds)[2]
resLFC <- lfcShrink(dds, coef = coef_name, type = 'apeglm')

Pros: best performance, preserves large LFC. Cons: requires coef (not contrast), needs apeglm package.

ashr

resLFC <- lfcShrink(dds, coef = 'condition_treated_vs_control', type = 'ashr')

Use: large datasets, works with contrasts.

normal (Legacy)

resLFC <- lfcShrink(dds, coef = 'condition_treated_vs_control', type = 'normal')

Use: only if apeglm/ashr unavailable.

Ranking Genes

# Top genes by shrunk LFC
ranked <- resLFC[order(abs(resLFC$log2FoldChange), decreasing = TRUE), ]

GSEA Export

gene_list <- resLFC$log2FoldChange
names(gene_list) <- rownames(resLFC)
gene_list <- gene_list[!is.na(gene_list)]
gene_list <- sort(gene_list, decreasing = TRUE)

write.table(
  data.frame(gene = names(gene_list), rank = gene_list),
  file = "gsea_ranked_list.rnk",
  quote = FALSE, sep = "\t", row.names = FALSE, col.names = FALSE
)

Transformations

Variance Stabilizing Transformation

vsd <- vst(dds, blind = FALSE)  # Uses design
vsd_mat <- assay(vsd)

Use: large datasets (>30 samples), fast.

Regularized Log

rld <- rlog(dds, blind = FALSE)

Use: small datasets (<30 samples), better stabilization but slower.

Normalized Counts

normalized_counts <- counts(dds, normalized = TRUE)
sizeFactors(dds)  # View size factors

Pre-Filtering

Standard

keep <- rowSums(counts(dds)) >= 10
dds <- dds[keep,]

By Sample Count

keep <- rowSums(counts(dds) >= 10) >= 3  # ≥3 samples with 10+ counts
dds <- dds[keep,]

By Mean Expression

keep <- rowMeans(counts(dds)) >= 10
dds <- dds[keep,]

Why filter: reduces memory, speeds computation, increases power.

Significance Thresholds

# Default
res <- results(dds)  # alpha = 0.1

# Custom
res <- results(dds, alpha = 0.05)

# LFC threshold
res <- results(dds, lfcThreshold = 1, altHypothesis = 'greaterAbs')

# Filter after
sig <- subset(res, padj < 0.05 & abs(log2FoldChange) > 1)

Alternative Inputs

SummarizedExperiment

library(SummarizedExperiment)
dds <- DESeqDataSet(se, design = ~ condition)

tximport (Salmon/Kallisto)

library(tximport)
files <- file.path('salmon_output', samples$sample_id, 'quant.sf')
names(files) <- samples$sample_id
txi <- tximport(files, type = 'salmon', tx2gene = tx2gene)
dds <- DESeqDataSetFromTximport(txi, colData = samples, design = ~ condition)

featureCounts

library(Rsubread)
fc <- featureCounts(files = bam_files, annot.ext = gtf_file,
                    isGTFAnnotationFile = TRUE, GTF.featureType = 'exon')
dds <- DESeqDataSetFromMatrix(fc$counts, coldata, design = ~ condition)

Likelihood Ratio Test

# Test multiple conditions
dds_lrt <- DESeq(dds, test = 'LRT', reduced = ~ 1)
res_lrt <- results(dds_lrt)

# Test specific term
# Full: ~ batch + genotype + treatment
# Reduced: ~ batch + genotype
dds_lrt <- DESeq(dds, test = 'LRT', reduced = ~ batch + genotype)

Working with Objects

Update Design

design(dds) <- ~ batch + condition
dds <- DESeq(dds)

Subset Samples

dds_subset <- dds[, dds$treatment == 'drug_A']
dds_subset <- DESeq(dds_subset)

Subset Genes

dds_genes <- dds[rownames(dds) %in% gene_list,]

Accessing Results

# Summary
summary(res)

# Order by significance
resOrdered <- res[order(res$padj),]

# Order by fold change
resOrdered <- res[order(abs(res$log2FoldChange), decreasing = TRUE),]

# Convert to data frame
res_df <- as.data.frame(res)
res_df$gene <- rownames(res_df)

# Count significant
sum(res$padj < 0.05, na.rm = TRUE)
sum(res$padj < 0.05 & res$log2FoldChange > 0, na.rm = TRUE)  # Up
sum(res$padj < 0.05 & res$log2FoldChange < 0, na.rm = TRUE)  # Down

Exporting Results

# Results tables
write.csv(as.data.frame(res), file = 'deseq2_results.csv')
sig <- subset(res, padj < 0.05)
write.csv(as.data.frame(sig), file = 'deseq2_significant.csv')

# Normalized counts
write.csv(counts(dds, normalized = TRUE), file = 'normalized_counts.csv')

# Transformed data
vsd <- vst(dds, blind = FALSE)
write.csv(assay(vsd), file = 'vst_transformed_counts.csv')

# Save object
saveRDS(dds, "dds_object.rds")
dds <- readRDS("dds_object.rds")

Common Errors (Brief)

ErrorCauseSolution
"design matrix not full rank"Confounded variablesRemove confounded variable or collapse
"counts matrix should be integers"Non-integer countsUse raw counts; for tximport use DESeqDataSetFromTximport()
"every gene contains at least one zero"Wrong orientationTranspose: t(counts)
"factor levels not in colData"Typo in designCheck colnames(colData(dds))
"subscript out of bounds"Sample name mismatchReorder: coldata <- coldata[colnames(counts), ]

Detailed solutions: troubleshooting.md.

Best Practices

  • Pre-filter low count genes before DESeq().
  • Set reference level explicitly with relevel().
  • Use shrinkage for visualization/ranking, not for testing.
  • Use padj (adjusted p-value), never pvalue alone.
  • Check QC plots before interpreting (PCA, dispersion).
  • Use vst() for large datasets, rlog() for small.
  • Document design formula and contrasts.
  • Report DESeq2 version: packageVersion('DESeq2').
  • Save session info: sessionInfo().

Related Documentation

  • decision-guide.md — method selection decision trees
  • troubleshooting.md — detailed error solutions
  • usage-guide.md — quick prompts and examples

What ships with it: 12 files

77.8 KB alongside SKILL.md

Keep looking

Skills are one crate of 326,835. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.