Skill Tracer
Skills › Scientific Agent Skills › genomic-coordinates › transcript-coordinates.md

transcript-coordinates.md

169 lines · skills/genomic-coordinates/references/transcript-coordinates.md at 5f2bc03

Open on GitHub
1# Transcript, CDS, and protein coordinates
2
3Four coordinate spaces describe the same locus, and a position number is
4meaningless without saying which one it is in.
5
6| Space | Prefix | Origin | Counts |
7| --- | --- | --- | --- |
8| Genomic | `g.` | contig base 1 | every base, introns included |
9| Transcript | `n.` | transcript base 1 | spliced bases, UTRs included |
10| Coding | `c.` | the `A` of the initiator `ATG` | spliced coding bases |
11| Protein | `p.` | initiator methionine | residues |
13"Position 250" in a paper, a spreadsheet column, or a variant list is ambiguous
14between all four, and the four differ by hundreds of bases.

Transcript, CDS, and protein coordinates

Four coordinate spaces describe the same locus, and a position number is meaningless without saying which one it is in.

SpacePrefixOriginCounts
Genomicg.contig base 1every base, introns included
Transcriptn.transcript base 1spliced bases, UTRs included
Codingc.the A of the initiator ATGspliced coding bases
Proteinp.initiator methionineresidues

"Position 250" in a paper, a spreadsheet column, or a variant list is ambiguous between all four, and the four differ by hundreds of bases.

Raw lines 1 to 15
16## Genomic to transcript
18The transcript is the concatenation of its exons in **transcription order**.
19Introns are not numbered. On the minus strand, transcription order is decreasing
20genomic coordinate, and the transcript sequence is the reverse complement.
22Synthetic worked example of a two-exon minus-strand transcript (not a real
23GRCh38 annotation):
25```
26exon 2: chr1:1,000-1,099 (100 bp) transcribed second
27exon 1: chr1:2,000-2,199 (200 bp) transcribed first
28```
30Transcript position 1 is genomic 2,199 — the *highest* coordinate. Positions
311–200 walk down exon 1 to genomic 2,000; position 201 jumps to genomic 1,099;
32positions 201–300 walk down exon 2 to genomic 1,000.
34Converting a genomic position to a transcript position:
361. Confirm the position falls inside an exon. If it does not, it is intronic and
37 has no plain transcript coordinate — see the intronic notation below.
382. Sum the lengths of all exons before it in transcription order.
393. Add its offset within its own exon, counted in transcription order:
40 `pos - exon_start + 1` on the plus strand, `exon_end - pos + 1` on the minus.
42Getting step 3's strand handling wrong is the single most common error here, and
43it fails silently: the number produced is a valid transcript coordinate, just the
44wrong one, mirrored within the exon.

Genomic to transcript

The transcript is the concatenation of its exons in transcription order. Introns are not numbered. On the minus strand, transcription order is decreasing genomic coordinate, and the transcript sequence is the reverse complement.

Synthetic worked example of a two-exon minus-strand transcript (not a real GRCh38 annotation):

exon 2:  chr1:1,000-1,099   (100 bp)   transcribed second
exon 1:  chr1:2,000-2,199   (200 bp)   transcribed first

Transcript position 1 is genomic 2,199 — the highest coordinate. Positions 1–200 walk down exon 1 to genomic 2,000; position 201 jumps to genomic 1,099; positions 201–300 walk down exon 2 to genomic 1,000.

Converting a genomic position to a transcript position:

  1. Confirm the position falls inside an exon. If it does not, it is intronic and has no plain transcript coordinate — see the intronic notation below.
  2. Sum the lengths of all exons before it in transcription order.
  3. Add its offset within its own exon, counted in transcription order: pos - exon_start + 1 on the plus strand, exon_end - pos + 1 on the minus.

Getting step 3's strand handling wrong is the single most common error here, and it fails silently: the number produced is a valid transcript coordinate, just the wrong one, mirrored within the exon.

Raw lines 16 to 45
46## Transcript to coding
48`c.1` is the first base of the initiator codon, not the first base of the
49transcript. If the 5' UTR is 150 bases long, transcript position 151 is `c.1`.
51HGVS coding numbering has no zero and uses four distinct forms:
53| Region | Notation | Example |
54| --- | --- | --- |
55| 5' UTR | negative, counting back from `c.1` | `c.-15` |
56| CDS | positive | `c.742` |
57| 3' UTR | `*`, counting from the base after the stop codon | `c.*23` |
58| Intron | nearest exonic base, then offset | `c.742+3`, `c.743-12` |
60Intronic offsets are relative to the nearest exon boundary: `+` counts forward
61from the last base of the preceding exon, `-` counts back from the first base of
62the following exon. Bases in the 5' half of an intron take the `+` form, those in
63the 3' half take the `-` form. `c.742+1` and `c.742+2` are the donor
64dinucleotide; `c.743-2` and `c.743-1` are the acceptor positions. For an odd-length intron,
65the midpoint uses the upstream `+` form. An intronic HGVS description needs a
66genomic reference with the versioned transcript model; a spliced transcript
67accession alone does not contain those intronic bases.
69There is no `c.0`. A tool that emits one has an off-by-one at the UTR boundary.

