| name | bio-applied-promoter |
| description | Detect TATA box/Inr/DPE promoter elements, call CpG islands (O/E ratio), and build/score PWMs for TFBS scanning. Use when finding a TATA box, calling CpG islands, or scanning a sequence for transcription factor binding sites. |
| tool_type | python |
| primary_tool | NumPy |
Core Promoter Elements
When to Use
- Annotating the core promoter architecture (TATA box, Inr, DPE) around a known or predicted TSS
- Classifying a gene's promoter as CpG-island-associated (housekeeping-like) vs CpG-poor (tissue-specific)
- Building a position weight matrix (PWM) from a set of aligned transcription factor binding sites
- Scanning a promoter or genomic region for candidate TFBS hits and picking a score threshold
- Fetching a known TF motif (e.g. CTCF, from JASPAR) instead of building one from scratch
For GRN-level context (linking TFBS hits to target genes, network construction), see bio-applied-regulatory-analysis.
Version Compatibility
Pure Python/NumPy — no version-sensitive APIs. Written against Python ≥3.9, NumPy ≥1.24. Optional: pyjaspar ≥3.0 (JASPAR2024 release) for fetching curated motifs.
Prerequisites
numpy, pandas for numeric/PWM work
pyjaspar if pulling motifs from JASPAR instead of building your own PPM/PWM
- Familiarity with PFM → PPM → PWM (log-odds) pipeline — see
bio-core-sequence-motifs / bio-core-motif-discovery for the general motif-discovery background this specializes
Promoter Architecture
| Element | Position | Notes |
|---|
| TATA box | −25 to −35 | TATAAA consensus; ~10–20% of human genes |
| Initiator (Inr) | +1 | Present in TATA-less promoters |
| DPE | +28 to +32 | Downstream promoter element; compensates for absent TATA |
| CpG island | −200 to +100 | ~70% of human promoters; mark of constitutive expression |
TATA Box Detection
Goal: flag candidate TATA boxes in a TSS-relative promoter sequence.
Approach: regex match on the consensus (strict) or the IUPAC-degenerate TATAWAW pattern (loose), then filter by expected position (−25 to −35 from TSS) to cut false positives.
import re
def find_tata_boxes(sequence, strict=True):
"""Find TATA box matches in a sequence.
strict=True: exact TATAAA consensus.
strict=False: TATAWAW (IUPAC W = A/T), catches degenerate boxes.
Returns list of (0-based start, matched string).
"""
pattern = 'TATAAA' if strict else r'TATA[AT]A[AT]'
return [(m.start(), m.group())
for m in re.finditer(pattern, sequence.upper())]
def filter_by_tss_position(hits, seq_len, tss_offset, window=(-35, -25)):
"""Keep only hits whose position (relative to TSS at tss_offset) falls in window."""
lo, hi = window
return [(pos, seq) for pos, seq in hits
if lo <= (pos - tss_offset) <= hi]
CpG Island Scanner
Goal: identify CpG islands using the classic Gardiner-Garden & Frommer (1987) criteria.
Approach: slide a window across the sequence, compute GC content and observed/expected CpG ratio, and flag windows passing both thresholds (length ≥200 bp, GC ≥50%, O/E ≥0.6).
def cpg_island_scanner(sequence, window=200, step=50, gc_thresh=0.5, oe_thresh=0.6):
"""Scan for CpG islands.
CpG O/E = (N_CpG * window_length) / (N_C * N_G) -- NOT (N_C+N_G)^2/4.
Returns list of (start, end, gc_fraction, oe_ratio) for windows passing
both the GC and O/E thresholds.
"""
sequence = sequence.upper()
islands = []
for i in range(0, len(sequence) - window + 1, step):
win = sequence[i:i + window]
n_c, n_g, n_cpg = win.count('C'), win.count('G'), win.count('CG')
gc = (n_c + n_g) / window
oe = (n_cpg * window) / (n_c * n_g) if n_c > 0 and n_g > 0 else 0.0
if gc >= gc_thresh and oe >= oe_thresh:
islands.append((i, i + window, round(gc, 3), round(oe, 3)))
return islands
PWM Construction and Scanning
Goal: turn a set of aligned binding-site strings into a scoring matrix, then scan a sequence for high-scoring matches.
Approach: count bases per column (PFM) → add a pseudocount and normalize (PPM) → take log2-odds against a background model (PWM). Score every window of the target sequence by summing per-position log-odds.
import numpy as np
def build_pwm(sites, pseudocount=0.5, bg=None):
"""Build a log2-odds PWM directly from aligned binding-site strings.
sites: list of equal-length strings (e.g. known TF binding sites).
bg: background base frequencies, default uniform (0.25 each).
Returns dict {base: [log2-odds per position]}.
"""
bg = bg or {b: 0.25 for b in 'ACGT'}
n, L = len(sites), len(sites[0])
counts = {b: [0] * L for b in 'ACGT'}
for site in sites:
for i, b in enumerate(site.upper()):
counts[b][i] += 1
pwm = {}
for b in 'ACGT':
pwm[b] = [np.log2((counts[b][i] + pseudocount) /
(n + 4 * pseudocount) / bg[b])
for i in range(L)]
return pwm
def scan_pwm(sequence, pwm, threshold=0.0):
"""Slide the PWM across sequence, returning (pos, window, score) for hits >= threshold."""
L = len(pwm['A'])
sequence = sequence.upper()
hits = []
for i in range(len(sequence) - L + ):
window = sequence[i:i + L]
score = (pwm.get(b, [] * L)[j] j, b (window))
score >= threshold:
hits.append((i, window, score))
hits
To use a curated motif instead of building one from scratch, fetch it from JASPAR:
from pyjaspar import jaspardb
jdb = jaspardb(release="JASPAR2024")
motif = jdb.fetch_motif_by_id("MA0139.1")
print(motif.name, motif.counts)
Pitfalls
- CpG O/E formula: denominator is
N_C * N_G, not (N_C + N_G)^2 / 4 — use the Gardiner-Garden & Frommer (1987) formula shown above
- Zero counts in PFM → −∞ PWM: always use pseudocounts (0.5 is standard); this is the most common source of
-inf scores
- TATA box false positives: random AT-rich sequences generate TATAAA by chance; always cross-check the −25 to −35 position window relative to the TSS
- Coordinate systems: BED = 0-based half-open; VCF/GFF = 1-based inclusive — mismatched conventions silently shift every position by one
- Scanner step size:
step=1 gives exact island boundaries; step=50 can miss islands smaller than the step or blur their true edges
See Also
bio-applied-regulatory-analysis — GRN context, linking TFBS hits to target genes
bio-core-motif-discovery — de novo motif discovery (MEME-style EM), significance thresholds
bio-core-sequence-motifs — general PFM/PPM/PWM background and information content
jaspar-database — querying JASPAR for curated TF motifs