agentsclimarketplace

Bio genome intervals proximity operations skills proximity operations

Skill bg-szy/TOP-SKILLS/skills/awesome-skills/bio-genome-intervals-proximity-operations__skills-proximity-operations

Performs proximity operations on genomic intervals with bedtools (closest, window, flank, slop) and pybedtools - nearest-feature queries with signed/strand-aware distance, fixed-radius window searches, strand-aware promoter construction, and interval extension. Covers the closest -d/-D a/b/ref/-t/-k/-io/-iu/-id flags, the -D ref strand sign-flip, silent chromosome-end clipping in slop/flank, -t all tie double-counting, and the critical distinction between a geometry answer (nearest TSS) and a biology answer (which gene an element regulates). Use when assigning peaks or variants to genes, defining promoters from a gene model, building distance-to-TSS distributions, finding features within a window, or extending intervals - and when deciding whether nearest-gene is a fair prior (GWAS locus) or a trap (distal enhancer).From its SKILL.md

Install
npx -y skills add bg-szy/TOP-SKILLS --skill bio-genome-intervals-proximity-operations__skills-proximity-operations

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 5 stars5 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

17.8 KB, ~4.7k tokens by cl100k_base, as published. Nobody here has run it

Version Compatibility

Reference examples tested with: bedtools 2.31+, pybedtools 0.10+.

Before using code patterns, verify installed versions match. If versions differ:

  • CLI: bedtools --version then bedtools <subcommand> --help to confirm flags
  • Python: pip show pybedtools then help(pybedtools.BedTool.closest) to check signatures

flank and slop REQUIRE a chrom-sizes (genome.txt, two columns: chrom<TAB>length) file via -g; closest requires both inputs coordinate-sorted (sort -k1,1 -k2,2n). If code throws an error, introspect the installed tool and adapt rather than retrying.

Proximity Operations

"Which gene is nearest to each peak, and is that the gene it regulates?" -> Compute interval geometry (nearest feature, signed distance, window membership, strand-aware promoters) with bedtools, then decide honestly whether geometry answers the biological question.

  • CLI: bedtools closest -D b -t first -a peaks.bed -b genes.bed, bedtools window -w 50000, bedtools slop -s -l 2000 -r 200 -g genome.txt
  • Python: peaks.closest(genes.sort(), D='b', t='first'), peaks.window(genes, w=50000), tss.slop(g='genome.txt', s=True, l=2000, r=200) (pybedtools)

The Single Most Important Modern Insight -- closest Answers a GEOMETRY Question Misread as a BIOLOGY Question

bedtools closest answers "what is the nearest annotated TSS?" - a coordinate fact. The user almost always wants "which gene does this element regulate?" - a biology claim. For distal regulatory elements these disagree the majority of the time. In the CRISPRi-FlowFISH gold standard (Fulco 2019 Nat Genet 51:1664), assigning each tested distal element to the closest expressed gene gave only ~47% precision and ~37% recall - the nearest gene was the wrong target most of the time, and the method missed nearly two-thirds of real links. Enhancers routinely skip intervening genes: the canonical case is the obesity-associated FTO intron regulating IRX3 ~500 kb away, not FTO (Smemo 2014 Nature 507:371). Do the bedtools arithmetic flawlessly here, then route real enhancer->gene linking to activity/contact/QTL methods (ABC: Fulco 2019, Nasser 2021; PCHi-C; eQTL-coloc) at atac-seq/enhancer-gene-linking - never present "nearest gene" as a regulatory call for a distal element.

The deeper twist - two regimes, opposite advice, identical command:

  • Enhancer -> target (closest is a TRAP). Distal ATAC/H3K27ac peaks, enhancer GWAS variants: nearest gene is wrong most of the time. Use as a candidate generator, validate with ABC/PCHi-C/eQTL.
  • GWAS locus -> gene (closest is a fair PRIOR). For a fine-mapped, colocalized credible-set SNP, the nearest protein-coding gene is right ~50-65% of the time - a strong, hard-to-beat baseline for which gene a locus implicates. Route to causal-genomics for the rigorous version, but nearest-coding-gene is a defensible first pass.

Conflating the two regimes is the real error. The discriminator: is the question the target of an enhancer (distrust nearest) or the gene under a GWAS peak (nearest is a fine first pass)?

Operation Taxonomy