Transcript to coding

c.1 is the first base of the initiator codon, not the first base of the transcript. If the 5' UTR is 150 bases long, transcript position 151 is c.1.

HGVS coding numbering has no zero and uses four distinct forms:

RegionNotationExample
5' UTRnegative, counting back from c.1c.-15
CDSpositivec.742
3' UTR*, counting from the base after the stop codonc.*23
Intronnearest exonic base, then offsetc.742+3, c.743-12

Intronic offsets are relative to the nearest exon boundary: + counts forward from the last base of the preceding exon, - counts back from the first base of the following exon. Bases in the 5' half of an intron take the + form, those in the 3' half take the - form. c.742+1 and c.742+2 are the donor dinucleotide; c.743-2 and c.743-1 are the acceptor positions. For an odd-length intron, the midpoint uses the upstream + form. An intronic HGVS description needs a genomic reference with the versioned transcript model; a spliced transcript accession alone does not contain those intronic bases.

There is no c.0. A tool that emits one has an off-by-one at the UTR boundary.

Raw lines 46 to 70
71## Coding to protein
73```
74codon = (c_pos - 1) // 3 + 1
75in_codon = (c_pos - 1) % 3 + 1 # 1, 2 or 3
76```
78`p.1` is the initiator methionine. `c.1`, `c.2` and `c.3` all map to `p.1`, so
79protein coordinates lose information — three different nucleotide variants share
80one protein position, and two of them may be synonymous.
82This formula maps a positive coding-base position to a codon index. Predicting
83a protein change additionally needs alleles, transcript sequence, phase, splice
84effects and the genetic code; it is not an arithmetic conversion of a variant. A protein
85position corresponds to three nucleotide positions, and a protein *change*
86usually corresponds to several possible nucleotide changes. Back-translating a
87`p.` description into a genomic coordinate requires the transcript sequence and
88still may be ambiguous. Never do it arithmetically.

Coding to protein

codon      = (c_pos - 1) // 3 + 1
in_codon   = (c_pos - 1) %  3 + 1     # 1, 2 or 3

p.1 is the initiator methionine. c.1, c.2 and c.3 all map to p.1, so protein coordinates lose information — three different nucleotide variants share one protein position, and two of them may be synonymous.

This formula maps a positive coding-base position to a codon index. Predicting a protein change additionally needs alleles, transcript sequence, phase, splice effects and the genetic code; it is not an arithmetic conversion of a variant. A protein position corresponds to three nucleotide positions, and a protein change usually corresponds to several possible nucleotide changes. Back-translating a p. description into a genomic coordinate requires the transcript sequence and still may be ambiguous. Never do it arithmetically.

Raw lines 71 to 89
90## Phase, and why it is not frame
92GFF3 column 8 (`phase`, called `frame` in GTF) locates the next complete codon
93from the transcriptional start of this CDS segment. Do not remove phase bases
94from each exon before concatenation: they can complete the preceding codon.
95It takes the values 0, 1, and 2.
97It is not `start % 3`, and it is not a property of the genomic position. It is
98determined by how many coding bases precede this feature in the transcript:
100```
101phase = (3 - (coding_bases_before_this_CDS % 3)) % 3
102```
104A complete CDS beginning at its initiation codon starts at phase 0. Partial
105CDSs can start with nonzero phase; special translation annotations can break
106the simple cumulative-length rule. On the minus strand, "start of
107the feature" means the end with the **higher** genomic coordinate, because that is
108where translation reaches first.
110Concatenating CDS features in genomic order and translating produces protein for
111plus-strand genes and nonsense for minus-strand genes. On the minus strand, either reverse-complement the concatenated genomic-order
112segments once, or reverse-complement each segment and join in transcription
113order. Doing both reversals of segment order gives the wrong sequence.
115Check whether the annotation includes the stop codon in CDS rows. GENCODE GTF
116uses separate stop_codon features; blindly treating the CDS end as the end of
117the HGVS coding reference can move the c.* boundary by three bases.

Phase, and why it is not frame

GFF3 column 8 (phase, called frame in GTF) locates the next complete codon from the transcriptional start of this CDS segment. Do not remove phase bases from each exon before concatenation: they can complete the preceding codon. It takes the values 0, 1, and 2.

It is not start % 3, and it is not a property of the genomic position. It is determined by how many coding bases precede this feature in the transcript:

phase = (3 - (coding_bases_before_this_CDS % 3)) % 3

A complete CDS beginning at its initiation codon starts at phase 0. Partial CDSs can start with nonzero phase; special translation annotations can break the simple cumulative-length rule. On the minus strand, "start of the feature" means the end with the higher genomic coordinate, because that is where translation reaches first.

Concatenating CDS features in genomic order and translating produces protein for plus-strand genes and nonsense for minus-strand genes. On the minus strand, either reverse-complement the concatenated genomic-order segments once, or reverse-complement each segment and join in transcription order. Doing both reversals of segment order gives the wrong sequence.

