HGVS Easy
hgvs easy
Core Principle: The
hgvs.easymodule provides initialized parser, validator, and mapper singletons connected to public remote UTA endpoints for exploratory analysis and interactive prototyping.
The fastest possible start
from hgvs.easy import *
var_c = parse("NM_000546.6:c.215C>G")
var_g = am38.c_to_g(var_c)
var_p = am38.c_to_p(var_c)
print(var_g) # NC_000017.11:g.7676154G>C
print(var_p) # NP_000537.3:p.(Pro72Arg)
Three lines from a string to genomic and protein coordinates, with no database setup. That is genuinely useful for learning and for one-off lookups.
What you get
This code completed without producing output.
| Name | What |
|---|---|
parser |
a Parser instance |
hdp |
a UTA data provider, connected to the public remote instance |
am37 |
AssemblyMapper for GRCh37 |
am38 |
AssemblyMapper for GRCh38 |
hn |
a Normalizer |
hv |
a Validator |
parse, normalize, validate, g_to_t, t_to_p, ... |
module-level function wrappers |
The shell
hgvs-shell
Drops you into an interactive Python session with from hgvs.easy import * already executed. Genuinely the fastest way to explore the library or check a single variant.
>>> var = parse("NM_000546.6:c.215C>G")
>>> am38.relevant_transcripts(am38.c_to_g(var))
>>> normalize(var)
>>> validate(var)
Common one-liners
True
That GRCh37/GRCh38 pair is worth running once yourself. The same variant, two assemblies, ~97,000 bp apart in coordinates. It makes concrete why a genomic position without an assembly is meaningless.
Configuration
export UTA_DB_URL=postgresql://anonymous@localhost:5432/uta/uta_20210129
export HGVS_SEQREPO_DIR=/usr/local/share/seqrepo/2024-02-20
hgvs.easy honours these, so you can point it at local data sources and keep the convenient API. That combination: easy's brevity with local data: is a reasonable setup for interactive work.
When not to use it
hgvs.easy connects to the public remote UTA at import time. That has consequences:
- Importing is slow: it opens a network database connection.
- Every operation is a network round-trip unless you have set the environment variables.
- The remote service is rate-limited and shared. Batch work through it is slow for you and antisocial to everyone else.
from hgvs.easy import *dumps a dozen names into your namespace, includingparseandvalidate, which collide with common names.- The data version is whatever the remote happens to be, so results are not reproducible.
The explicit form for pipelines
import hgvs.parser
import hgvs.dataproviders.uta
import hgvs.assemblymapper
import hgvs.normalizer
import hgvs.validator
hp = hgvs.parser.Parser()
hdp = hgvs.dataproviders.uta.connect(pooling=True)
am = hgvs.assemblymapper.AssemblyMapper(
hdp,
assembly_name="GRCh38",
alt_aln_method="splign",
replace_reference=True,
normalize=True,
)
hn = hgvs.normalizer.Normalizer(hdp)
hv = hgvs.validator.Validator(hdp)
print(f"UTA: {hdp.data_version()}") # ← record this
More lines, but you get: explicit configuration, a recorded data version, connection pooling, no namespace pollution, and control over alt_aln_method and replace_reference.
Build these objects once, at module level or in a factory, and pass them down. Constructing AssemblyMapper per variant is the most common performance mistake in hgvs code.
from functools import lru_cache
@lru_cache(maxsize=1)
def get_tools(assembly="GRCh38"):
hdp = hgvs.dataproviders.uta.connect(pooling=True)
return (hgvs.parser.Parser(),
hgvs.assemblymapper.AssemblyMapper(hdp, assembly_name=assembly,
alt_aln_method="splign",
replace_reference=True),
hgvs.normalizer.Normalizer(hdp),
hgvs.validator.Validator(hdp))
The progression
- Learning:
hgvs-shellandhgvs.easy. No setup, immediate feedback. - Prototyping:
hgvs.easywithUTA_DB_URLandHGVS_SEQREPO_DIRpointed at local data. - Production: explicit construction, local data, recorded versions, cached objects, structured error handling.
Do not skip step 1. Understanding what the library does is easier when setup is not in the way.
Common mistakes
- Using
hgvs.easyin a pipeline against the remote instance. from hgvs.easy import *shadowingparse,validate,normalize.- Not recording the data version.
- Reconstructing tools per variant.
- Assuming
am38means your data is GRCh38: it means the mapper targets GRCh38. If your input coordinates are GRCh37, you needam37or a liftover. - Forgetting
hgvs.easyrespects the env vars and setting up local data but never using it.
See also
biocommons hgvs · AssemblyMapper · UTA and Data Providers · SeqRepo · hgvs Parser · Applied - Variant Normalization Pipeline
Exercise
hgvs.easy gives you c_to_p in one call, but the package
is not available in this sandbox, so you will build the codon table yourself
and apply it to a coding sequence supplied on stdin. That is precisely what
am.c_to_p does for a substitution: index the codon, retranslate it, and wrap
the answer in parentheses because it is a prediction, not an observation.
Write predict(text), taking the whole of stdin and returning a list of output
lines. Above a line -- stdin gives cds <sequence>, which begins at the A of
the initiator ATG, and protein <accession>. Below it come c. substitutions.
For each one emit <variant> <protein_accession>:p.(<consequence>) where the
consequence is <Ref><n>= when the amino acid is unchanged, <Ref><n>Ter when
the new codon is a stop, and <Ref><n><Alt> otherwise, using three-letter
codes and numbering residues from 1. Emit <variant> out_of_range when the
position lies outside the coding sequence, <variant> ref_mismatch when the
stated reference base disagrees with the sequence, and <variant> unsupported
for anything that is not a substitution. Finish with predicted=<n> counting
the variants that produced a protein consequence; that line is the only output
when no variants follow the --.
The lesson's worked example is NM_000546.6:c.215C>G on TP53, which predicts
NP_000537.3:p.(Pro72Arg). The transcript in the test data is synthetic so
that you can check the arithmetic without a sequence database, but residue 72
is a proline there too, so the same input gives the same consequence.
Test cases · 4
| # | via | input | expected stdout |
|---|---|---|---|
| 1 | stdin | cds ATGCCTACCTTGAAAGTCCGCTGGGAGCCTACCTTGAAAGTCCGCTGGGAGCCTACCTTGAAAGTCCGCTGGGAGCCTACCTTGAAAGTCCGCTGGGAGCCTACCTTGAAAGTCCGCTGGGAGCCTACCTTGAAAGTCCGCTGGGAGCCTACCTTGAAAGTCCGCTGGGAGCCTACCTTGAAAGTCCGCTGGGAGCCTACCTTGAAAGTCCGCCCCGAGCCTACCTTGAAAGTCCGCTGGCAGCCTACCTTGAAAGTCCGCTGGGAGCCTACCTTGAAAGTCCGCTGGGAGCCTACCTTGTGA protein NP_999999.1 -- NM_999999.1:c.215C>G NM_999999.1:c.216C>T NM_999999.1:c.241C>T NM_999999.1:c.1A>G NM_999999.1:c.215A>G | NM_999999.1:c.215C>G NP_999999.1:p.(Pro72Arg) NM_999999.1:c.216C>T NP_999999.1:p.(Pro72=) NM_999999.1:c.241C>T NP_999999.1:p.(Gln81Ter) NM_999999.1:c.1A>G NP_999999.1:p.(Met1Val) NM_999999.1:c.215A>G ref_mismatch predicted=4 |
| 2 | stdin | cds ATGCCTACCTTGAAAGTCCGCTGGGAGCCTACCTTGAAAGTCCGCTGGGAGCCTACCTTGAAAGTCCGCTGGGAGCCTACCTTGAAAGTCCGCTGGGAGCCTACCTTGAAAGTCCGCTGGGAGCCTACCTTGAAAGTCCGCTGGGAGCCTACCTTGAAAGTCCGCTGGGAGCCTACCTTGAAAGTCCGCTGGGAGCCTACCTTGAAAGTCCGCCCCGAGCCTACCTTGAAAGTCCGCTGGCAGCCTACCTTGAAAGTCCGCTGGGAGCCTACCTTGAAAGTCCGCTGGGAGCCTACCTTGTGA protein NP_999999.1 -- NM_999999.1:c.9999C>G NM_999999.1:c.303A>G NM_999999.1:c.304C>G NM_999999.1:c.215del NM_999999.1:c.-24A>G | NM_999999.1:c.9999C>G out_of_range NM_999999.1:c.303A>G NP_999999.1:p.(Ter101Trp) NM_999999.1:c.304C>G out_of_range NM_999999.1:c.215del unsupported NM_999999.1:c.-24A>G unsupported predicted=1 |
| 3 | stdin | cds ATGCCCTGA protein NP_000001.1 -- NM_000001.1:c.5C>G NM_000001.1:c.6C>T NM_000001.1:c.4C>T | NM_000001.1:c.5C>G NP_000001.1:p.(Pro2Arg) NM_000001.1:c.6C>T NP_000001.1:p.(Pro2=) NM_000001.1:c.4C>T NP_000001.1:p.(Pro2Ser) predicted=3 |
| 4 | stdin | cds ATGCCCTGA protein NP_000001.1 -- | predicted=0 |
Hints · 3
01Hint
Residue number is (pos - 1) // 3 + 1 and the base within the codon is (pos - 1) % 3.
02Hint
Build the table from the 64-character standard code indexed by 16*b1 + 4*b2 + b3 with bases ordered TCAG.
03Hint
Check ref_mismatch against the sequence before you translate, or you will confidently report the wrong reference residue.