medical-schedule-ocr / validate.py
JoselinMaria's picture
Add validate.py accuracy checker
1a0a109 verified
Raw
History Blame Contribute Delete
2.69 kB
"""
validate.py β€” Compare extracted CSV/JSON against ground truth.
Prints per-field accuracy and a detailed diff.
"""
import json, sys, os
from difflib import SequenceMatcher
def similarity(a: str, b: str) -> float:
return SequenceMatcher(None, a.lower().strip(), b.lower().strip()).ratio()
def main(gt_path: str, extracted_path: str):
with open(gt_path) as f:
gt = json.load(f)
with open(extracted_path) as f:
ext = json.load(f)["rows"]
FIELDS = ["begin_time", "end_time", "patient_name", "dob"]
total = {k: 0 for k in FIELDS}
exact = {k: 0 for k in FIELDS}
fuzzy_sum = {k: 0.0 for k in FIELDS}
matched = 0
print(f"Ground truth rows: {len(gt)} Extracted rows: {len(ext)}\n")
print(f"{'PG':>3} {'FIELD':<15} {'EXPECTED':<28} {'GOT':<28} {'SIM':>5} {'OK':>3}")
print("─" * 90)
for g in gt:
# find matching row by page + approximate begin_time
candidates = [e for e in ext if e["page"] == g["page"]]
best, best_sim = None, 0
for c in candidates:
s = similarity(c["begin_time"], g["begin_time"])
s += similarity(c["patient_name"], g["patient_name"])
if s > best_sim:
best_sim = s
best = c
if best is None:
print(f" p{g['page']} β€” NO MATCH for {g['begin_time']} {g['patient_name']}")
for k in FIELDS:
total[k] += 1
continue
matched += 1
for k in FIELDS:
total[k] += 1
sim = similarity(str(best.get(k, "")), str(g[k]))
fuzzy_sum[k] += sim
ok = sim >= 0.85
if ok:
exact[k] += 1
flag = "βœ“" if ok else "βœ—"
if not ok: # only print mismatches
print(f"{g['page']:>3} {k:<15} {str(g[k]):<28} {str(best.get(k,'')):28} {sim:5.2f} {flag:>3}")
print("\n" + "=" * 60)
print(f"{'FIELD':<15} {'EXACT':>6} {'TOTAL':>6} {'ACC':>7} {'FUZZY_AVG':>9}")
print("─" * 60)
for k in FIELDS:
acc = exact[k] / total[k] * 100 if total[k] else 0
favg = fuzzy_sum[k] / total[k] if total[k] else 0
print(f"{k:<15} {exact[k]:>6} {total[k]:>6} {acc:>6.1f}% {favg:>9.3f}")
overall = sum(exact.values()) / sum(total.values()) * 100 if sum(total.values()) else 0
print(f"\nOverall field accuracy : {overall:.1f}%")
print(f"Row match rate : {matched}/{len(gt)}")
if __name__ == "__main__":
gt = sys.argv[1] if len(sys.argv) > 1 else "./test_pdfs/ground_truth.json"
ext = sys.argv[2] if len(sys.argv) > 2 else "./output/schedule_extracted.json"
main(gt, ext)