Skip to content
Back to skills

Bio Applied Dmr Analysis

ASecurity

Differentially Methylated Regions (DMRs)

  • 4 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added May 27, 2026
data-aipythonexpresstestinggitdocumentation

Works with

  • cli

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add Pavel-Kravchenko/Bioinformatics --skill bio-applied-dmr-analysis --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Bio Applied Dmr Analysis?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Bio Applied Dmr Analysis
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/pavel-kravchenko-bio-applied-dmr-analysis/badge)](https://www.skillsdirectory.com/skills/pavel-kravchenko-bio-applied-dmr-analysis)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: bio-applied-dmr-analysis
description: Differentially Methylated Regions (DMRs)
tool_type: python
primary_tool: Python
---

# Differentially Methylated Regions (DMRs)

- [methylKit vignette](https://bioconductor.org/packages/release/bioc/vignettes/methylKit/)
- [DSS (Dispersion Shrinkage for Sequencing)](https://bioconductor.org/packages/release/bioc/html/DSS.html)
- [DMRfinder documentation](https://github.com/jmschrei/dmrfinder)

## Genome-Wide DMR Calling and Multiple Testing

When running BSmooth, DSS, or dmrseq on whole-genome data, the pipeline extends to:

### DSS workflow (R)

```r
library(DSS)
# Create BSseq objects from count data
bs_ctrl  <- makeBSseqData(list(ctrl1, ctrl2, ctrl3),  sampleNames=c("C1","C2","C3"))
bs_treat <- makeBSseqData(list(trt1,  trt2,  trt3),   sampleNames=c("T1","T2","T3"))

# Perform DML (differentially methylated loci) test
dml_test  <- DMLtest(bs_ctrl, bs_treat, smoothing=TRUE, smoothing.span=500)

# Call DMRs from DML results
dmrs <- callDMR(dml_test, p.threshold=0.001, delta=0.1, minlen=50, minCG=3)
```

**Key parameters:**
- `smoothing.span`: bandwidth in bp for local smoothing (200–500 bp is typical)
- `p.threshold`: per-CpG Wald test p-value threshold for DMR seeding
- `delta`: minimum mean methylation difference (avoid calling DMRs with tiny effect sizes)
- `minlen`: minimum DMR length in bp
- `minCG`: minimum number of CpGs within a DMR

### Statistical model in DSS

DSS fits a beta-binomial model at each CpG:

$$Y_i \sim \text{Binomial}(n_i, p_i), \quad \text{logit}(p_i) = \mu + \epsilon_i$$

where $\epsilon_i$ captures biological overdispersion (variance > what pure binomial would predict). This overdispersion parameter is estimated by moment matching across all samples. The Wald statistic tests $H_0: \mu_{\text{ctrl}} = \mu_{\text{treat}}$.

### Multiple testing

For genome-wide DMR calling, individual p-values are not directly interpretable (~25M tests). Instead:
- **Area statistic**: the sum of test statistics across a DMR is used as the primary score (not a p-value)
- **Permutation testing**: condition labels are permuted to generate a null distribution of DMR statistics
- **FDR estimation**: by comparing observed DMR statistics to the permutation null

## DMR Annotation to Genomic Features

Calling DMRs is only the first step. To understand their functional significance, each DMR must be annotated against:

1. **Gene features**: promoters (e.g., TSS ± 2 kb), 5'UTR, exons, introns, 3'UTR, intergenic
2. **CpG island features**: CGI, shore (±2 kb), shelf (±4 kb), open sea (everything else)
3. **Regulatory elements**: ENCODE enhancers, CTCF sites, accessible chromatin (ATAC/DNase peaks)

Tools for annotation:
- **genomation** (R): `annotateWithGeneParts()`, `annotateWithFeatureFlank()`
- **annotatr** (R): flexible annotation using UCSC Genome Browser tracks
- **ChIPseeker** (R): pie charts and heatmaps of genomic feature distribution
- **Python**: `pybedtools` for overlap operations; `pyranges` for fast genomic arithmetic

### Why do hypermethylated DMRs at promoters silence genes?

Promoter methylation at CpG islands blocks transcription factor binding and recruits methyl-CpG-binding domain (MBD) proteins (MBD1, MeCP2), which in turn recruit histone deacetylases (HDACs). This creates a self-reinforcing silencing loop: methylation → deacetylation → compact chromatin → no transcription.

## Integrating DMRs with Gene Expression

One of the most powerful validations for DMR findings is to ask: do hypermethylated promoter DMRs correlate with reduced gene expression in matched RNA-seq data? The classic "methylation–expression anti-correlation" is the hallmark of epigenetic silencing.

### Workflow

1. Identify DMRs overlapping gene promoters (TSS ± 2 kb)
2. Extract the mean methylation change (Δ beta) at the promoter
3. Correlate with the RNA-seq log₂ fold-change for the same gene
4. Expect: negative correlation — genes with hypermethylated promoters are downregulated

### Caveats

- Not all promoter methylation changes affect expression — TF binding and chromatin state matter
- Gene body methylation is actually **positively** correlated with expression (counterintuitive)
- Some genes are silenced by other mechanisms (H3K27me3, mutations) and methylation is secondary
- The correlation weakens at low-methylation promoters (floor effects)

```python
np.random.seed(99)

# Simulate 80 genes with promoter DMRs and matched RNA-seq fold changes
n_genes = 80

# Promoter methylation changes (Δ beta): mix of hyper and hypo
promo_delta = np.concatenate([
    np.random.uniform(0.10, 0.65, 50),    # hypermethylated promoters
    -np.random.uniform(0.05, 0.40, 30),   # hypomethylated
])

# RNA-seq log2FC: correlated with -delta_beta (methylation silences expression)
# Add noise to reflect incomplete correlation
rna_log2fc = -2.5 * promo_delta + np.random.normal(0, 0.6, n_genes)

gene_labels = [f'Gene{i+1}' for i in range(n_genes)]
is_hyper = promo_delta > 0.25    # "strongly silenced" genes

integ_df = pd.DataFrame({
    'gene': gene_labels,
    'promo_delta_beta': promo_delta,
    'rna_log2fc': rna_log2fc,
    'strongly_silenced': is_hyper
})

# Pearson correlation
r, p_val = stats.pearsonr(promo_delta, rna_log2fc)

fig, ax = plt.subplots(figsize=(7, 5))

# Non-highlighted genes
mask_ns = ~is_hyper
ax.scatter(integ_df.loc[mask_ns, 'promo_delta_beta'],
           integ_df.loc[mask_ns, 'rna_log2fc'],
           c='#78909C', alpha=0.6, s=35, label='Other genes')

# Strongly silenced genes
ax.scatter(integ_df.loc[is_hyper, 'promo_delta_beta'],
           integ_df.loc[is_hyper, 'rna_log2fc'],
           c='#E53935', alpha=0.85, s=50, zorder=5,
           label='Hypermethylated (Δβ > 0.25)')

# Regression line
x_range = np.linspace(promo_delta.min(), promo_delta.max(), 100)
slope, intercept, _, _, _ = stats.linregress(promo_delta, rna_log2fc)
ax.plot(x_range, slope * x_range + intercept, 'k--', lw=1.5)

# Reference lines
ax.axhline(0, color='gray', lw=0.8, linestyle=':')
ax.axvline(0, color='gray', lw=0.8, linestyle=':')

ax.set_xlabel('Promoter Δ Beta (methylation change)', fontsize=11)
ax.set_ylabel('RNA-seq log₂ Fold-Change', fontsize=11)
ax.set_title(f'Promoter Methylation vs Gene Expression\nPearson r = {r:.3f}, p = {p_val:.2e}', fontsize=11)
ax.legend(fontsize=9)

plt.tight_layout()
plt.savefig('dmr_expression_integration.png', dpi=120, bbox_inches='tight')
plt.show()

print(f"Pearson r = {r:.3f}  (p = {p_val:.2e})")
print(f"Strongly silenced genes (Δβ > 0.25 AND log2FC < -1): "
      f"{((is_hyper) & (rna_log2fc < -1)).sum()}")
```

## Methylation Heatmap Across Samples

A common visualization in DMR papers is a **heatmap of beta values** at the top DMRs across all samples. This shows: (a) which samples cluster together, (b) whether the methylation difference is consistent across replicates, and (c) which DMRs have the most distinct patterns.

```python
np.random.seed(5)

# Build a beta matrix: 50 top DMRs  6 samples (3 ctrl, 3 treat)
n_dmrs_heat = 50
n_samples   = 6
sample_names = [f'Ctrl_{i+1}' for i in range(3)] + [f'Treat_{i+1}' for i in range(3)]

# DMR base methylation
dmr_base = np.random.beta(2, 2, n_dmrs_heat)  # random baseline

# Build matrix: control gets baseline treatment gets shifted betas
ctrl_matrix  = dmr_base[:, None] + np.random.normal(0, 0.05, (n_dmrs_heat, 3))
delta_each   = np.random.uniform(-0.6, 0.6, n_dmrs_heat)   # per-DMR delta
treat_matrix = dmr_base[:, None] + delta_each[:, None] + np.random.normal(0, 0.05, (n_dmrs_heat, 3))
beta_matrix  = np.clip(np.hstack([ctrl_matrix, treat_matrix]), 0, 1)

# Sort DMRs by delta (hyper at top, hypo at bottom)
order = np.argsort(-delta_each)
beta_matrix_sorted = beta_matrix[order, :]

fig, ax = plt.subplots(figsize=(8, 10))
im = ax.imshow(beta_matrix_sorted, aspect='auto', cmap='RdYlBu_r',
               vmin=0, vmax=1, interpolation='nearest')

ax.set_xticks(range(n_samples))
ax.set_xticklabels(sample_names, rotation=45, ha='right', fontsize=9)
ax.set_yticks(range(0, n_dmrs_heat, 10))
ax.set_yticklabels([f'DMR {i+1}' for i in range(0, n_dmrs_heat, 10)], fontsize=8)
ax.set_title('Methylation Heatmap — Top 50 DMRs\n(sorted by Δ beta, hyper→hypo)', fontsize=11)

cbar = plt.colorbar(im, ax=ax, shrink=0.6)
cbar.set_label('Beta value', fontsize=9)

# Vertical separator between ctrl and treat
ax.axvline(2.5, color='white', lw=2.5)
ax.text(0.75, -2.5, 'Control', ha='center', va='top', fontsize=9,
        transform=ax.transData, color='#1565C0', fontweight='bold')
ax.text(3.75, -2.5, 'Treatment', ha='center', va='top', fontsize=9,
        transform=ax.transData, color='#B71C1C', fontweight='bold')

plt.tight_layout()
plt.savefig('dmr_heatmap.png', dpi=120, bbox_inches='tight')
plt.show()
```

## Summary and Key Takeaways

1. **Why DMRs over DMPs**: Neighbouring CpGs are correlated; regional testing increases power and biological interpretability. The multiple-testing burden (~25M CpGs) makes genome-wide single-CpG FDR correction very stringent.

2. **Smoothing**: BSmooth and DSS use local averaging to reduce sampling noise at individual CpGs by borrowing information from neighbours. This is especially important at 10–30× coverage.

3. **DMR calling**: Runs of CpGs exceeding a t-statistic threshold (BSmooth) or Wald test threshold (DSS) with ≥3–5 CpGs and ≥50 bp are called as DMRs. The sum of test statistics (area statistic) is more powerful than the peak statistic.

4. **Annotation**: DMRs are annotated against CGI features, gene features, and regulatory elements. Hypermethylated promoter CGI DMRs are the most functionally interpretable (silencing of tumor suppressors in cancer).

5. **Expression integration**: Promoter Δ beta correlates negatively with RNA-seq log₂FC. This anti-correlation is the key evidence that methylation is functionally silencing gene expression rather than being a bystander mark.

**Next**: Notebook 3 covers **epigenetic clocks** — how methylation at specific CpG sites predicts biological age.

## Pitfalls

- **Coordinate systems**: BED uses 0-based half-open; VCF/GFF use 1-based inclusive — mixing them causes off-by-one errors
- **Batch effects**: Always check for batch confounding before interpreting biological signal
- **Multiple testing**: Apply FDR correction (Benjamini-Hochberg) when testing thousands of features simultaneously

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…