Check whether the annotation includes the stop codon in CDS rows. GENCODE GTF uses separate stop_codon features; blindly treating the CDS end as the end of the HGVS coding reference can move the c.* boundary by three bases.

Raw lines 90 to 118
119## Which transcript
121A gene has many transcripts and the same variant gets a different `c.` and `p.`
122in each. A `c.` description without a versioned transcript accession is not
123actionable.
125| Source | Default choice |
126| --- | --- |
127| MANE Select | one transcript per protein-coding gene, identical in RefSeq and Ensembl |
128| Ensembl canonical | MANE Select where one exists, otherwise Ensembl's own rule |
129| RefSeq Select | one per gene, not always the same as Ensembl canonical |
130| UCSC canonical | track-dependent; inspect the actual selection and release |
131| VEP output | transcript consequences depend on options and format; VCF CSQ can contain several in one row |
133MANE Select provides a matched RefSeq/Ensembl transcript pair for supported
134human genes. It is a useful reporting baseline, not proof that other isoforms
135are irrelevant; MANE Plus Clinical adds transcripts needed for some clinically
136relevant variants. Preserve the transcript used by the original evidence.
138The version suffix matters. `ENST00000269305.9` and `ENST00000269305.8` can differ
139in exon boundaries, UTR or CDS sequence. Which coordinates change depends on
140where the annotation differs; UTR length alone does not shift every c. number. Record the version; a bare `ENST00000269305` is under-specified.

Which transcript

A gene has many transcripts and the same variant gets a different c. and p. in each. A c. description without a versioned transcript accession is not actionable.

SourceDefault choice
MANE Selectone transcript per protein-coding gene, identical in RefSeq and Ensembl
Ensembl canonicalMANE Select where one exists, otherwise Ensembl's own rule
RefSeq Selectone per gene, not always the same as Ensembl canonical
UCSC canonicaltrack-dependent; inspect the actual selection and release
VEP outputtranscript consequences depend on options and format; VCF CSQ can contain several in one row

MANE Select provides a matched RefSeq/Ensembl transcript pair for supported human genes. It is a useful reporting baseline, not proof that other isoforms are irrelevant; MANE Plus Clinical adds transcripts needed for some clinically relevant variants. Preserve the transcript used by the original evidence.

The version suffix matters. ENST00000269305.9 and ENST00000269305.8 can differ in exon boundaries, UTR or CDS sequence. Which coordinates change depends on where the annotation differs; UTR length alone does not shift every c. number. Record the version; a bare ENST00000269305 is under-specified.

Raw lines 119 to 141
142## Two traps at boundaries
144**Exon edges.** A variant at the last base of an exon is exonic in one transcript
145and intronic in another whose exon is two bases shorter. Its consequence changes
146from missense to splice-region accordingly. This is a real disagreement between
147annotation sources, not a bug in either.
149**Indels near boundaries.** HGVS shifts indels 3'-most along the *transcript*;
150VCF left-aligns along the *genome*. On a minus-strand transcript, 3' shifting runs toward decreasing genomic
151coordinates and can agree with VCF left-alignment. On the plus strand the
152directions oppose. Exon-junction exceptions constrain HGVS shifting, so do not
153move an event across a splice boundary by repeat arithmetic alone. See `variant-representation.md`.
155Both are reasons to convert with a tool that holds the transcript model — VEP,
156Mutalyzer, or the `hgvs` Python package — rather than by
157arithmetic on exon coordinates.

Two traps at boundaries

Exon edges. A variant at the last base of an exon is exonic in one transcript and intronic in another whose exon is two bases shorter. Its consequence changes from missense to splice-region accordingly. This is a real disagreement between annotation sources, not a bug in either.

Indels near boundaries. HGVS shifts indels 3'-most along the transcript; VCF left-aligns along the genome. On a minus-strand transcript, 3' shifting runs toward decreasing genomic coordinates and can agree with VCF left-alignment. On the plus strand the directions oppose. Exon-junction exceptions constrain HGVS shifting, so do not move an event across a splice boundary by repeat arithmetic alone. See variant-representation.md.

Both are reasons to convert with a tool that holds the transcript model — VEP, Mutalyzer, or the hgvs Python package — rather than by arithmetic on exon coordinates.

Raw lines 142 to 159
160## Official sources reviewed 2026-10-01
162- [HGVS numbering](https://hgvs-nomenclature.org/stable/background/numbering/) and
163 [general recommendations](https://hgvs-nomenclature.org/stable/recommendations/general/).
164- [GENCODE GTF format](https://www.gencodegenes.org/pages/data_format.html).
165- [GFF3 phase](https://github.com/The-Sequence-Ontology/Specifications/blob/master/gff3.md).
166- [NCBI MANE](https://www.ncbi.nlm.nih.gov/refseq/MANE/).
167- [VEP output documentation](https://github.com/Ensembl/ensembl-vep): use the
168 documented output-format and transcript-selection options for the installed
169 release. No clinical transcript conversion was executed for this refresh.

Official sources reviewed 2026-10-01

Raw lines 160 to 169