OperationWhat it computesStrand-aware?Needs genome file?
closestFor each A, the nearest B (+ optional signed distance)optional (-s/-S, -D a/-D b)no
windowFor each A, all B within +-W bp (fuzzy intersect)optional (-sw/-sm/-Sm)no
slopGrow each interval by N bp, keeping it one featureoptional (-s)yes (-g)
flankEmit the regions BESIDE each interval, dropping the bodyoptional (-s)yes (-g)

closest/window are queries (A vs B); slop/flank are transforms (A only, + genome file). The slop-vs-flank distinction trips people: slop -b 1000 makes a peak 2 kb wider (one feature); flank -b 1000 returns only the left/right neighboring 1 kb regions and discards the peak itself (two features). window -w 0 is approximately intersect.

Decision Tree by Scenario

ScenarioRecommendedWhy
Nearest gene to a promoter-proximal mark (H3K4me3, Pol II, CAGE)closest -D b -io -t firstthe peak really is at the gene it marks; closest is honest here
Distal enhancer / ATAC peak -> which gene?closest/window as candidates, then -> atac-seq/enhancer-gene-linkingnearest is wrong the majority of the time (ABC/PCHi-C/eQTL link it)
GWAS credible-set SNP -> implicated geneclosest to nearest protein-coding gene, then -> causal-genomics/colocalization-analysisnearest-coding-gene is a ~50-65% prior; a fair first pass
All candidate genes near an elementwindow -w 50000 (or TAD-scale)honest "candidate set", not a single call
Build promoters from a gene modelcollapse to TSS, then slop -s -l UP -r DOWN -ga promoter is an imposed definition, strand-aware, from the TSS
Distance-to-TSS distributionclosest -D b -d then plot signed distancea distribution beats a binary "promoter vs distal" threshold
Upstream-only / downstream-only nearestclosest -D b -iu / -iddirection must be strand-relative (-D b), never -D ref
Peak-set GO enrichment from proximity-> GREAT/rGREAT (regulatory-domain model)avoids the -t all double-counting and distal mis-assignment
Regions flanking a feature (splice/boundary context)flank -s -b N -gthe regions outside the feature, strand-aware
Peaks not yet called-> chip-seq/peak-calling, atac-seq/atac-peak-callingthis skill operates on existing intervals

closest - Nearest Feature with Signed, Strand-Aware Distance

Default: for each A, report the single nearest B; on ties, report ALL tied B (-t all is the default - the double-counting trap below). Both inputs must be sorted. When A's chromosome has no B feature, bedtools prints none for B columns and -1 for distance - filter this sentinel before any numeric summary.

# Nearest gene, signed distance by the GENE's strand, ignore overlaps, one row per peak
bedtools sort -i peaks.bed > peaks.sorted.bed
bedtools sort -i genes.bed  > genes.sorted.bed
bedtools closest -a peaks.sorted.bed -b genes.sorted.bed -D b -io -t first > nearest.bed
#                                                         ^^^^ sign by gene strand (biology, not coordinates)
#                                                              ^^^ closest non-overlapping gene
#                                                                  ^^^^^^^^ resolve ties deterministically (document this)

# k=3 nearest with unsigned distance (k>1 intentionally multiplies rows)
bedtools closest -a peaks.sorted.bed -b genes.sorted.bed -k 3 -d > top3.bed
import pybedtools

peaks = pybedtools.BedTool('peaks.bed').sort()
genes = pybedtools.BedTool('genes.bed').sort()
near = peaks.closest(genes, D='b', io=True, t='first')      # -D b -io -t first
near = near.filter(lambda x: int(x.fields[-1]) != -1)        # drop the no-feature sentinel
near.saveas('nearest.bed')

Key flags: -d unsigned distance (overlaps = 0); -D ref signed by coordinate only (strand-agnostic - see Failure Modes); -D a/-D b signed by A's / B's strand; -t all|first|last; -k N k-nearest; -io ignore overlapping B; -iu/-id ignore upstream/downstream (require -D); -fu/-fd first upstream/downstream; -s/-S same/opposite strand; -N require different names; -mdb each|all and -names/-filenames for multiple -b files.

window - Features Within a Search Radius

window reports all B within a window around each A (default 1000 bp each side). Use it for the honest "candidate genes near this element" framing.

# All genes within 50 kb of each peak, counted per peak
bedtools window -a peaks.bed -b genes.bed -w 50000 -c > peak_gene_counts.bed

Flags: -w N symmetric (default 1000); -l N/-r N asymmetric (coordinate left/right); -sw define -l/-r BY STRAND; -sm/-Sm keep only same/opposite-strand B; -u boolean (A once if any B); -c count of B per A; -v A with no B in window. -sw controls where the window is; -sm/-Sm control which B count - distinct concerns.

