Bio applied clinical genomics
Skill Pavel-Kravchenko/Bioinformatics/Skills/bio-applied-clinical-genomics
208 bioinformatics skills for Claude Code — NGS, single-cell, metagenomics, structural biology, algorithms, AI for science
npx -y skills add Pavel-Kravchenko/Bioinformatics --skill bio-applied-clinical-genomicsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Classify germline variant pathogenicity with ACMG/AMP 5-tier criteria (PVS1/PS1-4/PM1-6/PP1-5/BA1/BS1-4/BP1-7), query ClinVar via NCBI E-utilities, and filter by gnomAD population frequency to draft a clinical variant report. Use when doing ACMG classification, deciding Pathogenic/Likely Pathogenic/VUS/Likely Benign/Benign calls, looking up a variant in ClinVar, or writing a clinical genomics/diagnostic report.
SKILL.md
12.2 KB, as published. Nobody here has run it
Applied Clinical Genomics: ACMG/AMP Variant Classification
When to Use
- Classifying a germline variant's pathogenicity from a list of ACMG/AMP evidence codes (PVS1, PS1-4, PM1-6, PP1-5, BA1, BS1-4, BP1-7).
- Looking up a variant or gene in ClinVar to check prior clinical significance and review status.
- Filtering candidate variants by gnomAD-style population allele frequency before applying ACMG rules (PM2/BA1/BS1 gating).
- Drafting the interpretation section of a diagnostic, carrier, or predictive-testing clinical report.
- Deciding which type of genetic test (diagnostic, carrier, pharmacogenomic, prenatal, tumor) applies to a clinical scenario.
Version Compatibility
- ACMG/AMP guidelines: Richards et al. 2015 (Table 5 combining rules), as refined by ClinGen Sequence Variant Interpretation (SVI) working group recommendations (ongoing updates to PVS1, PM2, BS1/BA1 thresholds — always check the current ClinGen SVI recommendations for a given gene/criterion before finalizing a call).
- ClinVar/NCBI E-utilities: JSON
esearch/esummaryendpoints, stable API, no versioning concerns;requests≥2.28, Python ≥3.10 (useslist[str],dict | Nonesyntax).
Prerequisites
pip install requests(only needed for live ClinVar queries; classification logic has no dependencies).- Familiarity with VCF fields (see
bio-variant-calling-vcf-basics) and variant annotation (bio-variant-calling-variant-annotation). - A gnomAD or in-house population frequency source for PM2/BA1/BS1 gating.
Types of Genetic Testing
| Type | Purpose | Typical Approach |
|---|---|---|
| Diagnostic | Identify cause of existing disease | WES/WGS or gene panels |
| Predictive | Assess future disease risk | Targeted testing |
| Carrier | Identify heterozygous carriers | Carrier panels |
| Pharmacogenomic | Guide drug selection/dosing | PGx panels |
| Prenatal/Newborn | Screen or diagnose fetus/newborn | cfDNA, targeted panels |
| Somatic/Tumor | Guide cancer treatment | Tumor panels, WES |
ACMG/AMP 5-Tier Classification
Pathogenic > Likely Pathogenic > VUS > Likely Benign > Benign
(P) (LP) (LB) (B)
- LP/P: >90% certainty disease-causing, reportable and actionable.
- VUS: insufficient evidence either way, not acted upon clinically.
- LB/B: >90% certainty benign, reportable as not disease-causing.
Pathogenic evidence:
| Strength | Codes | Examples |
|---|---|---|
| Very Strong | PVS1 | Null variant in a gene where loss-of-function is a known disease mechanism |
| Strong | PS1-PS4 | Same AA change as known pathogenic; confirmed de novo; functional study; prevalence in affected cohort |
| Moderate | PM1-PM6 | Mutational hotspot; absent from population databases; protein length change; novel missense in low-missense-tolerant gene |
| Supporting | PP1-PP5 | Co-segregation with disease; computational evidence; phenotype specificity; reputable source without independent evidence |
Benign evidence:
| Strength | Codes | Examples |
|---|---|---|
| Stand-alone | BA1 | Allele frequency >5% in any gnomAD population |
| Strong | BS1-BS4 | Frequency exceeds expected for the disorder; healthy adult carrier; functional no-effect; non-segregation |
| Supporting | BP1-BP7 | Missense in a gene where only truncating variants cause disease; benign in silico consensus; synonymous with no splice impact |
Core Workflow
Goal: Turn a list of ACMG/AMP evidence codes into one of the 5 classification tiers using the Richards et al. 2015 Table 5 combining rules.
Approach: Bucket the codes by strength, then walk the benign rules first (BA1 short-circuits to Benign), then the pathogenic/likely-pathogenic rules in order of decreasing evidence.
CRITERIA_STRENGTH = {
'PVS1': 'very_strong',
'PS1': 'strong', 'PS2': 'strong', 'PS3': 'strong', 'PS4': 'strong',
'PM1': 'moderate', 'PM2': 'moderate', 'PM3': 'moderate',
'PM4': 'moderate', 'PM5': 'moderate', 'PM6': 'moderate',
'PP1': 'supporting', 'PP2': 'supporting', 'PP3': 'supporting',
'PP4': 'supporting', 'PP5': 'supporting',
'BA1': 'stand_alone',
'BS1': 'strong', 'BS2': 'strong', 'BS3': 'strong', 'BS4': 'strong',
'BP1': 'supporting', 'BP2': 'supporting', 'BP3': 'supporting',
'BP4': 'supporting', 'BP5': 'supporting', 'BP6': 'supporting', 'BP7': 'supporting',
}
def classify_variant(criteria: list[str]) -> str:
"""Classify a variant from ACMG/AMP evidence codes.
Implements the combining rules from Richards et al. 2015, Table 5.
Returns one of: Pathogenic, Likely Pathogenic, VUS, Likely Benign, Benign.
"""
path_criteria = [c for c in criteria if c.startswith(('PVS', 'PS', 'PM', 'PP'))]
benign_criteria = [c for c in criteria if c.startswith(('BA', 'BS', 'BP'))]
pvs = sum(1 for c in path_criteria if CRITERIA_STRENGTH.get(c) == 'very_strong')
ps = sum(1 for c in path_criteria if CRITERIA_STRENGTH.get(c) == 'strong')
pm = sum(1 for c in path_criteria if CRITERIA_STRENGTH.get(c) == 'moderate')
pp = sum(1 for c in path_criteria if CRITERIA_STRENGTH.get(c) == 'supporting')
ba = sum(1 for c in benign_criteria if CRITERIA_STRENGTH.get(c) == 'stand_alone')
bs = sum(1 for c in benign_criteria if CRITERIA_STRENGTH.get(c) == 'strong')
bp = sum(1 for c in benign_criteria if CRITERIA_STRENGTH.get(c) == 'supporting')
# Benign rules checked first -- BA1 alone is stand-alone benign
if ba >= 1:
return 'Benign'
if bs >= 2:
return 'Benign'
if bs >= 1 and bp >= 1:
return 'Likely Benign'
if bp >= 2:
return 'Likely Benign'
# Pathogenic rules
if pvs >= 1 and (ps >= 1 or pm >= 2 or (pm >= 1 and pp >= 1) or pp >= 2):
return 'Pathogenic'
if ps >= 2:
return 'Pathogenic'
if ps >= 1 and (pm >= 3 or (pm >= 2 and pp >= 2) or (pm >= 1 and pp >= 4)):
return 'Pathogenic'
# Likely Pathogenic rules
if pvs >= 1 and pm >= 1:
return 'Likely Pathogenic'
if ps >= 1 and 1 <= pm <= 2:
return 'Likely Pathogenic'
if ps >= 1 and pp >= 2:
return 'Likely Pathogenic'
if pm >= 3:
return 'Likely Pathogenic'
if pm >= 2 and pp >= 2:
return 'Likely Pathogenic'
if pm >= 1 and pp >= 4:
return 'Likely Pathogenic'
if pvs >= 1:
# Not stated verbatim in Table 5 but a common pragmatic convention
# (used by tools like InterVar) for a lone PVS1.
return 'Likely Pathogenic'
return 'VUS'
Goal: Look up prior clinical significance for a variant/gene in ClinVar before finalizing a call (feeds PP5/BP6-style "reputable source" evidence).
Approach: Two-step NCBI E-utilities call — esearch for ClinVar UIDs matching a term, then esummary to pull clinical significance and review status for each hit.
import requests
def query_clinvar(variant_description: str, retmax: int = 5) -> dict:
"""Query ClinVar for a variant/gene via NCBI E-utilities.
Args:
variant_description: e.g. 'BRCA1[gene] AND pathogenic[clinical_significance]'
or a specific HGVS term like 'NM_007294.4:c.5266dupC'.
retmax: max number of records to summarize.
Returns:
dict with 'query', 'count' (total hits), and 'records' (list of dicts).
"""
base = 'https://eutils.ncbi.nlm.nih.gov/entrez/eutils'
search = requests.get(f'{base}/esearch.fcgi', params={
'db': 'clinvar', 'term': variant_description,
'retmax': retmax, 'retmode': 'json',
}, timeout=10)
search.raise_for_status()
ids = search.json().get('esearchresult', {}).get('idlist', [])
total = int(search.json()['esearchresult'].get('count', 0))
if not ids:
return {'query': variant_description, 'count': 0, 'records': []}
summary = requests.get(f'{base}/esummary.fcgi', params={
'db': 'clinvar', 'id': ','.join(ids), 'retmode': 'json',
}, timeout=10)
summary.raise_for_status()
result_block = summary.json().get('result', {})
records = []
for uid in ids:
entry = result_block.get(uid, {})
if entry:
records.append({
'uid': uid,
'title': entry.get('title', ''),
'clinical_significance': entry.get('clinical_significance', {}).get('description', 'N/A'),
'review_status': entry.get('clinical_significance', {}).get('review_status', 'N/A'),
'gene': entry.get('gene_sort', ''),
})
return {'query': variant_description, 'count': total, 'records': records}
Goal: Gate candidate variants by population frequency (PM2 "absent/rare" and BA1/BS1 "too common") before spending time on other evidence.
Approach: Compare per-population frequencies against a popmax threshold and a global-average threshold; report the reason each variant passed or failed.
def filter_by_frequency(
variants: list[dict], max_af: float = 0.01, max_popmax: float = 0.01
) -> tuple[list[dict], list[tuple]]:
"""Split variants into rare (candidate disease-causing) vs. common (BA1/BS1-range).
Args:
variants: dicts with a 'freq' key mapping population -> allele frequency
(e.g. gnomAD 'afr', 'nfe', 'eas', ...).
max_af: global allele frequency threshold (average across populations).
max_popmax: max allowed frequency in any single population (gnomAD popmax).
Returns:
(rare, filtered_out) where filtered_out is (variant, freq, reason) tuples.
"""
rare, filtered_out = [], []
for var in variants:
freqs = var['freq'].values()
popmax = max(freqs)
global_af = sum(freqs) / len(freqs) # simplified; gnomAD uses weighted average
if popmax > max_popmax:
filtered_out.append((var, popmax, 'popmax exceeds threshold'))
elif global_af > max_af:
filtered_out.append((var, global_af, 'global AF exceeds threshold'))
else:
rare.append(var)
return rare, filtered_out
Pitfalls
- Don't stack correlated evidence: PS1 (same AA change reported pathogenic) and PM5 (different AA change, same residue) are related but not interchangeable — don't invent a rule combination not in Table 5.
- PM2 is not stand-alone: "absent from population databases" alone should not drive a Pathogenic/Likely Pathogenic call in current ClinGen SVI guidance — always pair with other evidence.
- ClinVar submissions vary in quality: check
review_status(e.g. "reviewed by expert panel" vs. "no assertion criteria provided") before trusting a ClinVar significance label as PP5 evidence. - gnomAD population coverage differs by ancestry: a variant absent in one gnomAD population may still be common in an underrepresented population; low popmax confidence in small subpopulations can look artificially rare.
- Coordinate systems: BED is 0-based half-open; VCF/ClinVar HGVS are 1-based inclusive — mixing them causes off-by-one variant lookups.
- Multiple testing: when scanning many candidate variants against frequency/prediction thresholds, remember this is filtering, not hypothesis testing — FDR correction applies to statistical association tests, not ACMG evidence gating.
See Also
bio-clinical-databases-clinvar-lookup— deeper ClinVar query patterns and result parsing.bio-clinical-databases-gnomad-frequencies— full gnomAD population frequency access.bio-clinical-databases-variant-prioritization— combining multiple evidence sources to rank candidates.bio-variant-calling-clinical-interpretation— upstream VCF annotation feeding into ACMG classification.clinical-reports— formatting the final report document.