9.6 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 |
|---|---|---|---|---|---|---|---|---|---|
| Troubleshooting Guide | import | https://github.com/mims-harvard/ToolUniverse/blob/e2520a96/skills/tooluniverse-rnaseq-deseq2/references/troubleshooting.md | e2520a96 | 2026-06-26 | prompt | accepted | upstream | false |
Troubleshooting Guide
Common issues and solutions for RNA-seq analysis with PyDESeq2.
Data Loading Issues
"No matching samples between counts and metadata"
Cause: Sample names don't match between count matrix and metadata.
Solutions:
# Check sample names
print("Count samples:", list(counts.index)[:5])
print("Metadata samples:", list(metadata.index)[:5])
# Try case-insensitive matching
counts.index = counts.index.str.lower()
metadata.index = metadata.index.str.lower()
# Remove whitespace
counts.index = counts.index.str.strip()
metadata.index = metadata.index.str.strip()
# Try transpose
if set(counts.columns) & set(metadata.index):
counts = counts.T
"Non-integer counts detected"
Cause: Data is normalized (FPKM, TPM) or has floating point values.
Solutions:
# Option 1: Round to integers (acceptable if close to integers)
counts = counts.round().astype(int)
# Option 2: Use t-test instead of DESeq2 for normalized data
from scipy import stats
stat, pval = stats.ttest_ind(group1, group2)
# Check if data is truly raw counts
print(counts.head())
print("Min value:", counts.min().min())
print("Has decimals:", (counts % 1 != 0).any().any())
Matrix orientation wrong
Cause: Genes are rows instead of columns.
Solution:
# Check shape
print(f"Counts shape: {counts.shape}") # Should be (n_samples, n_genes)
print(f"Samples >> genes? {counts.shape[0] > counts.shape[1] * 5}")
# Transpose if needed
if counts.shape[0] > counts.shape[1] * 5:
counts = counts.T
DESeq2 Execution Issues
"Dispersion trend did not converge"
Cause: Small sample size or low variation.
Solution:
# Use mean fit instead of parametric
dds = DeseqDataSet(
counts=counts,
metadata=metadata,
design="~condition",
fit_type='mean', # Instead of 'parametric'
quiet=True
)
dds.deseq2()
"Contrast not found"
Cause: Wrong factor/level names in contrast.
Solution:
# Check available levels
print("Available factors:", metadata.columns)
print("Condition levels:", metadata['condition'].unique())
# Verify exact names (case-sensitive)
contrast = ['condition', 'treatment', 'control'] # Must match exactly
"All genes filtered out"
Cause: Too strict pre-filtering or all counts are zero.
Solution:
# Check data quality
print("Zero genes:", (counts.sum(axis=0) == 0).sum())
print("Low count genes:", (counts.sum(axis=0) < 10).sum())
# Remove only zero genes
nonzero = counts.sum(axis=0) > 0
counts = counts.loc[:, nonzero]
# Don't pre-filter too aggressively
# DESeq2 handles low counts internally
"Single replicate per condition"
Cause: Only 1 sample per condition - cannot estimate dispersion.
Solution:
# Cannot run DESeq2 with single replicates
# Use fold-change only
mean_A = counts[samples_A].mean(axis=1)
mean_B = counts[samples_B].mean(axis=1)
log2fc = np.log2((mean_B + 1) / (mean_A + 1))
# Or pool replicates from similar conditions
Reference Level Issues
Wrong reference level
Cause: Reference level not set correctly.
Solution:
# In PyDESeq2, FIRST category is reference
metadata['condition'] = pd.Categorical(
metadata['condition'],
categories=['control', 'treatment'] # Control first = reference
)
# Verify
print("Categories:", metadata['condition'].cat.categories)
print("First category (reference):", metadata['condition'].cat.categories[0])
LFC Shrinkage Issues
"Coefficient not found for shrinkage"
Cause: Wrong coefficient name format.
Solution:
# Check available coefficients
print("Available coefficients:", list(dds.varm['LFC'].columns))
# Standard format: factor[T.level]
coeff = 'condition[T.treatment]'
# Verify before shrinking
if coeff in dds.varm['LFC'].columns:
stat_res.lfc_shrink(coeff=coeff)
else:
print(f"ERROR: '{coeff}' not found")
print("Skipping shrinkage")
Result Extraction Issues
"NaN in padj column"
Cause: Independent filtering removed genes with insufficient evidence.
Solution:
# This is EXPECTED behavior
# Remove NaN before counting DEGs
sig_genes = results.dropna(subset=['padj'])
sig_genes = sig_genes[sig_genes['padj'] < 0.05]
# Don't include NaN genes in counts
answer = len(sig_genes) # Correct
# NOT: len(results[results['padj'] < 0.05]) # Wrong, includes NaN
"Gene not found in results"
Cause: Gene name case mismatch or gene filtered out.
Solution:
# Case-insensitive search
def find_gene(results_df, gene_name):
# Exact match
if gene_name in results_df.index:
return gene_name
# Case-insensitive
idx_lower = {g.lower(): g for g in results_df.index}
if gene_name.lower() in idx_lower:
return idx_lower[gene_name.lower()]
# Partial match
matches = [g for g in results_df.index if gene_name.lower() in g.lower()]
if len(matches) == 1:
return matches[0]
elif len(matches) > 1:
print(f"Multiple matches: {matches}")
return None
gene = find_gene(results, "TP53")
if gene:
lfc = results.loc[gene, 'log2FoldChange']
Enrichment Analysis Issues
"No enrichment results"
Cause: Gene list too small or wrong organism/library.
Solution:
# Check gene list
print(f"Gene list size: {len(gene_list)}")
print(f"Sample genes: {gene_list[:5]}")
# Try different libraries
libraries = [
'GO_Biological_Process_2023',
'GO_Biological_Process_2021',
'KEGG_2021_Human',
'KEGG_2019_Mouse'
]
for lib in libraries:
try:
enr = gp.enrich(gene_list=gene_list, gene_sets=lib, outdir=None, no_plot=True)
if len(enr.results) > 0:
print(f"✓ {lib}: {len(enr.results)} results")
break
except Exception as e:
print(f"✗ {lib}: {e}")
"gseapy organism parameter deprecated"
Cause: Using old gseapy syntax with organism parameter.
Solution:
# OLD (doesn't work in gseapy >= 1.1)
# enr = gp.enrich(gene_list=gene_list, organism='human', gene_sets='GO_Biological_Process')
# NEW (correct)
enr = gp.enrich(
gene_list=gene_list,
gene_sets='GO_Biological_Process_2023', # Organism in library name
outdir=None
)
Memory Issues
"Memory error with large gene set"
Cause: Too many genes (60K+ genes with many samples).
Solution:
# Pre-filter low-count genes
min_count = 10
keep = counts.sum(axis=0) >= min_count
counts_filtered = counts.loc[:, keep]
print(f"Kept {keep.sum()} / {len(keep)} genes")
# Use sparse matrices (if applicable)
from scipy.sparse import csr_matrix
counts_sparse = csr_matrix(counts.values)
Multi-Factor Design Issues
"Singular design matrix"
Cause: Confounded factors (e.g., batch perfectly correlated with condition).
Solution:
# Check confounding
print(pd.crosstab(metadata['batch'], metadata['condition']))
# If confounded, cannot separate effects
# Remove confounded factor from design
design = "~condition" # Remove batch if confounded
Performance Issues
"DESeq2 takes too long"
Cause: Large dataset or many factors.
Solutions:
# Use parallel processing (if available)
import multiprocessing
n_cpus = multiprocessing.cpu_count()
# Note: PyDESeq2 doesn't directly support n_cpus, but numpy may use multiple cores
# Pre-filter more aggressively
min_count = 10
min_samples = 3
keep = (counts >= min_count).sum(axis=0) >= min_samples
counts_filtered = counts.loc[:, keep]
# Use mean fit instead of parametric (faster)
dds = DeseqDataSet(counts=counts, metadata=metadata, design="~condition",
fit_type='mean', quiet=True)
Validation Issues
Results don't match expected answer
Checks:
# 1. Check reference level
print("Reference:", metadata['condition'].cat.categories[0])
# 2. Check contrast direction
print("Contrast:", contrast) # [factor, numerator, denominator]
# 3. Check filtering thresholds
print(f"padj < {padj_threshold}")
print(f"|log2FC| > {lfc_threshold}")
# 4. Check if shrinkage was applied
print("Shrinkage applied:", 'lfcSE' in results.columns)
# 5. Check for NaN handling
print("Genes with NaN padj:", results['padj'].isna().sum())
Debugging Checklist
Before reporting issues, verify:
- Count matrix has samples as rows, genes as columns
- Counts are non-negative integers
- Metadata index matches count matrix index exactly
- Design formula references valid column names in metadata
- Reference level is set correctly (first category in Categorical)
- Contrast factor and levels exist in metadata
- LFC shrinkage coefficient name matches pydeseq2 format
- Filtering thresholds match question exactly
- NaN values in padj are excluded from DEG counts
Getting Help
# Print diagnostic information
print("=" * 50)
print("DIAGNOSTIC INFORMATION")
print("=" * 50)
print(f"Counts shape: {counts.shape}")
print(f"Metadata shape: {metadata.shape}")
print(f"Design: {design}")
print(f"Reference level: {metadata['condition'].cat.categories[0]}")
print(f"Contrast: {contrast}")
print(f"PyDESeq2 version: {pydeseq2.__version__}")
print(f"\nSample counts:\n{counts.iloc[:3, :3]}")
print(f"\nMetadata:\n{metadata.head(3)}")
print(f"\nResults:\n{results.head(3)}")
print("=" * 50)