slop / flank - Extend or Find Adjacent Regions (genome file REQUIRED)

Both need -g genome.txt precisely so they can clip at chromosome boundaries - extension past coordinate 0 or past chrom length is silently truncated (start floored at 0, end capped). Flags: -b N both sides; -l N/-r N per side (coordinate unless -s); -s strand-aware (on a --strand feature -l adds to the END, so -l always means "upstream of the feature"); -pct treat N as a fraction of feature length; -header echo input header.

Build Strand-Aware Promoters from a Gene Model

Goal: Produce a promoter BED (TSS -2000 / +200 bp, strand-aware) that is correct for both strands - the right way to define "promoter", which is a choice imposed on a TSS, not an annotated feature.

Approach: Collapse genes to their TSS first (start for +, end-1 for -), THEN slop -s so "upstream" tracks strand. Running slop -b 2000 on a gene BODY is the wrong promoter (it grows the whole gene, ignores strand).

# 1) TSS BED from a BED6 gene model (strand-aware single base)
awk -v OFS='\t' '{ if ($6=="+") print $1,$2,$2+1,$4,$5,$6; else print $1,$3-1,$3,$4,$5,$6 }' genes.bed > tss.bed

# 2) Promoter = TSS -2000 / +200, strand-aware (-l is always the upstream side under -s)
bedtools slop -i tss.bed -g genome.txt -s -l 2000 -r 200 > promoters.bed
import pybedtools

UP = 2000   # bp upstream of TSS; common core-promoter convention, NOT a fact -- report it and tune per assay
DOWN = 200  # bp downstream of TSS; asymmetric on purpose (+1 nucleosome / 5'UTR sit downstream)

genes = pybedtools.BedTool('genes.bed')
tss = genes.each(lambda f: pybedtools.create_interval_from_list([f[0], str(f.start) if f.strand == '+' else str(f.end - 1), str(f.start + 1) if f.strand == '+' else str(f.end), f.name, f.score, f.strand])).saveas()
promoters = tss.slop(g='genome.txt', s=True, l=UP, r=DOWN).saveas('promoters.bed')

flank shares the flag vocabulary but emits the regions BESIDE each feature and drops the original (two intervals per input, used for splice/boundary context):

bedtools flank -i exons.bed -g genome.txt -s -b 1000 > exon_flanks.bed   # 1 kb each side, strand-aware

Per-Method Failure Modes

-D ref silently mis-signs minus-strand genes

