Differential gene expression analysis for bulk RNA-seq count matrices using a DESeq2-like workflow in Python; use when you need Wald tests, FDR correction, and optional LFC shrinkage for condition/batch/covariate designs.
68
83%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Use this skill when you need to run DESeq2-style differential expression in Python, especially in these scenarios:
~ batch + condition, ~ age + condition).padj).[variable, test_group, reference_group].Minimum environment (as documented in the source material):
pydeseq2 (install via pip/uv)pandas >= 1.4.3numpy >= 1.23.0scipy >= 1.11.0scikit-learn >= 1.1.1anndata >= 0.8.0 (optional, for AnnData I/O)Optional plotting:
matplotlib (recommended)seaborn (optional)Installation:
uv pip install pydeseq2The following script is a complete, runnable example for a standard treated-vs-control analysis.
import pandas as pd
import numpy as np
from pydeseq2.dds import DeseqDataSet
from pydeseq2.ds import DeseqStats
# -----------------------------
# 1) Load inputs
# -----------------------------
# counts.csv is commonly stored as genes x samples; transpose to samples x genes.
counts_df = pd.read_csv("counts.csv", index_col=0).T
metadata = pd.read_csv("metadata.csv", index_col=0)
# Ensure sample alignment
common = counts_df.index.intersection(metadata.index)
counts_df = counts_df.loc[common]
metadata = metadata.loc[common]
# -----------------------------
# 2) Basic filtering
# -----------------------------
# Remove genes with very low total counts
min_total_counts = 10
genes_to_keep = counts_df.columns[counts_df.sum(axis=0) >= min_total_counts]
counts_df = counts_df[genes_to_keep]
# Drop samples with missing condition
metadata = metadata.dropna(subset=["condition"])
counts_df = counts_df.loc[metadata.index]
# -----------------------------
# 3) Fit DESeq2 model
# -----------------------------
dds = DeseqDataSet(
counts=counts_df,
metadata=metadata,
design="~ condition",
refit_cooks=True,
n_cpus=1,
)
dds.deseq2()
# -----------------------------
# 4) Wald test with contrast
# -----------------------------
ds = DeseqStats(
dds,
contrast=["condition", "treated", "control"],
alpha=0.05,
cooks_filter=True,
independent_filter=True,
)
ds.summary()
# -----------------------------
# 5) Results + optional shrinkage
# -----------------------------
res = ds.results_df.copy()
sig = res[res["padj"] < 0.05].sort_values("padj")
print(f"Significant genes (padj < 0.05): {len(sig)}")
# Optional: shrink LFC for visualization/ranking (p-values do not change)
ds.lfc_shrink()
res_shrunk = ds.results_df.copy()
# Export
res.to_csv("deseq2_results.csv")
res_shrunk.to_csv("deseq2_results_shrunk_lfc.csv")
sig.to_csv("significant_genes.csv")
# -----------------------------
# 6) Minimal volcano plot (optional)
# -----------------------------
try:
import matplotlib.pyplot as plt
plot_df = res.copy()
plot_df["neglog10_padj"] = -np.log10(plot_df["padj"].clip(lower=1e-300))
is_sig = plot_df["padj"] < 0.05
plt.figure(figsize=(9, 5))
plt.scatter(
plot_df.loc[~is_sig, "log2FoldChange"],
plot_df.loc[~is_sig, "neglog10_padj"],
s=10,
alpha=0.3,
c="gray",
label="Not significant",
)
plt.scatter(
plot_df.loc[is_sig, "log2FoldChange"],
plot_df.loc[is_sig, "neglog10_padj"],
s=10,
alpha=0.6,
c="red",
label="padj < 0.05",
)
plt.axhline(-np.log10(0.05), linestyle="--", color="blue", alpha=0.5)
plt.xlabel("Log2 Fold Change")
plt.ylabel("-Log10(adjusted p-value)")
plt.title("Volcano Plot")
plt.legend()
plt.tight_layout()
plt.savefig("volcano_plot.png", dpi=300)
except ImportError:
pass.T after loading.~ condition (single factor)~ batch + condition (batch-adjusted)~ age + condition (continuous covariate)~ group + condition + group:condition (interaction)~ batch + condition) so the primary effect is interpreted cleanly.dds.deseq2() does (high level)The fitting pipeline typically includes:
refit_cooks=True)DeseqStats(...).summary() runs Wald tests for the requested coefficient/contrast.baseMean: mean normalized expressionlog2FoldChange, lfcSE, statpvalue: raw p-valuepadj: Benjamini–Hochberg FDR adjusted p-valuepadj < alpha (commonly 0.05) for significance.contrast=["variable", "test_group", "reference_group"]["condition", "treated", "control"] tests treated relative to control.ds.lfc_shrink() applies shrinkage to log2FoldChange for more stable ranking/plots.If your repository includes them, use:
references/api_reference.md for parameter/object details.references/workflow_guide.md for extended workflows and troubleshooting.scripts/run_deseq2_analysis.py for a CLI-style batch workflow (counts/metadata/design/contrast/output, optional plots).63c61d3
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.