ruanhelansantos commited on
Commit
5c34085
·
verified ·
1 Parent(s): 48246d2

Update explainer.py

Browse files
Files changed (1) hide show
  1. explainer.py +57 -96
explainer.py CHANGED
@@ -1,135 +1,96 @@
1
  # explainer.py
2
  # ECG Condition Classifier - Clinical Explanation Generator
3
 
4
- # ── Keyword-based label matching ──────────────────────────────────────────────
5
- # Maps keywords found in model output labels to clinical explanations.
6
- # This works regardless of whether the model returns "NORM", "Normal",
7
- # "LABEL_0", or any other format.
8
-
9
- CONDITION_MAP = [
10
- {
11
- "keywords": ["norm", "normal", "sinus"],
12
- "code": "NORM",
13
  "summary": "Normal Sinus Rhythm",
14
- "explanation": (
15
- "The ECG pattern is consistent with a normal sinus rhythm. "
16
- "Regular P waves, QRS complexes, and T waves are present within "
17
- "expected parameters. No significant cardiac abnormalities identified."
18
- ),
19
  "urgency": "Routine",
20
- "recommendation": "No immediate action indicated. Routine monitoring as appropriate."
 
21
  },
22
- {
23
- "keywords": ["afib", "atrial fib", "fibrillation", "af"],
24
- "code": "AFIB",
25
  "summary": "Atrial Fibrillation",
26
- "explanation": (
27
- "The ECG pattern is consistent with Atrial Fibrillation. "
28
- "Characteristic findings include an irregularly irregular rhythm, "
29
- "absence of distinct P waves, and variable R-R intervals. "
30
- "AFib is associated with increased risk of stroke and heart failure."
31
- ),
32
  "urgency": "Elevated",
33
- "recommendation": "Clinical evaluation recommended. Rate/rhythm management and stroke risk assessment advised."
 
34
  },
35
- {
36
- "keywords": ["mi", "infarct", "stemi", "nstemi", "myocardial"],
37
- "code": "MI",
38
  "summary": "Myocardial Infarction",
39
- "explanation": (
40
- "The ECG pattern is consistent with Myocardial Infarction. "
41
- "Findings may include ST-segment elevation or depression and "
42
- "pathological Q waves suggesting myocardial injury or ischemia."
43
- ),
44
  "urgency": "URGENT",
45
- "recommendation": "⚠️ IMMEDIATE clinical review strongly recommended. Activate emergency cardiac protocols if STEMI suspected."
 
46
  },
47
- {
48
- "keywords": ["sttc", "st ", "t-wave", "t wave", "st/t", "ischemi"],
49
- "code": "STTC",
50
  "summary": "ST/T-Wave Change",
51
- "explanation": (
52
- "Non-specific ST-segment and/or T-wave changes detected. "
53
- "These findings can represent myocardial ischemia, electrolyte "
54
- "abnormalities, medication effects, or normal variants. "
55
- "Clinical context is essential for interpretation."
56
- ),
57
  "urgency": "Moderate",
58
- "recommendation": "Clinical correlation advised. Serial ECGs and lab results may be warranted."
 
59
  },
60
- {
61
- "keywords": ["cd", "conduction", "bundle", "block", "bbb", "lbbb", "rbbb"],
62
- "code": "CD",
63
  "summary": "Conduction Disturbance",
64
- "explanation": (
65
- "The ECG shows signs of a conduction disturbance. "
66
- "Findings may include widened QRS complex or abnormal morphology "
67
- "consistent with bundle branch block or intraventricular conduction delay."
68
- ),
69
  "urgency": "Moderate",
70
- "recommendation": "Cardiology follow-up recommended. Echocardiogram may help determine significance."
 
71
  },
72
- ]
73
-
74
- DEFAULT = {
75
- "code": "?",
76
- "summary": "Unrecognized Pattern",
77
- "explanation": (
78
- "The model returned a label that could not be mapped to a known "
79
- "cardiac condition. This may indicate an unusual ECG pattern or "
80
- "a label category outside the standard NORM/AFIB/MI/STTC/CD set."
81
- ),
82
- "urgency": "Unknown",
83
- "recommendation": "Manual review by a qualified cardiologist is recommended."
84
  }
85
 
86
- DISCLAIMER = (
87
- "\n\n⚠️ DISCLAIMER: For educational and research purposes only. "
88
- "Does not constitute medical advice. Always consult a qualified healthcare provider."
89
- )
90
 
91
 
92
  def match_condition(label):
93
- """Matches a raw model label string to a known condition using keyword search."""
94
- label_lower = label.lower()
95
- for condition in CONDITION_MAP:
96
- if any(kw in label_lower for kw in condition["keywords"]):
97
- return condition
98
- return DEFAULT
 
 
 
 
 
 
99
 
100
 
101
- def generate_explanation(label, confidence):
102
  """
103
- Generates a clinical explanation for any label the model returns.
104
-
105
- Args:
106
- label: str — raw label from model (e.g. "Normal", "AFIB", "LABEL_2")
107
- confidence: float — confidence percentage
108
-
109
- Returns:
110
- (explanation_text: str, urgency: str)
111
  """
112
- data = match_condition(label)
 
 
 
 
 
 
 
 
 
 
 
 
113
 
114
- explanation_text = (
115
- f"Condition: {data['summary']}\n"
116
  f"Raw Model Label: {label}\n"
117
  f"Confidence: {confidence:.1f}%\n"
118
- f"Urgency: {data['urgency']}\n\n"
119
- f"{data['explanation']}\n\n"
120
- f"Recommendation: {data['recommendation']}"
121
  f"{DISCLAIMER}"
122
  )
