Install any skill in seconds. Free to start, no credit card required.
Get Started Free →BED file format fundamentals, creation, validation, and basic operations. Covers BED3 through BED12 formats, coordinate systems, sorting, and format conversion using bedtools and pybedtools. Use when working with genomic coordinates or preparing interval files for downstream tools.
.claude/skills/bio-genome-intervals-bed-file-basics/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-09 | ✗→✓ | ▲ Improved | 76% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 82% | 0% |
| case-22 | ✗→✓ | ▲ Improved | 147% | 0% |
| case-23 | ✗→✓ | ▲ Improved | 78% | 0% |
| case-07 | ✓→✓ | = Same ✓ | 562% | 0% |
<!--
#
#
-->
BED (Browser Extensible Data) format stores genomic intervals. Uses 0-based, half-open coordinates.
BED3: chr start end
BED4: chr start end name
BED5: chr start end name score
BED6: chr start end name score strand
BED12: chr start end name score strand thickStart thickEnd rgb blockCount blockSizes blockStartsBED uses 0-based, half-open coordinates:
bash# Create simple BED3 echo -e "chr1\t100\t200\nchr1\t300\t400" > regions.bed # Create BED6 with name and strand echo -e "chr1\t100\t200\tpeak1\t100\t+" > peaks.bed
pythonimport pybedtools # From string bed_string = '''chr1\t100\t200\tpeak1\t100\t+ chr1\t300\t400\tpeak2\t200\t-''' bed = pybedtools.BedTool(bed_string, from_string=True) # From list of tuples intervals = [ ('chr1', 100, 200, 'peak1', 100, '+'), ('chr1', 300, 400, 'peak2', 200, '-'), ] bed = pybedtools.BedTool(intervals) # From pandas DataFrame import pandas as pd df = pd.DataFrame({ 'chrom': ['chr1', 'chr1'], 'start': [100, 300], 'end': [200, 400], 'name': ['peak1', 'peak2'], 'score': [100, 200], 'strand': ['+', '-'] }) bed = pybedtools.BedTool.from_dataframe(df) # Save to file bed.saveas('output.bed')
bash# Sort by chromosome and position sort -k1,1 -k2,2n input.bed > sorted.bed # Using bedtools bedtools sort -i input.bed > sorted.bed # Sort by chromosome, start, then end sort -k1,1 -k2,2n -k3,3n input.bed > sorted.bed
pythonimport pybedtools bed = pybedtools.BedTool('input.bed') sorted_bed = bed.sort() sorted_bed.saveas('sorted.bed')
bash# Check column count awk -F'\t' '{print NF}' input.bed | sort -u # Check for invalid coordinates (start >= end) awk '$2 >= $3' input.bed # Check for negative coordinates awk '$2 < 0 || $3 < 0' input.bed # Validate with bedtools bedtools sort -i input.bed > /dev/null 2>&1 && echo "Valid" || echo "Invalid"
pythonimport pybedtools def validate_bed(filepath): try: bed = pybedtools.BedTool(filepath) for interval in bed: if interval.start < 0 or interval.end < 0: return False, 'Negative coordinates' if interval.start >= interval.end: return False, f'Invalid interval: {interval.start} >= {interval.end}' return True, 'Valid' except Exception as e: return False, str(e) valid, msg = validate_bed('input.bed') print(f'{msg}')
pythonimport pybedtools # Load BED file bed = pybedtools.BedTool('input.bed') # Iterate over intervals for interval in bed: print(f'{interval.chrom}:{interval.start}-{interval.end}') print(f'Name: {interval.name}, Score: {interval.score}, Strand: {interval.strand}') # Count intervals n_intervals = bed.count() # Convert to pandas DataFrame df = bed.to_dataframe() # Access specific columns df = bed.to_dataframe(names=['chrom', 'start', 'end', 'name', 'score', 'strand'])
bash# Single chromosome grep "^chr1\t" input.bed > chr1.bed # Multiple chromosomes grep -E "^(chr1|chr2)\t" input.bed > chr1_2.bed # Exclude chromosome grep -v "^chrM\t" input.bed > no_chrM.bed
bash# Intervals >= 100bp awk '($3 - $2) >= 100' input.bed > large.bed # Intervals between 100-500bp awk '($3 - $2) >= 100 && ($3 - $2) <= 500' input.bed > medium.bed
pythonimport pybedtools bed = pybedtools.BedTool('input.bed') # Filter by chromosome chr1 = bed.filter(lambda x: x.chrom == 'chr1') # Filter by size large = bed.filter(lambda x: len(x) >= 100) # Filter by strand plus_strand = bed.filter(lambda x: x.strand == '+') # Filter by score high_score = bed.filter(lambda x: float(x.score) >= 500) # Chain filters result = bed.filter(lambda x: x.chrom == 'chr1' and len(x) >= 100) result.saveas('filtered.bed')
bash# BED to GFF bedtools bed12togff -i input.bed > output.gff # BED to FASTA (extract sequences) bedtools getfasta -fi reference.fa -bed input.bed -fo output.fa # BED to FASTA with names bedtools getfasta -fi reference.fa -bed input.bed -name -fo output.fa
bash# Extract variant positions bcftools query -f '%CHROM\t%POS0\t%END\n' input.vcf > variants.bed # Or using awk (simpler for SNPs) grep -v "^#" input.vcf | awk '{print $1"\t"$2-1"\t"$2}' > snps.bed
bash# Convert alignments to BED bedtools bamtobed -i input.bam > alignments.bed # BED12 for spliced alignments bedtools bamtobed -i input.bam -split > spliced.bed
pythonimport pybedtools interval = pybedtools.create_interval_from_list(['chr1', '100', '200', 'peak1', '0', '+']) # Access fields print(interval.chrom) # chr1 print(interval.start) # 100 (int) print(interval.end) # 200 (int) print(interval.name) # peak1 print(interval.score) # 0 print(interval.strand) # + # Get length print(len(interval)) # 100 # Get fields list print(interval.fields) # ['chr1', '100', '200', 'peak1', '0', '+']
bash# Fixed-size windows across genome bedtools makewindows -g genome.txt -w 10000 > windows_10kb.bed # Windows with step size (sliding windows) bedtools makewindows -g genome.txt -w 10000 -s 5000 > sliding_10kb.bed # Fixed number of windows per chromosome bedtools makewindows -g genome.txt -n 100 > 100_windows_per_chr.bed # Windows within BED regions bedtools makewindows -b regions.bed -w 1000 > windows_in_regions.bed # Add window ID bedtools makewindows -g genome.txt -w 10000 -i winnum > numbered_windows.bed # Source chromosome in name bedtools makewindows -g genome.txt -w 10000 -i srcwinnum > windows_with_source.bed
pythonimport pybedtools # From genome file windows = pybedtools.BedTool().window_maker(g='genome.txt', w=10000) # Sliding windows windows = pybedtools.BedTool().window_maker(g='genome.txt', w=10000, s=5000) # From BED regions bed = pybedtools.BedTool('regions.bed') windows = pybedtools.BedTool().window_maker(b=bed.fn, w=1000) windows.saveas('windows.bed')
| Format | Description | Columns | |--------|-------------|---------| | narrowPeak | ENCODE narrow peaks | BED6 + signalValue, pValue, qValue, peak | | broadPeak | ENCODE broad peaks | BED6 + signalValue, pValue, qValue | | bedGraph | Signal track | chr, start, end, value | | bedpe | Paired intervals | chr1, s1, e1, chr2, s2, e2, name, score, strand1, strand2 |
pythonimport pybedtools # At end of script pybedtools.cleanup() # Or use context manager (auto-cleanup) pybedtools.set_tempdir('/tmp/pybedtools')
<!-- AUTHOR_SIGNATURE: 9a7f3c2e-MD-BABU-MIA-2026-MSSM-SECURE -->
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-07 | pass→pass | 2,693 | 3,285 | +22% | 1 | 1 | 0% | 497 | 3,288 | +562% | 0 | 0 | — |
case-01 | pass→pass | 10,549 | 3,349 | -68% | 1 | 1 | 0% | 966 | 3,331 | +245% | 0 | 0 | — |
case-06 | pass→pass | 5,047 | 2,365 | -53% | 1 | 1 | 0% | 1,000 | 3,134 | +213% | 0 | 0 | — |
case-02 | pass→pass | 4,679 | 3,579 | -24% | 1 | 1 | 0% | 847 | 3,347 | +295% | 0 | 0 | — |
case-03 | pass→pass | 6,724 | 3,815 | -43% | 1 | 1 | 0% | 1,316 | 3,471 | +164% | 0 | 0 | — |
case-04 | pass→pass | 8,900 | 3,105 | -65% | 1 | 1 | 0% | 1,542 | 3,197 | +107% | 0 | 0 | — |
case-05 | pass→pass | 6,786 | 2,662 | -61% | 1 | 1 | 0% | 1,385 | 3,154 | +128% | 0 | 0 | — |
case-08 | pass→pass | 5,560 | 4,568 | -18% | 1 | 1 | 0% | 1,065 | 3,541 | +232% | 0 | 0 | — |
case-09 | fail→pass | 10,482 | 3,851 | -63% | 1 | 1 | 0% | 1,988 | 3,493 | +76% | 0 | 0 | — |
case-10 | pass→pass | 7,203 | 3,067 | -57% | 1 | 1 | 0% | 1,440 | 3,227 | +124% | 0 | 0 | — |
case-11 | fail→pass | 9,141 | 1,804 | -80% | 1 | 1 | 0% | 1,627 | 2,969 | +82% | 0 | 0 | — |
case-12 | pass→pass | 6,529 | 4,679 | -28% | 1 | 1 | 0% | 1,266 | 3,551 | +180% | 0 | 0 | — |
case-13 | pass→pass | 3,325 | 2,008 | -40% | 1 | 1 | 0% | 641 | 3,014 | +370% | 0 | 0 | — |
case-14 | pass→pass | 11,179 | 2,876 | -74% | 1 | 1 | 0% | 2,112 | 3,240 | +53% | 0 | 0 | — |
case-15 | pass→pass | 5,336 | 1,976 | -63% | 1 | 1 | 0% | 1,055 | 3,026 | +187% | 0 | 0 | — |
case-16 | pass→pass | 5,320 | 3,600 | -32% | 1 | 1 | 0% | 972 | 3,344 | +244% | 0 | 0 | — |
case-17 | pass→pass | 3,314 | 1,873 | -43% | 1 | 1 | 0% | 595 | 2,957 | +397% | 0 | 0 | — |
case-18 | pass→pass | 8,386 | 4,211 | -50% | 1 | 1 | 0% | 1,635 | 3,423 | +109% | 0 | 0 | — |
case-19 | pass→pass | 3,435 | 2,067 | -40% | 1 | 1 | 0% | 553 | 2,957 | +435% | 0 | 0 | — |
case-20 | pass→pass | 5,563 | 5,170 | -7% | 1 | 1 | 0% | 1,104 | 3,683 | +234% | 0 | 0 | — |
case-21 | fail→fail | 8,513 | 7,456 | -12% | 1 | 1 | 0% | 1,602 | 4,083 | +155% | 0 | 0 | — |
case-22 | fail→pass | 9,279 | 8,940 | -4% | 1 | 1 | 0% | 1,818 | 4,486 | +147% | 0 | 0 | — |
case-23 | fail→pass | 12,374 | 8,032 | -35% | 1 | 1 | 0% | 2,437 | 4,326 | +78% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +17 percentage points is the difference between those two pass rates over the 23 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
| Model | Method | Date | Lift |
|---|---|---|---|
| gemini-3.6-flash | verified | 7/26/2026 | — |
Other measured skills in the registry, with their headline benchmark lift.