HGVS Nomenclature
HGVS Nomenclature
Core Principle: Human Genome Variation Society (HGVS) nomenclature specifies strict, unambiguous syntactic and semantic rules for describing sequence variants across genomic, transcript, and protein reference spaces.
Maintained by the Human Genome Variation Society in partnership with HUGO and the Human Variome Project. It is what clinical reports, ClinVar and LOVD all use.
The anatomy of a variant
NM_000546.6 : c . 215 C > G
─────┬───── ─┬─ ─┬─ ──┬──
│ │ │ └── the edit: what changed
│ │ └─────── the position
│ └─────────── the reference type
└─────────────────── the reference sequence, WITH VERSION
All four parts are required. c.215C>G alone is not a variant: it is meaningless without knowing which transcript. The accession version is part of the identity: NM_000546.5 and NM_000546.6 can number the same base differently.
The reference types
| Prefix | Reference | Numbering starts at |
|---|---|---|
g. |
linear genomic (a chromosome) | position 1 of the sequence |
m. |
mitochondrial | position 1 of the mtDNA |
c. |
coding DNA (a transcript) | the A of the initiator ATG |
n. |
non-coding RNA | position 1 of the transcript |
r. |
RNA | as c., lowercase acgu |
p. |
protein | the initiator methionine |
o. |
circular genomic |
c. is the one clinicians use and the one with the complicated numbering.
Coding DNA (c.) Coordinate Rules
5'UTR CDS 3'UTR
──────────┬─────────────────────────┬───────────
-3 -2 -1│ 1 2 3 ............ n │*1 *2 *3
A T G stop
Three rules:
- There is no position 0. The base before
c.1isc.-1. *marks the 3'UTR. The first base after the stop codon isc.*1.- Intronic positions use offsets from the nearest exonic base:
c.87+3: 3 bases into the intron after coding position 87c.88-3: 3 bases before coding position 88 (i.e. near the 3' end of that intron)c.-24+5: intronic, within the 5'UTRc.*12-8: intronic, within the 3'UTR
The +/- offset is measured from the nearest exon boundary on each side, so a long intron is numbered from both ends and the middle is unreachable by either convention. That is deliberate: introns are numbered relative to the splice sites that matter.
Never do arithmetic on the base number and ignore the offset. c.87+3 and c.90 are unrelated positions. See SequenceVariant.
Edit types
| Type | Example | Meaning |
|---|---|---|
| Substitution | c.215C>G |
one base replaced |
| Deletion | c.76_78del |
bases removed |
| Duplication | c.76_78dup |
copy inserted immediately 3' of the original |
| Insertion | c.76_77insT |
inserted between the two named bases |
| Delins | c.76_78delinsAC |
delete then insert |
| Inversion | c.76_78inv |
reverse-complemented in place |
| Repeat | c.76TG[8] |
a short tandem repeat with a count |
| No change | c.76= |
reference confirmed |
Insertions name the flanking bases, not an insertion point: c.76_77insT means "insert T between 76 and 77". The two positions must be consecutive. This is why insertion coordinates cannot be converted to or from VCF by simple arithmetic.
Duplication vs insertion. If the inserted sequence is a copy of the immediately preceding sequence, HGVS requires dup, not ins. c.76dup and c.76_77insG (where base 76 is G) describe the same event; only the first is correct HGVS.
Protein notation
p.Arg175His three-letter (preferred)
p.R175H one-letter (accepted)
p.(Arg175His) PREDICTED, not observed: note the parentheses
p.Arg175* nonsense (stop gained); p.Arg175Ter is equivalent
p.Arg175fs frameshift
p.Arg175ProfsTer13 frameshift with the new reading frame's stop 13 residues later
p.Met1? the start codon is affected; consequence unknown
p.= protein unchanged (synonymous)
p.0 no protein produced
The parentheses matter. p.(Arg175His) means the protein change was inferred from the DNA, not observed at the protein level. Anything produced by projecting a c. variant is a prediction and should carry them: which is exactly what AssemblyMapper does.
Multiple variants
NM_000546.6:c.[215C>G];[847C>T] two variants in trans (different alleles)
NM_000546.6:c.[215C>G;847C>T] two variants in cis (same allele)
NM_000546.6:c.215C>G(;)847C>T phase unknown
Phase: whether two variants are on the same chromosome copy: is clinically decisive. Two loss-of-function variants in trans in a recessive gene is a diagnosis; the same two in cis is a carrier. The bracket notation encodes it, and most pipelines throw it away.
Common conventions and pitfalls
The 3'-most rule. Where a variant could be described at several positions (an indel in a homopolymer or repeat), HGVS requires the most 3' position on the reference sequence. This is the opposite of VCF's left-alignment convention. See hgvs Normalizer.
Reference-sequence-relative, not strand-relative. "3'-most" means 3' on the reference sequence being used. For a c. variant on a minus-strand gene, that is the opposite genomic direction from a g. variant at the same locus. Getting this backwards is a real and common error.
Legacy notation you will still meet: IVS3+2T>C (intron numbering) and c.215C>G written without an accession. Both are ambiguous and both are still in clinical records. Convert them to proper HGVS before doing anything with them, and record what you assumed.
Worked examples
NC_000017.11:g.7676154G>C genomic, GRCh38, chr17
NM_000546.6:c.215C>G TP53 transcript, coding
NP_000537.3:p.(Pro72Arg) the predicted protein consequence
NM_000546.6:c.-28-2A>G intronic, in the 5'UTR
NM_000546.6:c.*1175A>C 3'UTR
NM_004006.2:c.4375_4379del a DMD deletion
NM_000492.4:c.1521_1523delCTT CFTR F508del at the DNA level
NP_000483.3:p.(Phe508del) the same event at the protein level
NC_012920.1:m.3243A>G MELAS, mitochondrial
Note that CFTR ΔF508 is c.1521_1523delCTT in HGVS but universally called "F508del" clinically: the legacy protein-level name persists. This gap between clinical shorthand and formal nomenclature is why parsing free-text variant names is hard.
Where to check
- Specification: https://hgvs-nomenclature.org/
- Validators: VariantValidator, Mutalyzer: useful for cross-checking your library output
- The library: biocommons hgvs
Common mistakes
- Omitting the accession, or omitting its version.
inswheredupis required.- Forgetting there is no
c.0. - Arithmetic on intronic base numbers, ignoring offsets.
- Assuming left-alignment. HGVS is 3'-most; VCF is leftmost.
- Treating a predicted
p.as observed: the parentheses are meaningful. - Losing phase information.
- Hand-writing HGVS strings rather than generating them with a library. A string you constructed by concatenation is unvalidated.
- Regex-parsing HGVS. It has a formal grammar for a reason. See hgvs Parser.
See also
biocommons hgvs · hgvs Parser · SequenceVariant · hgvs Normalizer · Coordinate Systems · hgvs Pitfalls
Exercise
The hgvs package is not available in this sandbox, so you
will encode the nomenclature rules yourself rather than letting the grammar
reject the string for you. Writing the rules out is what makes them memorable,
and it forces you to notice which of them a parser can check without sequence
data and which it cannot.
Write check_variants(lines), taking the input lines and returning a list of
output lines. Ignore blank lines. For each remaining line emit
<variant> <rule>, where <rule> is the first rule below that the string
breaks, or ok if it breaks none. Check the rules strictly in this order:
missing_accession: there is no:separating a reference sequence from the descriptionmissing_version: the accession is not two letters, an underscore, digits, a dot and a version numberunknown_type: the reference type is not one ofg,m,c,n,r,omalformed: the description is not a position orstart_endrange followed by one ofX>Y,del,dup,ins,inv,delinsor=position_zero: a position numbers zero; there is noc.0interval_reversed: the end of the range precedes the start, ordering-nbeforenbefore*ninsertion_not_flanking: aninsdoes not name two consecutive plain positionssubstitution_no_change: a substitution states the same base twice, which HGVS writes=
Finish with a line ok=<n> invalid=<m> counting the two outcomes; that line is
the only output when the input is empty.
Test cases · 4
| # | via | input | expected stdout |
|---|---|---|---|
| 1 | stdin | NM_000546.6:c.215C>G c.215C>G NM_000546:c.215C>G NM_000546.6:x.215C>G NM_000546.6:c.215Q>G NM_000546.6:c.0C>G NM_000546.6:c.218_215del NM_000546.6:c.76_78insT NM_000546.6:c.215C>C NM_000546.6:c.76_77insT | NM_000546.6:c.215C>G ok c.215C>G missing_accession NM_000546:c.215C>G missing_version NM_000546.6:x.215C>G unknown_type NM_000546.6:c.215Q>G malformed NM_000546.6:c.0C>G position_zero NM_000546.6:c.218_215del interval_reversed NM_000546.6:c.76_78insT insertion_not_flanking NM_000546.6:c.215C>C substitution_no_change NM_000546.6:c.76_77insT ok ok=2 invalid=8 |
| 2 | stdin | NC_000017.11:g.7676154G>C NM_000546.6:c.-28-2A>G NM_000546.6:c.*1175A>C NM_000492.4:c.1521_1523delCTT NC_012920.1:m.3243A>G NM_000546.6:c.76= NM_000546.6:c.*12_-24del NM_000546.6:c.76insG | NC_000017.11:g.7676154G>C ok NM_000546.6:c.-28-2A>G malformed NM_000546.6:c.*1175A>C ok NM_000492.4:c.1521_1523delCTT ok NC_012920.1:m.3243A>G ok NM_000546.6:c.76= ok NM_000546.6:c.*12_-24del interval_reversed NM_000546.6:c.76insG insertion_not_flanking ok=5 invalid=3 |
| 3 | stdin | NM_000546.6:c.215C>G | NM_000546.6:c.215C>G ok ok=1 invalid=0 |
| 4 | stdin | ok=0 invalid=0 |
Hints · 3
01Hint
Split once on ':' with str.partition, then check the accession and the two-character type prefix separately.
02Hint
Sort positions with a key of (1 if it starts with '*' else 0, int(value)) so -24 < 215 < *12 falls out.
03Hint
An insertion is only well-formed when both positions are plain integers and int(end) == int(start) + 1.