514 lines
13 KiB
Markdown
514 lines
13 KiB
Markdown
---
|
|
title: "Variant Annotation Guide"
|
|
task: ""
|
|
lineage_type: import
|
|
upstream_source: https://github.com/mims-harvard/ToolUniverse/blob/e2520a96/skills/tooluniverse-variant-analysis/references/annotation_guide.md
|
|
upstream_sha: e2520a96
|
|
imported_at: 2026-06-26
|
|
prompt_class: prompt
|
|
upstream_changes: accepted
|
|
author: upstream
|
|
validated: false
|
|
---
|
|
|
|
# Variant Annotation Guide
|
|
|
|
Complete guide to annotating variants using ToolUniverse annotation tools.
|
|
|
|
## When to Use ToolUniverse Annotation
|
|
|
|
Use ToolUniverse annotation tools when you need:
|
|
1. **Clinical significance**: ClinVar pathogenicity classifications
|
|
2. **Population frequencies**: gnomAD, ExAC, 1000 Genomes allele frequencies
|
|
3. **Pathogenicity scores**: CADD, SIFT, PolyPhen predictions
|
|
4. **Gene/transcript information**: Ensembl gene IDs, transcript IDs, protein changes
|
|
5. **Consequence prediction**: When VCF lacks annotation
|
|
|
|
## Annotation Tools Overview
|
|
|
|
| Tool | Best For | Input Format | Response |
|
|
|------|---------|--------------|----------|
|
|
| `MyVariant_query_variants` | Batch annotation (recommended) | rsID or HGVS | ClinVar, dbSNP, gnomAD, CADD, SIFT, PolyPhen |
|
|
| `MyVariant_get_variant_annotation` | Single variant detail | HGVS | Full annotation object |
|
|
| `dbsnp_get_variant_by_rsid` | Population frequencies | rsID (rs12345) | Allele frequencies, clinical significance |
|
|
| `gnomad_get_variant` | gnomAD-specific data | CHR-POS-REF-ALT | gnomAD variant metadata |
|
|
| `EnsemblVEP_annotate_rsid` | Consequence prediction | rsID | Transcript consequences |
|
|
| `ensembl_vep_region` | Regional annotation | chr:start-end | All variants in region |
|
|
|
|
## Annotation Workflow
|
|
|
|
### Step 1: Parse VCF and Extract rsIDs
|
|
|
|
```python
|
|
from python_implementation import parse_vcf
|
|
|
|
vcf_data = parse_vcf("variants.vcf")
|
|
|
|
# Extract variants with rsIDs (most reliable for annotation)
|
|
rsid_variants = [v for v in vcf_data.variants if v.vid and v.vid.startswith('rs')]
|
|
print(f"Found {len(rsid_variants)} variants with rsIDs")
|
|
```
|
|
|
|
### Step 2: Batch Annotate with MyVariant.info
|
|
|
|
**MyVariant.info is recommended** because it aggregates multiple sources in one call.
|
|
|
|
```python
|
|
from tooluniverse import ToolUniverse
|
|
from python_implementation import batch_annotate_variants
|
|
|
|
# Load ToolUniverse
|
|
tu = ToolUniverse()
|
|
tu.load_tools()
|
|
|
|
# Annotate up to 100 variants
|
|
annotations = batch_annotate_variants(
|
|
tu,
|
|
vcf_data.variants,
|
|
max_annotate=100 # Respect rate limits
|
|
)
|
|
|
|
# Process annotations
|
|
for ann in annotations:
|
|
print(f"{ann.variant_key} - {ann.gene_symbol}")
|
|
print(f" ClinVar: {ann.clinvar_classification}")
|
|
print(f" gnomAD AF: {ann.gnomad_af}")
|
|
print(f" CADD: {ann.cadd_phred}")
|
|
```
|
|
|
|
### Step 3: Query Specific Tools for Additional Data
|
|
|
|
For specific use cases, query tools directly:
|
|
|
|
```python
|
|
# Get detailed population frequencies from dbSNP
|
|
dbsnp_result = tu.execute_tool("dbsnp_get_variant_by_rsid", rsid="rs699")
|
|
|
|
# Get gnomAD-specific data
|
|
gnomad_result = tu.execute_tool("gnomad_get_variant", variant_id="1-55039974-G-A")
|
|
|
|
# Get VEP consequence prediction
|
|
vep_result = tu.execute_tool("EnsemblVEP_annotate_rsid", variant_id="rs699")
|
|
```
|
|
|
|
## MyVariant.info Annotation
|
|
|
|
### Query Variants (Batch)
|
|
|
|
**Best for**: Annotating multiple variants at once
|
|
|
|
```python
|
|
# Query by rsID
|
|
result = tu.execute_tool("MyVariant_query_variants", query="rs699 rs334")
|
|
|
|
# Query by HGVS
|
|
result = tu.execute_tool("MyVariant_query_variants", query="chr7:g.140753336A>T")
|
|
```
|
|
|
|
**Response structure**:
|
|
```json
|
|
{
|
|
"data": {
|
|
"hits": [
|
|
{
|
|
"_id": "chr7:g.140753336A>T",
|
|
"dbsnp": {
|
|
"rsid": "rs113488022",
|
|
"gene": {"symbol": "BRAF"}
|
|
},
|
|
"clinvar": {
|
|
"rcv": {
|
|
"clinical_significance": "Pathogenic",
|
|
"conditions": {"name": "Melanoma, malignant"}
|
|
}
|
|
},
|
|
"gnomad_genome": {
|
|
"af": {"af": 0.000008}
|
|
},
|
|
"cadd": {
|
|
"phred": 34.0
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
### Get Variant Annotation (Single)
|
|
|
|
**Best for**: Detailed information on one variant
|
|
|
|
```python
|
|
result = tu.execute_tool("MyVariant_get_variant_annotation", variant_id="chr7:g.140753336A>T")
|
|
```
|
|
|
|
### Extracted Fields
|
|
|
|
From MyVariant.info response, we extract:
|
|
|
|
| Field | Path | Description |
|
|
|-------|------|-------------|
|
|
| rsID | `dbsnp.rsid` | dbSNP identifier |
|
|
| Gene symbol | `dbsnp.gene.symbol` | Gene name |
|
|
| ClinVar classification | `clinvar.rcv.clinical_significance` | Pathogenicity |
|
|
| ClinVar disease | `clinvar.rcv.conditions.name` | Associated condition |
|
|
| gnomAD allele frequency | `gnomad_genome.af.af` | Population frequency |
|
|
| CADD PHRED | `cadd.phred` | Deleteriousness score |
|
|
| SIFT prediction | `cadd.sift.pred` | Functional prediction |
|
|
| PolyPhen prediction | `cadd.polyphen.pred` | Functional prediction |
|
|
| Protein change | `dbsnp.gene.hgvs_c` | HGVS protein notation |
|
|
|
|
## dbSNP Annotation
|
|
|
|
### Get Variant by rsID
|
|
|
|
**Best for**: Detailed population frequencies
|
|
|
|
```python
|
|
result = tu.execute_tool("dbsnp_get_variant_by_rsid", rsid="rs699")
|
|
```
|
|
|
|
**Response structure**:
|
|
```json
|
|
{
|
|
"status": "success",
|
|
"data": {
|
|
"rsid": "rs699",
|
|
"chromosome": "1",
|
|
"position": 230710048,
|
|
"allele_frequencies": {
|
|
"1000Genomes": {"G": 0.7432, "A": 0.2568},
|
|
"gnomAD_exome": {"G": 0.75, "A": 0.25},
|
|
"TOPMED": {"G": 0.76, "A": 0.24}
|
|
},
|
|
"clinical_significance": ["benign"],
|
|
"gene": "AGT",
|
|
"hgvs": "NC_000001.11:g.230710048G>A"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Extracted Fields
|
|
|
|
| Field | Description |
|
|
|-------|-------------|
|
|
| `allele_frequencies` | Frequencies from 1000G, gnomAD, ExAC, TOPMED |
|
|
| `clinical_significance` | ClinVar classifications |
|
|
| `gene` | Associated gene |
|
|
| `hgvs` | HGVS notation |
|
|
|
|
## gnomAD Annotation
|
|
|
|
### Get Variant
|
|
|
|
**Best for**: gnomAD-specific metadata
|
|
|
|
```python
|
|
result = tu.execute_tool("gnomad_get_variant", variant_id="1-55039974-G-A")
|
|
```
|
|
|
|
**Note**: This tool returns basic metadata only. For allele frequencies, use MyVariant.info instead.
|
|
|
|
**Response structure**:
|
|
```json
|
|
{
|
|
"status": "success",
|
|
"data": {
|
|
"data": {
|
|
"variant": {
|
|
"variant_id": "1-55039974-G-A",
|
|
"chrom": "1",
|
|
"pos": 55039974,
|
|
"ref": "G",
|
|
"alt": "A"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Ensembl VEP Annotation
|
|
|
|
### Annotate by rsID
|
|
|
|
**Best for**: Consequence prediction
|
|
|
|
```python
|
|
result = tu.execute_tool("EnsemblVEP_annotate_rsid", variant_id="rs699")
|
|
```
|
|
|
|
**Response format varies**:
|
|
- May return list of consequences
|
|
- May return `{data, metadata}` wrapper
|
|
- May return `{error}` if variant not found
|
|
|
|
**Handle all cases**:
|
|
```python
|
|
if isinstance(result, dict):
|
|
if 'error' in result:
|
|
print("Variant not found")
|
|
elif 'data' in result:
|
|
consequences = result['data']
|
|
else:
|
|
consequences = result
|
|
```
|
|
|
|
### Annotate by Region
|
|
|
|
**Best for**: Annotating all variants in a genomic region
|
|
|
|
```python
|
|
result = tu.execute_tool(
|
|
"ensembl_vep_region",
|
|
region="7:140753336-140753336",
|
|
species="human"
|
|
)
|
|
```
|
|
|
|
## Practical Examples
|
|
|
|
### Example 1: Clinical Variant Screening
|
|
|
|
Find all pathogenic/likely pathogenic variants:
|
|
|
|
```python
|
|
from python_implementation import parse_vcf, batch_annotate_variants
|
|
from tooluniverse import ToolUniverse
|
|
|
|
# Parse VCF
|
|
vcf_data = parse_vcf("patient.vcf")
|
|
|
|
# Load ToolUniverse
|
|
tu = ToolUniverse()
|
|
tu.load_tools()
|
|
|
|
# Annotate variants with rsIDs
|
|
rsid_variants = [v for v in vcf_data.variants if v.vid and v.vid.startswith('rs')]
|
|
annotations = batch_annotate_variants(tu, rsid_variants, max_annotate=100)
|
|
|
|
# Find pathogenic variants
|
|
pathogenic = [
|
|
a for a in annotations
|
|
if a.clinvar_classification and 'pathogenic' in a.clinvar_classification.lower()
|
|
]
|
|
|
|
print(f"Found {len(pathogenic)} pathogenic variants:")
|
|
for a in pathogenic:
|
|
print(f" {a.variant_key} - {a.gene_symbol}")
|
|
print(f" Classification: {a.clinvar_classification}")
|
|
print(f" gnomAD AF: {a.gnomad_af}")
|
|
print(f" CADD: {a.cadd_phred}")
|
|
```
|
|
|
|
### Example 2: Population Frequency Analysis
|
|
|
|
Find rare variants (AF < 1%):
|
|
|
|
```python
|
|
annotations = batch_annotate_variants(tu, vcf_data.variants, max_annotate=100)
|
|
|
|
rare = [a for a in annotations if a.gnomad_af is not None and a.gnomad_af < 0.01]
|
|
|
|
print(f"Found {len(rare)} rare variants (AF < 1%):")
|
|
for a in rare:
|
|
print(f" {a.variant_key} - {a.gene_symbol}")
|
|
print(f" AF: {a.gnomad_af}")
|
|
```
|
|
|
|
### Example 3: Pathogenicity Score Filtering
|
|
|
|
Find high CADD score variants (top 1% most deleterious):
|
|
|
|
```python
|
|
annotations = batch_annotate_variants(tu, vcf_data.variants, max_annotate=100)
|
|
|
|
high_cadd = [a for a in annotations if a.cadd_phred is not None and a.cadd_phred >= 20]
|
|
|
|
print(f"Found {len(high_cadd)} high CADD score variants (โฅ20):")
|
|
for a in high_cadd:
|
|
print(f" {a.variant_key} - {a.gene_symbol} - {a.mutation_type}")
|
|
print(f" CADD: {a.cadd_phred}")
|
|
```
|
|
|
|
### Example 4: Compare ClinVar with Population Frequency
|
|
|
|
Find variants classified as pathogenic but common in population (potential reclassification candidates):
|
|
|
|
```python
|
|
annotations = batch_annotate_variants(tu, vcf_data.variants, max_annotate=100)
|
|
|
|
conflicting = [
|
|
a for a in annotations
|
|
if a.clinvar_classification and 'pathogenic' in a.clinvar_classification.lower()
|
|
and a.gnomad_af is not None and a.gnomad_af > 0.01 # Common (>1%)
|
|
]
|
|
|
|
print(f"Found {len(conflicting)} variants with conflicting evidence:")
|
|
for a in conflicting:
|
|
print(f" {a.variant_key} - {a.gene_symbol}")
|
|
print(f" ClinVar: {a.clinvar_classification}")
|
|
print(f" gnomAD AF: {a.gnomad_af} (common)")
|
|
```
|
|
|
|
## Annotation Report
|
|
|
|
Generate a comprehensive annotation report:
|
|
|
|
```python
|
|
from python_implementation import variant_analysis_pipeline
|
|
|
|
report = variant_analysis_pipeline(
|
|
vcf_path="variants.vcf",
|
|
output_file="annotated_report.md",
|
|
annotate=True,
|
|
max_annotate=50 # Annotate top 50 variants
|
|
)
|
|
```
|
|
|
|
**Report includes**:
|
|
1. Variant Annotations table with:
|
|
- Variant ID
|
|
- Gene
|
|
- Mutation type
|
|
- ClinVar classification
|
|
- gnomAD AF
|
|
- CADD score
|
|
2. Clinical Significance distribution
|
|
3. Top mutated genes
|
|
4. Summary statistics
|
|
|
|
## Best Practices
|
|
|
|
### 1. Annotate Variants with rsIDs First
|
|
|
|
rsIDs are the most reliable identifiers:
|
|
|
|
```python
|
|
# Prioritize variants with rsIDs
|
|
rsid_variants = [v for v in vcf_data.variants if v.vid and v.vid.startswith('rs')]
|
|
annotations = batch_annotate_variants(tu, rsid_variants, max_annotate=100)
|
|
```
|
|
|
|
### 2. Use MyVariant.info for Batch Annotation
|
|
|
|
MyVariant.info aggregates multiple sources in one call:
|
|
- ClinVar
|
|
- dbSNP
|
|
- gnomAD
|
|
- CADD
|
|
- SIFT
|
|
- PolyPhen
|
|
|
|
### 3. Respect Rate Limits
|
|
|
|
Limit batch annotation to 50-100 variants at a time:
|
|
|
|
```python
|
|
annotations = batch_annotate_variants(tu, variants, max_annotate=100)
|
|
```
|
|
|
|
### 4. Handle Missing Data
|
|
|
|
Not all variants have all annotations:
|
|
|
|
```python
|
|
for ann in annotations:
|
|
if ann.clinvar_classification:
|
|
print(f"ClinVar: {ann.clinvar_classification}")
|
|
else:
|
|
print("ClinVar: No data")
|
|
|
|
if ann.gnomad_af is not None:
|
|
print(f"gnomAD AF: {ann.gnomad_af}")
|
|
else:
|
|
print("gnomAD AF: Not available")
|
|
```
|
|
|
|
### 5. Combine with Filtering
|
|
|
|
Annotate only high-quality variants:
|
|
|
|
```python
|
|
from python_implementation import filter_variants, FilterCriteria
|
|
|
|
# Filter first
|
|
criteria = FilterCriteria(min_depth=20, pass_only=True)
|
|
passing, failing = filter_variants(vcf_data.variants, criteria)
|
|
|
|
# Then annotate
|
|
annotations = batch_annotate_variants(tu, passing, max_annotate=50)
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Issue: "No annotation found"
|
|
|
|
**Cause**: Variant not in database or wrong identifier format
|
|
|
|
**Solution**:
|
|
- Verify rsID is correct (rs12345)
|
|
- Try HGVS format (chr7:g.140753336A>T)
|
|
- Check if variant is in dbSNP/ClinVar
|
|
|
|
### Issue: Empty ClinVar classification
|
|
|
|
**Cause**: Variant has no ClinVar submission
|
|
|
|
**Solution**: This is normal. Not all variants have clinical significance data.
|
|
|
|
### Issue: Missing gnomAD allele frequency
|
|
|
|
**Cause**: Variant not in gnomAD (very rare or not covered)
|
|
|
|
**Solution**: Consider as potentially rare/novel variant
|
|
|
|
### Issue: Rate limiting errors
|
|
|
|
**Cause**: Too many API requests
|
|
|
|
**Solution**: Reduce `max_annotate` parameter or add delays between batches
|
|
|
|
## Advanced: Custom Annotation Pipeline
|
|
|
|
Build a custom annotation pipeline:
|
|
|
|
```python
|
|
from tooluniverse import ToolUniverse
|
|
|
|
tu = ToolUniverse()
|
|
tu.load_tools()
|
|
|
|
def annotate_variant_full(variant):
|
|
"""Get comprehensive annotation from multiple sources"""
|
|
|
|
annotation = {
|
|
'variant_id': variant.vid,
|
|
'chrom': variant.chrom,
|
|
'pos': variant.pos,
|
|
'ref': variant.ref,
|
|
'alt': variant.alt
|
|
}
|
|
|
|
# MyVariant.info (primary)
|
|
if variant.vid and variant.vid.startswith('rs'):
|
|
myvariant = tu.execute_tool("MyVariant_query_variants", query=variant.vid)
|
|
if myvariant.get('data', {}).get('hits'):
|
|
hit = myvariant['data']['hits'][0]
|
|
annotation['clinvar'] = hit.get('clinvar', {})
|
|
annotation['gnomad'] = hit.get('gnomad_genome', {})
|
|
annotation['cadd'] = hit.get('cadd', {})
|
|
|
|
# dbSNP (supplemental)
|
|
if variant.vid and variant.vid.startswith('rs'):
|
|
dbsnp = tu.execute_tool("dbsnp_get_variant_by_rsid", rsid=variant.vid)
|
|
if dbsnp.get('status') == 'success':
|
|
annotation['allele_frequencies'] = dbsnp['data'].get('allele_frequencies', {})
|
|
|
|
return annotation
|
|
|
|
# Use on variants
|
|
for variant in vcf_data.variants[:10]:
|
|
full_annotation = annotate_variant_full(variant)
|
|
print(full_annotation)
|
|
```
|