9.3 KiB
9.3 KiB
title, task, lineage_type, upstream_source, upstream_sha, imported_at, prompt_class, upstream_changes, author, validated
| title | task | lineage_type | upstream_source | upstream_sha | imported_at | prompt_class | upstream_changes | author | validated |
|---|---|---|---|---|---|---|---|---|---|
| PyDESeq2 Complete Workflow | import | https://github.com/mims-harvard/ToolUniverse/blob/e2520a96/skills/tooluniverse-rnaseq-deseq2/references/pydeseq2_workflow.md | e2520a96 | 2026-06-26 | prompt | accepted | upstream | false |
PyDESeq2 Complete Workflow
Comprehensive code examples for PyDESeq2 analysis.
Basic Single-Factor Analysis
import pandas as pd
from pydeseq2.dds import DeseqDataSet
from pydeseq2.ds import DeseqStats
# Assume counts and metadata are already loaded and validated
# Set reference level (first category becomes reference)
metadata['condition'] = pd.Categorical(
metadata['condition'],
categories=['control', 'treatment'] # control is reference
)
# Create DESeq2 dataset
dds = DeseqDataSet(
counts=counts,
metadata=metadata,
design="~condition",
quiet=True
)
# Run DESeq2 pipeline (normalization + dispersion + testing)
dds.deseq2()
# Extract results for contrast
stat_res = DeseqStats(
dds,
contrast=['condition', 'treatment', 'control'],
alpha=0.05,
quiet=True
)
stat_res.run_wald_test()
stat_res.summary()
# Get results DataFrame
results = stat_res.results_df
Multi-Factor Design
# Design with multiple factors
metadata['strain'] = pd.Categorical(metadata['strain'], categories=['WT', 'mutant'])
metadata['media'] = pd.Categorical(metadata['media'], categories=['LB', 'M9'])
# Multi-factor design
dds = DeseqDataSet(
counts=counts,
metadata=metadata,
design="~strain + media", # Additive model
quiet=True
)
dds.deseq2()
# Extract results for strain effect (controlling for media)
stat_res = DeseqStats(dds, contrast=['strain', 'mutant', 'WT'], quiet=True)
stat_res.run_wald_test()
stat_res.summary()
results_strain = stat_res.results_df
Interaction Design
# Design with interaction term
dds = DeseqDataSet(
counts=counts,
metadata=metadata,
design="~strain + treatment + strain:treatment",
quiet=True
)
dds.deseq2()
# Main effect of treatment
stat_res_treatment = DeseqStats(dds, contrast=['treatment', 'treated', 'control'], quiet=True)
stat_res_treatment.run_wald_test()
# Interaction effect (requires coefficient name)
# Check available coefficients
print(dds.varm['LFC'].columns) # View all coefficients
# Extract interaction results
stat_res_interaction = DeseqStats(dds, quiet=True)
# Note: interaction testing requires specifying coefficient directly
Batch Effect Correction
# Include batch as covariate
metadata['batch'] = pd.Categorical(metadata['batch'])
metadata['condition'] = pd.Categorical(metadata['condition'], categories=['control', 'treatment'])
dds = DeseqDataSet(
counts=counts,
metadata=metadata,
design="~batch + condition", # Batch first
quiet=True
)
dds.deseq2()
# Extract condition effect (adjusted for batch)
stat_res = DeseqStats(dds, contrast=['condition', 'treatment', 'control'], quiet=True)
stat_res.run_wald_test()
stat_res.summary()
results = stat_res.results_df
Continuous Covariates
# Design with continuous variable (e.g., age, time)
dds = DeseqDataSet(
counts=counts,
metadata=metadata,
design="~age + condition",
continuous_factors=['age'], # Specify continuous variables
quiet=True
)
dds.deseq2()
# Extract condition effect (adjusted for age)
stat_res = DeseqStats(dds, contrast=['condition', 'treatment', 'control'], quiet=True)
stat_res.run_wald_test()
results = stat_res.results_df
LFC Shrinkage
# After running Wald test, apply shrinkage
stat_res = DeseqStats(dds, contrast=['condition', 'treatment', 'control'], quiet=True)
stat_res.run_wald_test()
# Determine coefficient name for shrinkage
# Format: factor[T.level] where level is the numerator
coeff = 'condition[T.treatment]'
# Verify coefficient exists
if coeff in dds.varm['LFC'].columns:
stat_res.lfc_shrink(coeff=coeff)
else:
print(f"WARNING: Coefficient '{coeff}' not found.")
print(f"Available: {list(dds.varm['LFC'].columns)}")
results = stat_res.results_df
Set Reference Level
CRITICAL: In PyDESeq2 v0.5.4+, use pd.Categorical with ordered categories. The FIRST category is the reference.
def set_reference_level(metadata, factor_col, ref_value):
"""Set reference level for a factor by reordering Categorical.
The FIRST category becomes the reference level in PyDESeq2.
"""
current_cats = metadata[factor_col].unique().tolist()
if ref_value not in current_cats:
raise ValueError(f"Reference '{ref_value}' not in categories: {current_cats}")
# Put reference first
ordered_cats = [ref_value] + [c for c in current_cats if c != ref_value]
metadata[factor_col] = pd.Categorical(
metadata[factor_col],
categories=ordered_cats
)
return metadata
# Usage
metadata = set_reference_level(metadata, 'condition', 'wildtype')
Multiple Contrasts
# Run DESeq2 once
dds = DeseqDataSet(counts=counts, metadata=metadata, design="~condition", quiet=True)
dds.deseq2()
# Extract multiple contrasts
contrasts = [
['condition', 'A', 'control'],
['condition', 'B', 'control'],
['condition', 'C', 'control']
]
results_dict = {}
for contrast in contrasts:
stat_res = DeseqStats(dds, contrast=contrast, quiet=True)
stat_res.run_wald_test()
stat_res.summary()
contrast_name = f"{contrast[1]}_vs_{contrast[2]}"
results_dict[contrast_name] = stat_res.results_df
Alternative Multiple Testing Correction
from statsmodels.stats.multitest import multipletests
import numpy as np
# Run DESeq2 with default BH correction
stat_res = DeseqStats(dds, contrast=['condition', 'treatment', 'control'], quiet=True)
stat_res.run_wald_test()
results = stat_res.results_df
# Apply alternative correction
pvalues = results['pvalue'].values
mask = ~np.isnan(pvalues)
# Bonferroni
_, results.loc[mask, 'padj_bonf'], _, _ = multipletests(pvalues[mask], method='bonferroni')
# Benjamini-Yekutieli
_, results.loc[mask, 'padj_by'], _, _ = multipletests(pvalues[mask], method='fdr_by')
# Holm
_, results.loc[mask, 'padj_holm'], _, _ = multipletests(pvalues[mask], method='holm')
Dispersion Fitting Options
# Parametric fit (default, recommended for large samples)
dds = DeseqDataSet(counts=counts, metadata=metadata, design="~condition", fit_type='parametric')
dds.deseq2()
# Mean fit (for small samples or when parametric fails)
dds = DeseqDataSet(counts=counts, metadata=metadata, design="~condition", fit_type='mean')
dds.deseq2()
# If dispersion trend doesn't converge, use mean
try:
dds = DeseqDataSet(counts=counts, metadata=metadata, design="~condition", fit_type='parametric')
dds.deseq2()
except Exception as e:
print(f"Parametric fit failed: {e}")
print("Retrying with fit_type='mean'")
dds = DeseqDataSet(counts=counts, metadata=metadata, design="~condition", fit_type='mean')
dds.deseq2()
Access Normalized Counts
# After running dds.deseq2(), normalized counts are available
normalized_counts = dds.obsm['normed_counts'] # DataFrame, same shape as counts
# Size factors
size_factors = dds.obs['size_factors']
# Manual normalization
manual_norm = counts.div(size_factors, axis=0)
Cook's Distance Filtering
# Set minimum replicates for Cook's filtering
dds = DeseqDataSet(
counts=counts,
metadata=metadata,
design="~condition",
min_replicates=7, # Default is 7
quiet=True
)
dds.deseq2()
# Genes with Cook's outliers will have padj = NA
# These are automatically excluded in downstream filtering
Pre-filtering Low-Count Genes
# Filter genes with low mean counts before DESeq2 (optional, improves speed)
min_count = 10
keep_genes = counts.sum(axis=0) >= min_count
counts_filtered = counts.loc[:, keep_genes]
# Then run DESeq2 on filtered data
dds = DeseqDataSet(counts=counts_filtered, metadata=metadata, design="~condition", quiet=True)
dds.deseq2()
Complete Example: Multi-Factor with Shrinkage
import pandas as pd
from pydeseq2.dds import DeseqDataSet
from pydeseq2.ds import DeseqStats
# Load and validate data (from data_loading.md)
counts = pd.read_csv("counts.csv", index_col=0)
metadata = pd.read_csv("metadata.csv", index_col=0)
# Orient and validate
if counts.shape[0] > counts.shape[1] * 5:
counts = counts.T
common = sorted(set(counts.index) & set(metadata.index))
counts = counts.loc[common].astype(int)
metadata = metadata.loc[common]
# Set reference levels
metadata['strain'] = pd.Categorical(metadata['strain'], categories=['WT', 'mutant'])
metadata['replicate'] = pd.Categorical(metadata['replicate'])
# Create and fit DESeq2 model
dds = DeseqDataSet(
counts=counts,
metadata=metadata,
design="~replicate + strain",
quiet=True
)
dds.deseq2()
# Extract results with shrinkage
stat_res = DeseqStats(dds, contrast=['strain', 'mutant', 'WT'], quiet=True)
stat_res.run_wald_test()
stat_res.summary()
stat_res.lfc_shrink(coeff='strain[T.mutant]')
results = stat_res.results_df
# Filter DEGs
sig_genes = results[
(results['padj'] < 0.05) &
(results['log2FoldChange'].abs() > 0.5)
]
print(f"Significant DEGs: {len(sig_genes)}")
print(f"Upregulated: {(sig_genes['log2FoldChange'] > 0).sum()}")
print(f"Downregulated: {(sig_genes['log2FoldChange'] < 0).sum()}")