123
 
124
- return explanation_text, data["urgency"]
125
 
126
 
127
  def format_top3(results):
128
- """Formats top predictions into a readable breakdown."""
129
  lines = ["Top Predictions:"]
130
  for i, r in enumerate(results[:3], 1):
131
- lbl = r["label"]
132
- pct = r["score"] * 100
133
- bar = "█" * int(pct / 5)
134
- lines.append(f" {i}. {lbl}: {pct:.1f}% {bar}")
135
  return "\n".join(lines)
 
1
  # explainer.py
2
  # ECG Condition Classifier - Clinical Explanation Generator
3
 
4
+ CONDITION_MAP = {
5
+ "NORM": {
 
 
 
 
 
 
 
6
  "summary": "Normal Sinus Rhythm",
 
 
 
 
 
7
  "urgency": "Routine",
8
+ "description": "The ECG shows a normal sinus rhythm with regular rate and rhythm, normal P waves, QRS complexes, and T waves.",
9
+ "recommendation": "No immediate cardiac intervention required. Continue routine follow-up as clinically indicated.",
10
  },
11
+ "AFIB": {
 
 
12
  "summary": "Atrial Fibrillation",
 
 
 
 
 
 
13
  "urgency": "Elevated",
14
+ "description": "The ECG pattern is consistent with Atrial Fibrillation. Characteristic findings include an irregularly irregular rhythm, absence of distinct P waves, and variable R-R intervals. AFib is associated with increased risk of stroke and heart failure.",
15
+ "recommendation": "Clinical evaluation recommended. Rate/rhythm management and stroke risk assessment advised.",
16
  },
17
+ "MI": {
 
 
18
  "summary": "Myocardial Infarction",
 
 
 
 
 
19
  "urgency": "URGENT",
20
+ "description": "The ECG pattern is consistent with Myocardial Infarction. Findings may include ST-segment elevation or depression and pathological Q waves suggesting myocardial injury or ischemia.",
21
+ "recommendation": "⚠️ IMMEDIATE clinical review strongly recommended. Activate emergency cardiac protocols if STEMI suspected.",
22
  },
23
+ "STTC": {
 
 
24
  "summary": "ST/T-Wave Change",
 
 
 
 
 
 
25
  "urgency": "Moderate",
26
+ "description": "Non-specific ST-segment and/or T-wave changes detected. These findings can represent myocardial ischemia, electrolyte abnormalities, medication effects, or normal variants. Clinical context is essential for interpretation.",
27
+ "recommendation": "Clinical correlation advised. Serial ECGs and lab results may be warranted.",
28
  },
29
+ "CD": {
 
 
30
  "summary": "Conduction Disturbance",
 
 
 
 
 
31
  "urgency": "Moderate",
32
+ "description": "The ECG shows features consistent with a conduction disturbance, such as bundle branch block or AV conduction delay. These findings alter the normal electrical pathway through the ventricles.",
33
+ "recommendation": "Cardiology consultation recommended to evaluate hemodynamic significance and underlying etiology.",
34
  },
 
 
 
 
 
 
 
 
 
 
 
 
35
  }
36
 
37
+ DISCLAIMER = "⚠️ DISCLAIMER: For educational and research purposes only. Does not constitute medical advice. Always consult a qualified healthcare provider."
 
 
 
38
 
39
 
40
  def match_condition(label):
41
+ """Return condition info dict for a given label string."""
42
+ label_upper = (label or "").upper()
43
+ for key, val in CONDITION_MAP.items():
44
+ if key in label_upper:
45
+ return {**val, "key": key}
46
+ return {
47
+ "key": "UNKNOWN",
48
+ "summary": "Unrecognized Pattern",
49
+ "urgency": "Unknown",
50
+ "description": "The model returned a label that could not be mapped to a known cardiac condition.",
51
+ "recommendation": "Manual review by a qualified cardiologist is recommended.",
52
+ }
53
 
54
 
55
+ def generate_explanation(label, confidence, full_result=None):
56
  """
57
+ Generate the clinical explanation text block.
58
+ If full_result (from Claude API) is available, use its rich findings.
59
+ Otherwise fall back to template text.
 
 
 
 
 
60
  """
61
+ condition = match_condition(label)
62
+
63
+ if full_result:
64
+ # Use Claude's own clinical findings text
65
+ findings = full_result.get("findings", condition["description"])
66
+ recommendation = full_result.get("recommendation", condition["recommendation"])
67
+ urgency = full_result.get("urgency", condition["urgency"])
68
+ full_name = full_result.get("full_name", condition["summary"])
69
+ else:
70
+ findings = condition["description"]
71
+ recommendation = condition["recommendation"]
72
+ urgency = condition["urgency"]
73
+ full_name = condition["summary"]
74
 
75
+ text = (
76
+ f"Condition: {full_name}\n"
77
  f"Raw Model Label: {label}\n"
78
  f"Confidence: {confidence:.1f}%\n"
79
+ f"Urgency: {urgency}\n\n"
80
+ f"{findings}\n\n"
81
+ f"Recommendation: {recommendation}\n\n"
82
  f"{DISCLAIMER}"
83
  )
84
 
85
+ return text, urgency
86
 
87
 
88
  def format_top3(results):
89
+ """Format the confidence breakdown for display."""
90
  lines = ["Top Predictions:"]
91
  for i, r in enumerate(results[:3], 1):
92
+ label = r["label"]
93
+ score = r["score"] * 100
94
+ bar = "█" * int(score / 5)
95
+ lines.append(f" {i}. {label}: {score:.1f}% {bar}")
96
  return "\n".join(lines)