Files
medtech-hackathon/scripts/proof/normalize_all.py
T
admins 14462e8c10 feat(etl): нормализация (каскад), валидация (8 правил), Vision-fallback
- etl/normalize/matcher.py — каскад код→точное→эмбеддинги→fuzzy→unmatched
- etl/validate/rules.py — правила ТЗ §4.4; проверено: 15588 done / 11 needs_review
- etl/extractors/vision.py — Gemini Vision для битых/нестандартных PDF
- замер % автонормализации — после установки модели эмбеддингов

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 16:46:18 +05:00

60 lines
2.5 KiB
Python

"""Замер автонормализации: извлечь весь архив, сопоставить со справочником, дать %."""
import sys
from collections import Counter
from pathlib import Path
REPO = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(REPO))
from sentence_transformers import SentenceTransformer
from etl.dictionary import load_dictionary
from etl.extractors import extract
from etl.normalize import Matcher
# --- собрать все извлечённые позиции ---
rows: list[tuple[str, str | None]] = []
for path in sorted((REPO / "data/raw").glob("*")):
for row in extract(str(path)).rows:
rows.append((row.service_name_raw, row.service_code_source))
print(f"позиций всего: {len(rows)}")
# дедуп по (имя, код) — одинаковые строки сопоставляем один раз
occurrences: Counter[tuple[str, str | None]] = Counter(rows)
keys = list(occurrences)
print(f"уникальных (имя, код): {len(keys)}")
services = load_dictionary(REPO / "data/reference/dictionary.xlsx")
print("загружаю модель эмбеддингов (первый раз — скачивание)…")
embedder = SentenceTransformer("paraphrase-multilingual-MiniLM-L12-v2")
matcher = Matcher(services, embedder=embedder)
print("сопоставляю…")
results = matcher.match_batch([k[0] for k in keys], [k[1] for k in keys])
by_method: Counter[str | None] = Counter()
total = matched = 0
samples: dict[str | None, list] = {}
canon_by_id = {s.service_id: s.name_ru for s in services}
for key, res in zip(keys, results, strict=True):
count = occurrences[key]
total += count
by_method[res.method] += count
if res.service_id:
matched += count
bucket = samples.setdefault(res.method, [])
if len(bucket) < 5:
bucket.append((key[0], canon_by_id.get(res.service_id, "—"), round(res.confidence, 2)))
print(f"\nАВТОНОРМАЛИЗАЦИЯ: {matched}/{total} = {100 * matched / total:.0f}% (цель ТЗ ≥70%)")
for method in ("code", "exact", "embedding", "fuzzy", None):
print(f" {method or 'unmatched':10}: {by_method[method]} строк")
print("\nпримеры сопоставлений:")
for method in ("code", "exact", "embedding", "fuzzy"):
for raw, canon, score in samples.get(method, [])[:3]:
print(f" [{method:9}] «{raw[:36]}» → «{canon[:36]}» ({score})")
print("\nпримеры unmatched:")
for raw, _canon, _score in samples.get(None, [])[:6]:
print(f" «{raw[:52]}»")