gitmodelmujtaba's picture
Deploy Medical Guidelines Knowledge Graph Explorer to Hugging Face Spaces
62c74ae
Raw History Blame Contribute Delete
10.3 kB
"""
Clinical Acronym and Abbreviation Expansion Engine.
Provides high-coverage clinical abbreviation normalization, context-sensitive
disambiguation, and query augmentation across SNOMED CT, LOINC, and RxNorm domains.
"""
import re
from typing import Dict, List, Optional, Tuple
# Core clinical abbreviation mappings: Acronym -> Canonical Expansion
CLINICAL_ACRONYMS: Dict[str, str] = {
# Cardiovascular
"HTN": "Hypertension",
"CAD": "Coronary artery disease",
"CABG": "Coronary artery bypass graft",
"CHF": "Congestive heart failure",
"HF": "Heart failure",
"HFpEF": "Heart failure with preserved ejection fraction",
"HFrEF": "Heart failure with reduced ejection fraction",
"AF": "Atrial fibrillation",
"AFIB": "Atrial fibrillation",
"A-FIB": "Atrial fibrillation",
"AFL": "Atrial flutter",
"STEMI": "ST elevation myocardial infarction",
"NSTEMI": "Non-ST elevation myocardial infarction",
"SVT": "Supraventricular tachycardia",
"VT": "Ventricular tachycardia",
"VF": "Ventricular fibrillation",
"DVT": "Deep vein thrombosis",
"PAD": "Peripheral artery disease",
"PVD": "Peripheral vascular disease",
"AAA": "Abdominal aortic aneurysm",
"AS": "Aortic valve stenosis",
"AR": "Aortic regurgitation",
"MR": "Mitral regurgitation",
# Respiratory
"COPD": "Chronic obstructive pulmonary disease",
"SOB": "Shortness of breath",
"DOE": "Dyspnea on exertion",
"URI": "Upper respiratory infection",
"OSA": "Obstructive sleep apnea",
"ARDS": "Acute respiratory distress syndrome",
"PNA": "Pneumonia",
"CAP": "Community acquired pneumonia",
"HAP": "Hospital acquired pneumonia",
"CF": "Cystic fibrosis",
"ILD": "Interstitial lung disease",
"IPF": "Idiopathic pulmonary fibrosis",
# Endocrine & Metabolic
"DM": "Diabetes mellitus",
"DM1": "Type 1 diabetes mellitus",
"T1DM": "Type 1 diabetes mellitus",
"DM2": "Type 2 diabetes mellitus",
"T2DM": "Type 2 diabetes mellitus",
"DKA": "Diabetic ketoacidosis",
"HHS": "Hyperosmolar hyperglycemic state",
"HTG": "Hypertriglyceridemia",
"HLD": "Hyperlipidemia",
"PCOS": "Polycystic ovary syndrome",
# Nephrology & Urology
"AKI": "Acute kidney injury",
"CKD": "Chronic kidney disease",
"ESRD": "End stage renal disease",
"UTI": "Urinary tract infection",
"BPH": "Benign prostatic hyperplasia",
"ATN": "Acute tubular necrosis",
"GN": "Glomerulonephritis",
# Neurological
"CVA": "Cerebrovascular accident",
"TIA": "Transient ischemic attack",
"ICH": "Intracerebral hemorrhage",
"SAH": "Subarachnoid hemorrhage",
"SDH": "Subdural hematoma",
"ALS": "Amyotrophic lateral sclerosis",
"PD": "Parkinson's disease",
"AD": "Alzheimer's disease",
"ICP": "Intracranial pressure",
# Gastrointestinal
"GERD": "Gastroesophageal reflux disease",
"IBD": "Inflammatory bowel disease",
"IBS": "Irritable bowel syndrome",
"PUD": "Peptic ulcer disease",
"UGIB": "Upper gastrointestinal bleeding",
"LGIB": "Lower gastrointestinal bleeding",
"SBO": "Small bowel obstruction",
"LBO": "Large bowel obstruction",
"NASH": "Nonalcoholic steatohepatitis",
"NAFLD": "Nonalcoholic fatty liver disease",
# Rheumatology & Infectious Disease
"RA": "Rheumatoid arthritis",
"OA": "Osteoarthritis",
"SLE": "Systemic lupus erythematosus",
"HIV": "Human immunodeficiency virus infection",
"AIDS": "Acquired immunodeficiency syndrome",
"TB": "Tuberculosis",
"MRSA": "Methicillin resistant Staphylococcus aureus",
"MSSA": "Methicillin susceptible Staphylococcus aureus",
"C. DIFF": "Clostridioides difficile infection",
"CDIFF": "Clostridioides difficile infection",
# Common Labs (LOINC domain)
"CBC": "Complete blood count",
"BMP": "Basic metabolic panel",
"CMP": "Comprehensive metabolic panel",
"LFT": "Liver function tests",
"HGB": "Hemoglobin",
"HB": "Hemoglobin",
"HCT": "Hematocrit",
"WBC": "White blood cell count",
"RBC": "Red blood cell count",
"PLT": "Platelet count",
"INR": "International normalized ratio",
"PT": "Prothrombin time",
"PTT": "Partial thromboplastin time",
"CR": "Creatinine",
"BUN": "Blood urea nitrogen",
"HBA1C": "Hemoglobin A1c",
"A1C": "Hemoglobin A1c",
"TSH": "Thyroid stimulating hormone",
"ESR": "Erythrocyte sedimentation rate",
"CRP": "C-reactive protein",
"BNP": "B-type natriuretic peptide",
"UA": "Urinalysis",
# Procedures & Diagnostics
"EGD": "Esophagogastroduodenoscopy",
"CXR": "Chest X-ray",
"CT": "Computed tomography",
"CTA": "Computed tomography angiography",
"MRI": "Magnetic resonance imaging",
"MRA": "Magnetic resonance angiography",
"ECHO": "Echocardiogram",
"EKG": "Electrocardiogram",
"ECG": "Electrocardiogram",
"US": "Ultrasound",
"LP": "Lumbar puncture",
}
# Context-dependent disambiguation rules:
# Acronym -> List of (ContextKeywords, DisambiguatedExpansion)
CONTEXT_DISAMBIGUATION_RULES: Dict[str, List[Tuple[List[str], str]]] = {
"MS": [
(["valve", "mitral", "murmur", "stenosis", "rheumatic", "cardiac", "heart"], "Mitral valve stenosis"),
(["sclerosis", "demyelinating", "relapse", "flare", "neurologic", "mri", "brain", "spine", "optic"], "Multiple sclerosis"),
(["mental", "status", "alert", "oriented", "confused"], "Mental status"),
],
"MI": [
(["chest", "pain", "troponin", "stemi", "nstemi", "cardiac", "coronary", "ecg", "ekg"], "Myocardial infarction"),
(["mitral", "insufficiency", "valve", "regurgitation", "murmur"], "Mitral valve insufficiency"),
],
"PE": [
(["chest", "pain", "shortness", "breath", "d-dimer", "cta", "thrombus", "embolism", "clot", "dvt", "lung"], "Pulmonary embolism"),
(["exam", "physical", "vital", "abdomen", "extremities", "heent", "neuro", "pupils", "clear"], "Physical examination"),
(["effusion", "pleural", "thoracentesis", "fluid"], "Pleural effusion"),
],
"MR": [
(["valve", "mitral", "regurgitation", "systolic", "murmur", "cardiac"], "Mitral regurgitation"),
(["retardation", "mental", "intellectual"], "Mental retardation"),
(["resonance", "magnetic", "scan", "imaging"], "Magnetic resonance"),
],
"AS": [
(["valve", "aortic", "stenosis", "systolic", "murmur", "cardiac", "tavr", "echo"], "Aortic valve stenosis"),
(["ankylosing", "spondylitis", "hla-b27", "back", "sacroiliac"], "Ankylosing spondylitis"),
],
"RA": [
(["arthritis", "rheumatoid", "joint", "swelling", "rf", "anti-ccp", "methotrexate"], "Rheumatoid arthritis"),
(["atrium", "right", "atrial", "cardiac", "enlargement"], "Right atrium"),
],
"PT": [
(["inr", "prothrombin", "coagulation", "warfarin", "bleeding"], "Prothrombin time"),
(["physical", "therapy", "rehab", "ambulation", "gait", "exercises"], "Physical therapy"),
(["patient"], "Patient"),
],
}
class ClinicalAcronymExpander:
"""
Normalizes clinical shorthand and expands domain acronyms
into fully qualified medical terminology.
"""
def __init__(self, custom_dict: Optional[Dict[str, str]] = None):
self.lexicon = dict(CLINICAL_ACRONYMS)
if custom_dict:
self.lexicon.update(custom_dict)
self.context_rules = dict(CONTEXT_DISAMBIGUATION_RULES)
def disambiguate(self, acronym: str, context: Optional[str] = None) -> Optional[str]:
"""
Disambiguates an acronym using contextual note keywords if ambiguous.
"""
acr_upper = acronym.strip().upper()
if acr_upper in self.context_rules:
if context:
context_lower = context.lower()
best_expansion = None
max_matches = 0
for keywords, expansion in self.context_rules[acr_upper]:
matches = sum(1 for kw in keywords if re.search(rf"\b{re.escape(kw)}\b", context_lower))
if matches > max_matches:
max_matches = matches
best_expansion = expansion
if best_expansion and max_matches > 0:
return best_expansion
# Default to first expansion if no context matched
return self.context_rules[acr_upper][0][1]
return self.lexicon.get(acr_upper, None)
def expand_mention(self, mention: str, context: Optional[str] = None) -> str:
"""
Expands a standalone clinical mention string if it is an acronym,
or returns the original mention if not recognized.
"""
cleaned = mention.strip()
cleaned_upper = cleaned.upper()
expansion = self.disambiguate(cleaned_upper, context)
if expansion:
return expansion
return cleaned
def get_search_queries(self, mention: str, context: Optional[str] = None) -> List[str]:
"""
Generates enriched search query representations for retrieval:
e.g. ['HTN', 'Hypertension', 'HTN (Hypertension)']
"""
cleaned = mention.strip()
expansion = self.disambiguate(cleaned, context)
queries = [cleaned]
if expansion and expansion.lower() != cleaned.lower():
queries.append(expansion)
queries.append(f"{cleaned} ({expansion})")
return list(dict.fromkeys(queries))
def normalize_text_acronyms(self, text: str) -> str:
"""
Scans a clinical narrative for known unambiguous uppercase acronyms
and attaches their expansions inline: e.g. 'c/o SOB' -> 'c/o SOB (Shortness of breath)'
"""
def replace_fn(match):
word = match.group(0)
word_upper = word.upper()
if word_upper in self.lexicon and word_upper not in self.context_rules:
exp = self.lexicon[word_upper]
return f"{word} ({exp})"
return word
# Only target standalone uppercase words of length 2 to 5
pattern = r"\b[A-Z0-9\-\.]{2,6}\b"
return re.sub(pattern, replace_fn, text)