Back to Claude Scientific Skills

Transcript, CDS, and protein coordinates

skills/genomic-coordinates/references/transcript-coordinates.md

2.57.06.2 KB
Original Source

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.

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.

Worked example, a two-exon minus-strand transcript on GRCh38:

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.

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.

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

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.

Note the asymmetry: c.p. is a function; p.c. is not. 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.

Phase, and why it is not frame

GFF3 column 8 (phase, called frame in GTF) is the number of bases to remove from the start of this CDS feature to reach the first base of the next 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

The first CDS feature of a transcript has phase 0. 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. Sort in transcription order, reverse-complement, then translate.

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 canonicalhistorically the longest CDS; now largely MANE-aligned
VEP default outputevery transcript, one consequence line each

MANE Select is the right default for anything clinical or cross-database, because it is the one choice where the RefSeq and Ensembl transcripts have identical sequence and identical exon coordinates.

The version suffix matters. ENST00000269305.9 and ENST00000269305.8 can differ in UTR length, which shifts every c.- and c.* coordinate even though the CDS is unchanged. Record the version; a bare ENST00000269305 is under-specified.

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. For a minus-strand gene these run in opposite genomic directions, so a deletion can be intronic in its VCF representation and exonic in its HGVS one. See variant-representation.md.

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