Trigger: using closest -D ref and interpreting the sign as upstream/downstream. Mechanism: -D ref signs by genomic coordinate only (lower = negative); for a --strand gene the TSS is at the HIGHER coordinate, so "upstream" runs to higher coordinates and the coordinate sign is inverted relative to biology. Symptom: half the genes (the --strand ones) are folded the wrong way; symmetric QC (TSS-enrichment plot) still looks fine, but any "enhancers preferentially upstream" claim washes out or inverts. Fix: use -D b (sign by the gene's strand) for any upstream/downstream biology; reserve -D ref for pure left/right genomic distance.

slop on a gene body is not a promoter

Trigger: slop -b 2000 (or -l 2000 -r 0 without -s) on a gene-body BED, called "the promoter". Mechanism: it grows the window around the whole gene, not the TSS, and without -s adds the "upstream" side to the wrong (3') end on --strand genes. Symptom: a 100 kb gene becomes a 104 kb "promoter"; every --strand promoter is shifted into the gene body. Fix: collapse to TSS first, then slop -s -l UP -r DOWN.

slop/flank clip silently at chromosome ends

Trigger: fixed-width windows near contig starts / telomeres. Mechanism: slop/flank truncate at 0 and chrom length with no warning. Symptom: a TSS 800 bp from a contig start yields a 1000-bp (not 2000-bp) upstream window - quietly asymmetric, biasing per-window normalization (reads/kb, motif density); flank can drop a region entirely, breaking a 2:1 feature->flank assumption. Fix: after slop verify end-start == requested width; after flank verify the per-feature flank count; treat chrom-end features as edge cases.

-t all double-counts ties into inflated enrichment

Trigger: letting default -t all rows flow into a per-gene tally, wc -l peak count, or GO/hypergeometric enrichment. Mechanism: a peak equidistant to two TSSs emits two rows; ties concentrate NON-randomly at bidirectional (head-to-head) promoters and gene-dense regions. Symptom: association counts inflated exactly where biology is most interesting; broken independence inflates significance. Fix: -t first (deterministic but arbitrary - document it) OR -t all then aggregate counting distinct PEAKS not rows; for enrichment prefer GREAT/rGREAT, whose regulatory-domain model exists to avoid this artifact.

closest on unsorted input

Trigger: closest on a BED that was filtered/edited and not re-sorted. Mechanism: closest assumes coordinate-sorted input. Symptom: wrong nearest feature or an error. Fix: bedtools sort (or .sort() in pybedtools) both A and B first.

Quantitative Thresholds

ThresholdSourceRationale
Promoter TSS -2000 / +200 bp (strand-aware)common conventiona CHOICE, not a fact; asymmetric because core-promoter elements sit upstream and the +1 nucleosome / 5'UTR downstream. Report it; "% promoter-proximal" is sensitive to it
GREAT basal domain 5 kb up / 1 kb down, extension <=1 MbMcLean 2010 Nat Biotechnol 28:495the principled "proximity++": asymmetric basal domain + extension to the neighbor, a far better proximity heuristic than raw closest
ChIPseeker default promoter +-3 kbtool defaultshows the convention spans an order of magnitude (+-500 bp to +-10 kb across tools)
ABC candidate window 5 MbFulco 2019activity-by-contact scores all elements within 5 Mb of a gene's promoter - "distal" is tens of kb to megabases
Nearest gene precision/recall ~47% / ~37% (enhancers)Fulco 2019 CRISPRi-FlowFISHthe empirical ceiling on nearest-gene for distal-enhancer targeting
Nearest protein-coding gene ~50-65% right (GWAS loci)fine-mapping/coloc literaturethe GWAS-regime baseline; strong, hard to beat, but imperfect
Distal flagdist> ~50-100 kb

Common Errors

Error / symptomCauseSolution
Nearest-gene call wrong for an enhancergeometry != regulation for distal elementstreat as candidate; route to atac-seq/enhancer-gene-linking (ABC/PCHi-C/eQTL)
Upstream/downstream asymmetry washes out or inverts-D ref mis-signs --strand genesuse -D b
Promoter window includes the whole geneslop -b on a gene body, not the TSScollapse to TSS, then slop -s -l UP -r DOWN
Per-gene counts inflated near bidirectional promoters-t all rows counted as peaks-t first or aggregate by distinct peak; or use GREAT
Asymmetric "fixed-width" windows near contig endssilent slop/flank clippingverify end-start; treat chrom-end features as edge cases
none / -1 rows poison a mean distanceno B feature on that chromosomefilter the -1 sentinel before summarizing
Wrong nearest feature, or closest errorsunsorted inputbedtools sort both A and B
Empty outputchr1 vs 1 naming mismatch between A, B, genome fileharmonize chromosome naming across all files

References

  • Quinlan AR, Hall IM. 2010. BEDTools: a flexible suite of utilities for comparing genomic features. Bioinformatics 26:841-842.
  • Dale RK, Pedersen BS, Quinlan AR. 2011. Pybedtools: a flexible Python library for manipulating genomic datasets and annotations. Bioinformatics 27:3423-3424.
  • Fulco CP, Nasser J, Jones TR, et al. 2019. Activity-by-contact model of enhancer-promoter regulation from thousands of CRISPR perturbations. Nat Genet 51:1664-1669.
  • Nasser J, Bergman DT, Fulco CP, et al. 2021. Genome-wide enhancer maps link risk variants to disease genes. Nature 593:238-243.
  • Smemo S, Tena JJ, Kim KH, et al. 2014. Obesity-associated variants within FTO form long-range functional connections with IRX3. Nature 507:371-375.
  • McLean CY, Bristor D, Hiller M, et al. 2010. GREAT improves functional interpretation of cis-regulatory regions. Nat Biotechnol 28:495-501.

Related Skills

  • bed-file-basics - BED coordinate systems and the sort/conversion this skill depends on
  • gtf-gff-handling - Extract TSS and gene models from GTF/GFF for promoter construction
  • interval-arithmetic - intersect/merge/subtract; window -w 0 is approximately intersect
  • chip-seq/peak-annotation - Assigns peaks to genes via the same closest-TSS logic and caveats
  • atac-seq/enhancer-gene-linking - The real enhancer->gene science (ABC, contact, peak-gene correlation) this skill routes distal calls to
  • atac-seq/footprinting - Uses strand-aware windows over motif/TSS sites
  • data-visualization/genome-tracks - Render the promoter/proximity intervals built here

What ships with it: 1 file

5.9 KB alongside SKILL.md

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.