{"id":"genomic-coordinates","name":"genomic-coordinates","summary":"座標慣習間でのゲノム区間変換、変異表現の正規化・比較、アセンブリや連写名の不一致を解析が破損する前に検出します。","body":"# Genomic Coordinates\n\n## When to use\n\nAny time a coordinate crosses a boundary: between two file formats, between two\ntools, between two assemblies, or between the genome and a transcript.\n\n## The rule\n\n**A coordinate is three facts, not one: the number, the convention it is written\nin, and the assembly it was measured against.** Carry all three or the number is\nnot interpretable.\n\nCoordinate errors are the quietest class of bug in genomics. An off-by-one BED\nfile parses, sorts, and intersects without complaint. A GRCh37 VCF joined against\na GRCh38 annotation returns rows. A right-shifted indel simply fails to match its\nentry in ClinVar, and the result is a variant reported as novel. Nothing raises\nan error; the answer is just wrong, and it is wrong in a direction that looks\nplausible.\n\nSo: convert with the table, not from memory, and verify against the reference\nwhenever a reference is available.\n\n## The two conversions\n\n```\n1-based inclusive  ->  0-based half-open :  start - 1,  end\n0-based half-open  ->  1-based inclusive :  start + 1,  end\n```\n\nThe end coordinate never moves. If a conversion changed both numbers, it is wrong.\n\n## Which format is which\n\n| 0-based, half-open | 1-based, inclusive |\n| --- | --- |\n| BED, bedGraph, bigWig, narrowPeak | GFF3, GTF, VCF |\n| BAM/CRAM (binary POS) | SAM (text POS) |\n| PSL, genePred, refFlat | WIG, Picard interval_list |\n| MAF (UCSC multiple alignment) | MAF (TCGA mutation annotation) |\n| PyRanges, pybedtools | GRanges/IRanges, samtools & UCSC & Ensembl region strings |\n\nBoth \"MAF\" formats exist, they mean different things, and they disagree. UCSC\nserves 0-based files through a 1-based browser box. `references/format-conventions.md`\nhas the full table with per-format detail.\n\n```bash\ncd skills/genomic-coordinates/scripts\n\npython3 convert_coords.py --list                          # the table\npython3 convert_coords.py --from bed --to gff chr1 999 1000\npython3 convert_coords.py --from ucsc --to bed \"chr7:5,530,601-5,530,625\"\npython3 convert_coords.py --from granges --to pyranges --input regions.tsv\n```\n\n```\ncontig  input                 output           length  status  detail\nchr7    chr7:5530601-5530625  5530600-5530625  25      ok\n```\n\nZero-length BED features (`chromStart == chromEnd`, a legal insertion point) are\nreported as `unrepresentable` rather than converted to `end = start - 1`. Exit\ncode is 1 when any interval is degenerate or invalid.\n\n## Variants are not intervals\n\nA VCF `POS` for an indel is the **anchor base** — the base *before* the event,\nitself unchanged. And the same change can be written many ways:\n`chr1:7:CAC:C`, `chr1:3:CAC:C` and `chr1:2:GCA:G` are one deletion. Joining,\ndeduplicating, or looking up variants before normalising loses real matches\nsilently, and it loses them preferentially in repeats, where indels concentrate.\n\nNormalise — trim to parsimony, then left-align against the reference — before any\ncomparison:\n\n```bash\npython3 normalize_variant.py --fasta ref.fa chr1 7 CAC C\npython3 normalize_variant.py --fasta ref.fa --split --input cohort.vcf\npython3 normalize_variant.py --fasta ref.fa --compare chr1:7:CAC:C chr1:2:GCA:G\n```\n\n```\ninput         normalized    type      pos_shift  ref_check  changed\nchr1:7:CAC:C  chr1:2:GCA:G  deletion  5          ok         yes\n```\n\nEvery record's `REF` is checked against the FASTA first. A `MISMATCH` means the\nvariants and the reference are different assemblies — stop and run\n`check_contigs.py` rather than adjusting coordinates. Multi-allelic records must\nbe split with `--split` **before** normalising, never after.\n\nHGVS shifts indels the opposite way, 3'-most along the transcript. For a\nminus-strand gene that is the opposite genomic direction from VCF's\nleft-alignment. Details and the full procedure: `references/variant-representation.md`.\n\n## Check the assembly before trusting a join\n\n```bash\npython3 check_contigs.py --identify unknown.fa.fai\npython3 check_contigs.py variants.vcf annotation.gtf --genome GRCh38.fa.fai\n```\n\n```\nfile          kind    contigs  naming        assembly  detail\nref.fa.fai    sizes   25       plain         GRCh37    24/24 primary chromosome lengths match;\n                                                       chrM is 16569 bp, i.e. GRCh37/38 (rCRS MT)\n```\n\nThe script reads `.fai`, `.chrom.sizes`, VCF headers, SAM headers, FASTA, BED,\nand GTF/GFF, identifies the assembly from primary-chromosome lengths, and reports\nevery reason a join between two files would go wrong: naming mismatch, length\nconflict, coordinates past a contig end, contigs present in one file only. Exit\ncode 1 on any incompatibility.\n\n**GRCh37 and hg19 differ only in the mitochondrion** — 16,569 bp (rCRS) versus\n16,571 bp. Nuclear coordinates are identical, so a mixed pipeline runs fine and\nonly the mtDNA results are wrong. `check_contigs.py` reports which one it found.\nBuilds, naming schemes, ALT contigs, and liftover pitfalls:\n`references/reference-builds.md`.\n\n## Audit a file against its own format\n\n```bash\npython3 audit_intervals.py peaks.bed\npython3 audit_intervals.py gencode.gtf --genome hg38.chrom.sizes\npython3 audit_intervals.py cohort.vcf --genome GRCh38.fa.fai\n```\n\nLooks for the evidence that a coordinate mistake leaves behind:\n\n| Finding | What it proves |\n| --- | --- |\n| `start_below_one` in GFF/GTF | 0-based data in a 1-based file; everything is one base left |\n| `many_zero_length` in BED | 1-based single-base features written into a 0-based file |\n| `past_contig_end` | wrong assembly, or an off-by-one at the contig edge |\n| `mixed_contig_naming` | any join will silently match one subset |\n| `first_block_offset` | BED12 `blockStarts` written as absolute coordinates |\n| `not_parsimonious` | untrimmed alleles; normalise before joining |\n| `bad_alt_allele` | Ensembl/VEP `-` notation in a VCF, which has no anchor base |\n\nExit code 1 on any fatal finding, so it works as a CI gate on a data directory.\n\n## Transcript, CDS, and protein positions\n\n`c.742` and `chr17:7,674,220` are both \"position\", and neither converts to the\nother by arithmetic. Transcript coordinates count spliced bases in transcription\norder — decreasing genomic coordinate on the minus strand — and `c.1` is the `A`\nof the initiator `ATG`, not the start of the transcript.\n\nThe rules that get mis-remembered: there is no `c.0`; 5' UTR positions are\nnegative and 3' UTR positions take a `*`; GFF phase is the bases to *remove* to\nreach the next codon, not `start % 3`; and a `c.` description is meaningless\nwithout a versioned transcript accession, because the same variant numbers\ndifferently in each transcript. `references/transcript-coordinates.md` has the\nconversion procedure and the boundary cases.\n\nDo the conversion with a tool that holds the transcript model — VEP,\n`bcftools csq`, Mutalyzer, the `hgvs` package — not by hand.\n\n## Reporting results\n\nState the assembly next to the coordinates, every time.\n`chr7:5,530,601-5,530,625` is not a location; `chr7:5,530,601-5,530,625 (GRCh38)`\nis. Say which convention a coordinate column is in, in the column header or the\nfile's documentation. When a conversion produced a result, say which direction it\nwent.\n\n## References\n\n- `references/format-conventions.md` — every format's convention, with per-format\n  detail, BED12 block rules, region-string syntax, and tool behaviour.\n- `references/variant-representation.md` — VCF allele conventions, the\n  normalisation algorithm, equivalence checking, multi-allelic splitting, and how\n  HGVS disagrees with VCF.\n- `references/reference-builds.md` — build signatures, GRCh37 vs hg19, ALT\n  contigs, naming schemes, and liftover failure modes.\n- `references/transcript-coordinates.md` — genomic ↔ transcript ↔ CDS ↔ protein,\n  HGVS numbering, phase, and transcript choice.","author":"@K-Dense-AI","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/K-Dense-AI/scientific-agent-skills/tree/main/skills/genomic-coordinates","license":"MIT","category":"coding","lang":"en","tokens":2033,"stars":0,"calls30d":2,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[{"path":"references/format-conventions.md","size":9511,"sha256":"bc501b01e57a03591e735d2592d38e2815a63a6079a1144b0baa4afc0bc0bba2"},{"path":"references/reference-builds.md","size":7150,"sha256":"27eee0f7050941a3ebf33373d0c84098b6220c6df2fe44e6b926521910c3b8cc"},{"path":"references/transcript-coordinates.md","size":6334,"sha256":"400ee336e6f0c78187b0e77522187b150e8d8a2601539e07eaf4edad0c7f20b8"},{"path":"references/variant-representation.md","size":6274,"sha256":"633f496a63d6a3d432fcf4999f7049ee9947c545087490df96ca35b976042c29"},{"path":"scripts/audit_intervals.py","size":19077,"sha256":"e9e8bf64e0dca3bdccbde6185d0711311c27c081537f24728dfe443fe0ffd750"},{"path":"scripts/check_contigs.py","size":16049,"sha256":"cf1e700572954611c37a6590bed3c9061bbf045551b61b581f3432e45446a386"},{"path":"scripts/_common.py","size":13550,"sha256":"c06e0f36e180849f59ca91346e19d88dfd997d96a65c667a216a01c08854c647"},{"path":"scripts/convert_coords.py","size":6618,"sha256":"6bfd8600d14a7cf0bb22b0524ebe6cd0855157c0ce68fb66eb2408ea96696fb3"},{"path":"scripts/normalize_variant.py","size":9962,"sha256":"6250b8eff0ae9e33bef2a58f90f0dae2c88fe3079d75958bccb1271773e8efcd"}],"requires":{"mcp":[],"tools":["Read Write Edit Bash"]},"safety":{"flags":[],"scannedAt":"2026-08-22","hasScripts":true,"networkEndpoints":[]}}