283 lines
9.1 KiB
Markdown
283 lines
9.1 KiB
Markdown
---
|
|
title: "Genomics & Epigenomics Data Processing - Quick Start"
|
|
task: ""
|
|
lineage_type: import
|
|
upstream_source: https://github.com/mims-harvard/ToolUniverse/blob/e2520a96/skills/tooluniverse-epigenomics/QUICK_START.md
|
|
upstream_sha: e2520a96
|
|
imported_at: 2026-06-26
|
|
prompt_class: prompt
|
|
upstream_changes: accepted
|
|
author: upstream
|
|
validated: false
|
|
---
|
|
|
|
# Genomics & Epigenomics Data Processing - Quick Start
|
|
|
|
## Overview
|
|
|
|
This skill processes epigenomics data files (methylation arrays, ChIP-seq peaks, ATAC-seq data) and answers quantitative questions using pure Python (pandas, scipy, statsmodels) plus ToolUniverse annotation tools. Designed for questions about CpG sites, differential methylation, chromatin accessibility, and multi-omics integration.
|
|
|
|
---
|
|
|
|
## Quick Start Examples
|
|
|
|
### Example 1: Differential Methylation Analysis
|
|
|
|
**Question**: "How many CpG sites show significant differential methylation between tumor and normal?"
|
|
|
|
```python
|
|
import pandas as pd
|
|
import numpy as np
|
|
from scipy import stats
|
|
import statsmodels.stats.multitest as mt
|
|
|
|
# Load data
|
|
beta = pd.read_csv('methylation_beta_values.csv', index_col=0)
|
|
clinical = pd.read_csv('clinical_data.csv', index_col=0)
|
|
|
|
# Define groups
|
|
tumor_samples = clinical[clinical['sample_type'] == 'Tumor'].index.tolist()
|
|
normal_samples = clinical[clinical['sample_type'] == 'Normal'].index.tolist()
|
|
|
|
# Filter to common samples
|
|
tumor_samples = [s for s in tumor_samples if s in beta.columns]
|
|
normal_samples = [s for s in normal_samples if s in beta.columns]
|
|
|
|
# Differential methylation (vectorized for speed)
|
|
g1 = beta[tumor_samples]
|
|
g2 = beta[normal_samples]
|
|
|
|
results = pd.DataFrame({
|
|
'mean_tumor': g1.mean(axis=1),
|
|
'mean_normal': g2.mean(axis=1),
|
|
'delta_beta': g1.mean(axis=1) - g2.mean(axis=1),
|
|
})
|
|
|
|
# T-test per probe
|
|
pvalues = []
|
|
for probe in beta.index:
|
|
vals1 = g1.loc[probe].dropna().values
|
|
vals2 = g2.loc[probe].dropna().values
|
|
if len(vals1) >= 2 and len(vals2) >= 2:
|
|
_, pval = stats.ttest_ind(vals1, vals2, equal_var=False)
|
|
pvalues.append(pval)
|
|
else:
|
|
pvalues.append(np.nan)
|
|
|
|
results['pvalue'] = pvalues
|
|
valid = results['pvalue'].dropna()
|
|
reject, padj, _, _ = mt.multipletests(valid.values, method='fdr_bh')
|
|
results.loc[valid.index, 'padj'] = padj
|
|
|
|
# Count significant
|
|
n_sig = (results['padj'] < 0.05).sum()
|
|
print(f"Significant DMPs (padj < 0.05): {n_sig}")
|
|
```
|
|
|
|
---
|
|
|
|
### Example 2: Age-Related CpG Chromosome Density
|
|
|
|
**Question**: "What is the ratio of filtered age-related CpG density between chr19 and chr1?"
|
|
|
|
```python
|
|
import pandas as pd
|
|
import numpy as np
|
|
from scipy import stats
|
|
import statsmodels.stats.multitest as mt
|
|
|
|
# Load data
|
|
beta = pd.read_csv('methylation_beta_values.csv', index_col=0)
|
|
manifest = pd.read_csv('probe_manifest.csv')
|
|
clinical = pd.read_csv('clinical_data.csv', index_col=0)
|
|
|
|
# Get ages
|
|
ages = clinical['age_at_diagnosis'].values
|
|
|
|
# Correlate each probe with age
|
|
correlations = []
|
|
for probe in beta.index:
|
|
vals = beta.loc[probe].values
|
|
mask = ~np.isnan(vals) & ~np.isnan(ages)
|
|
if sum(mask) >= 5:
|
|
corr, pval = stats.pearsonr(ages[mask], vals[mask])
|
|
correlations.append({'probe': probe, 'corr': corr, 'pvalue': pval})
|
|
|
|
corr_df = pd.DataFrame(correlations).set_index('probe')
|
|
reject, padj, _, _ = mt.multipletests(corr_df['pvalue'].values, method='fdr_bh')
|
|
corr_df['padj'] = padj
|
|
|
|
# Filter significant age-related CpGs
|
|
age_cpgs = corr_df[corr_df['padj'] < 0.05].index.tolist()
|
|
|
|
# Map to chromosomes
|
|
def normalize_chr(c):
|
|
c = str(c).strip()
|
|
return f'chr{c}' if not str(c).startswith('chr') else c
|
|
|
|
manifest_idx = manifest.set_index('Name') # or 'probe_id'
|
|
probe_chrs = manifest_idx.loc[manifest_idx.index.isin(age_cpgs), 'CHR']
|
|
probe_chrs = probe_chrs.apply(normalize_chr)
|
|
chr_counts = probe_chrs.value_counts()
|
|
|
|
# Chromosome lengths (hg38)
|
|
chr_lengths = {
|
|
'chr1': 248956422, 'chr19': 58617616,
|
|
# ... (full dict in SKILL.md)
|
|
}
|
|
|
|
# Calculate density
|
|
density_19 = chr_counts.get('chr19', 0) / chr_lengths['chr19']
|
|
density_1 = chr_counts.get('chr1', 0) / chr_lengths['chr1']
|
|
ratio = density_19 / density_1
|
|
print(f"chr19/chr1 density ratio: {ratio:.4f}")
|
|
```
|
|
|
|
---
|
|
|
|
### Example 3: Multi-Omics Missing Data Analysis
|
|
|
|
**Question**: "How many patients have no missing data for vital status, gene expression, and methylation data?"
|
|
|
|
```python
|
|
import pandas as pd
|
|
|
|
# Load data
|
|
clinical = pd.read_csv('clinical_data.csv', index_col=0)
|
|
expression = pd.read_csv('expression_matrix.csv', index_col=0) # genes x samples
|
|
methylation = pd.read_csv('methylation_beta.csv', index_col=0) # probes x samples
|
|
|
|
# Get samples with vital_status
|
|
clinical_with_vital = set(clinical[clinical['vital_status'].notna()].index)
|
|
|
|
# Get samples in expression data
|
|
expression_samples = set(expression.columns)
|
|
|
|
# Get samples in methylation data
|
|
methylation_samples = set(methylation.columns)
|
|
|
|
# Intersection
|
|
complete = clinical_with_vital & expression_samples & methylation_samples
|
|
print(f"Patients with complete data: {len(complete)}")
|
|
```
|
|
|
|
---
|
|
|
|
### Example 4: ChIP-seq Peak Analysis
|
|
|
|
**Question**: "How many ChIP-seq peaks overlap with promoter regions?"
|
|
|
|
```python
|
|
import pandas as pd
|
|
|
|
# Load peak file
|
|
peaks = pd.read_csv('H3K27ac_peaks.narrowPeak', sep='\t', header=None,
|
|
names=['chrom', 'start', 'end', 'name', 'score', 'strand',
|
|
'signalValue', 'pValue', 'qValue', 'peak'])
|
|
|
|
# Load gene annotation (or use Ensembl via ToolUniverse)
|
|
genes = pd.read_csv('gene_annotation.bed', sep='\t',
|
|
names=['chr', 'start', 'end', 'gene_name', 'score', 'strand'])
|
|
|
|
# Define promoters (TSS +/- 2000bp)
|
|
promoters = genes.copy()
|
|
promoters['prom_start'] = promoters.apply(
|
|
lambda g: g['start'] - 2000 if g['strand'] == '+' else g['end'] - 2000, axis=1)
|
|
promoters['prom_end'] = promoters.apply(
|
|
lambda g: g['start'] + 500 if g['strand'] == '+' else g['end'] + 500, axis=1)
|
|
|
|
# Count overlaps (pure Python)
|
|
n_promoter_peaks = 0
|
|
for _, peak in peaks.iterrows():
|
|
chr_proms = promoters[promoters['chr'] == peak['chrom']]
|
|
overlap = chr_proms[
|
|
(chr_proms['prom_start'] < peak['end']) &
|
|
(chr_proms['prom_end'] > peak['start'])
|
|
]
|
|
if len(overlap) > 0:
|
|
n_promoter_peaks += 1
|
|
|
|
print(f"Peaks in promoters: {n_promoter_peaks}/{len(peaks)} ({100*n_promoter_peaks/len(peaks):.1f}%)")
|
|
```
|
|
|
|
---
|
|
|
|
### Example 5: Genome-Wide CpG Density
|
|
|
|
**Question**: "What is the genome-wide average chromosomal density of unique age-related CpGs per base pair?"
|
|
|
|
```python
|
|
# After identifying age-related CpGs and chromosome mapping (Example 2)
|
|
total_cpgs = chr_counts.sum()
|
|
total_genome_length = sum(chr_lengths[c] for c in chr_counts.index if c in chr_lengths)
|
|
genome_wide_density = total_cpgs / total_genome_length
|
|
print(f"Genome-wide density: {genome_wide_density:.2e} CpGs/bp")
|
|
```
|
|
|
|
---
|
|
|
|
## ToolUniverse Annotation
|
|
|
|
Use ToolUniverse for biological context after computational analysis:
|
|
|
|
```python
|
|
from tooluniverse import ToolUniverse
|
|
tu = ToolUniverse()
|
|
tu.load_tools()
|
|
|
|
# Annotate genes from differential methylation
|
|
gene = "TP53"
|
|
ens = tu.tools.ensembl_lookup_gene(id=gene, species='homo_sapiens')
|
|
|
|
# Get regulatory elements near a gene
|
|
screen = tu.tools.SCREEN_get_regulatory_elements(
|
|
gene_name="TP53", element_type="enhancer", limit=10
|
|
)
|
|
|
|
# Find ChIP-seq experiments for histone mark
|
|
chipatlas = tu.tools.ChIPAtlas_get_experiments(
|
|
operation="get_experiment_list",
|
|
genome="hg38",
|
|
antigen="H3K27ac",
|
|
limit=20
|
|
)
|
|
|
|
# Get regulatory features for a region
|
|
ensembl_reg = tu.tools.ensembl_get_regulatory_features(
|
|
region="17:7661779-7687550", # No "chr" prefix
|
|
feature="regulatory",
|
|
species="human"
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## Key Functions Reference
|
|
|
|
| Function | Purpose | Input | Output |
|
|
|----------|---------|-------|--------|
|
|
| `load_methylation_data()` | Load beta/M-value matrix | file path | DataFrame |
|
|
| `detect_methylation_type()` | Detect beta vs M-values | DataFrame | 'beta' or 'mvalue' |
|
|
| `filter_cpg_probes()` | Filter probes by criteria | DataFrame + filters | filtered DataFrame |
|
|
| `differential_methylation()` | DM analysis between groups | beta + samples | DataFrame with padj |
|
|
| `identify_age_related_cpgs()` | Age-correlated CpGs | beta + ages | DataFrame with padj |
|
|
| `chromosome_cpg_density()` | CpG density per chromosome | probes + manifest | density DataFrame |
|
|
| `genome_wide_average_density()` | Overall genome density | density DataFrame | float |
|
|
| `chromosome_density_ratio()` | Ratio between chromosomes | density + chr names | float |
|
|
| `load_bed_file()` | Load BED/narrowPeak | file path | DataFrame |
|
|
| `peak_statistics()` | Basic peak stats | BED DataFrame | dict |
|
|
| `annotate_peaks_to_genes()` | Annotate peaks to genes | peaks + genes | annotated DataFrame |
|
|
| `find_overlaps()` | Peak overlap analysis | two BED DataFrames | overlap DataFrame |
|
|
| `missing_data_analysis()` | Cross-modality completeness | multiple DataFrames | dict |
|
|
| `correlate_methylation_expression()` | Meth-expression correlation | beta + expression | correlation DataFrame |
|
|
|
|
---
|
|
|
|
## Genome Builds Supported
|
|
|
|
| Build | Species | Autosomes | Sex Chromosomes |
|
|
|-------|---------|-----------|-----------------|
|
|
| hg38 (GRCh38) | Human | chr1-chr22 | chrX, chrY |
|
|
| hg19 (GRCh37) | Human | chr1-chr22 | chrX, chrY |
|
|
| mm10 (GRCm38) | Mouse | chr1-chr19 | chrX, chrY |
|