Spaces:
Runtime error
Runtime error
Download src/data/query_entity_export.py from anurag-raapid/query-entity-export: direct link, hf CLI and curl.
- Browser
- Download file 10.1 kB
-
https://huggingface.co/spaces/anurag-raapid/query-entity-export/resolve/main/src/data/query_entity_export.py
- Command line
-
hf download hf://spaces/anurag-raapid/query-entity-export/src/data/query_entity_export.py
-
curl -L -o query_entity_export.py https://huggingface.co/spaces/anurag-raapid/query-entity-export/resolve/main/src/data/query_entity_export.py
10.1 kB
| """Export QuickUMLS-detected CUIs and clinical-AUI-validated AUIs for query texts.""" | |
| from __future__ import annotations | |
| import json | |
| from dataclasses import asdict, dataclass | |
| from pathlib import Path | |
| from typing import Iterable, Iterator | |
| from config.paths import ( | |
| CUI_TO_VALID_AUI_JSON, | |
| CUI_TO_VALID_AUI_PARQUET, | |
| GRAPH_ID_MAPS_PARQUET, | |
| MRCONSO_CLINICAL_PARQUET, | |
| MRCONSO_CLINICAL_PARQUET_DEFAULT, | |
| ) | |
| from src.data.query_entity_linker import QueryEntityLinker | |
| def _resolve_mrconso_parquet(path: Path | None) -> Path: | |
| if path is not None: | |
| candidate = path | |
| elif MRCONSO_CLINICAL_PARQUET.exists(): | |
| candidate = MRCONSO_CLINICAL_PARQUET | |
| else: | |
| candidate = MRCONSO_CLINICAL_PARQUET_DEFAULT | |
| if not candidate.exists(): | |
| raise FileNotFoundError( | |
| f"Clinical MRCONSO parquet not found: {candidate}. " | |
| "Copy mrconso_clinical.parquet to data/processed/graph/ or set " | |
| "ICD_MRCONSO_CLINICAL_PARQUET." | |
| ) | |
| return candidate | |
| class ValidAuiRecord: | |
| cui: str | |
| aui: str | |
| graph_idx: int | |
| str: str | |
| sab: str | |
| def _cui_to_valid_aui_sql( | |
| *, | |
| id_maps_parquet: Path, | |
| mrconso_parquet: Path, | |
| ) -> str: | |
| return f""" | |
| WITH graph_auis AS ( | |
| SELECT | |
| cui, | |
| REPLACE(node_id, 'AUI:', '') AS aui, | |
| idx, | |
| degree, | |
| str, | |
| sab | |
| FROM read_parquet('{id_maps_parquet.as_posix()}') | |
| WHERE kind = 'AUI' AND cui IS NOT NULL | |
| ), | |
| ranked AS ( | |
| SELECT | |
| g.cui, | |
| g.aui, | |
| g.idx, | |
| g.str, | |
| g.sab, | |
| ROW_NUMBER() OVER ( | |
| PARTITION BY g.cui | |
| ORDER BY g.degree DESC, g.idx | |
| ) AS rn | |
| FROM graph_auis g | |
| INNER JOIN read_parquet('{mrconso_parquet.as_posix()}') m | |
| ON g.aui = m.AUI | |
| ) | |
| SELECT cui, aui, idx AS graph_idx, str, sab | |
| FROM ranked | |
| WHERE rn = 1 | |
| """ | |
| def build_cui_to_valid_aui_parquet( | |
| output: Path = CUI_TO_VALID_AUI_PARQUET, | |
| *, | |
| id_maps_parquet: Path = GRAPH_ID_MAPS_PARQUET, | |
| mrconso_parquet: Path | None = None, | |
| ) -> int: | |
| """Build the CUI→AUI parquet cache via DuckDB + clinical MRCONSO parquet.""" | |
| mrconso = _resolve_mrconso_parquet(mrconso_parquet) | |
| if not id_maps_parquet.exists(): | |
| raise FileNotFoundError(f"Graph id maps not found: {id_maps_parquet}") | |
| import duckdb | |
| output.parent.mkdir(parents=True, exist_ok=True) | |
| con = duckdb.connect() | |
| con.execute( | |
| f""" | |
| COPY ( | |
| {_cui_to_valid_aui_sql( | |
| id_maps_parquet=id_maps_parquet, | |
| mrconso_parquet=mrconso, | |
| )} | |
| ) TO '{output.as_posix()}' (FORMAT PARQUET) | |
| """ | |
| ) | |
| con.close() | |
| import polars as pl | |
| return pl.scan_parquet(output).select(pl.len()).collect().item() | |
| def build_cui_to_valid_aui_map( | |
| *, | |
| id_maps_parquet: Path = GRAPH_ID_MAPS_PARQUET, | |
| mrconso_parquet: Path | None = None, | |
| ) -> dict[str, ValidAuiRecord]: | |
| """Map each CUI to its highest-degree AUI present in clinical MRCONSO.""" | |
| build_cui_to_valid_aui_parquet( | |
| id_maps_parquet=id_maps_parquet, | |
| mrconso_parquet=mrconso_parquet, | |
| ) | |
| lookup = CuiToValidAuiLookup(CUI_TO_VALID_AUI_PARQUET) | |
| try: | |
| import polars as pl | |
| cuis = ( | |
| pl.scan_parquet(CUI_TO_VALID_AUI_PARQUET) | |
| .select("cui") | |
| .collect() | |
| .get_column("cui") | |
| .to_list() | |
| ) | |
| return lookup.lookup_many(cuis) | |
| finally: | |
| lookup.close() | |
| def save_cui_to_valid_aui_map( | |
| path: Path, | |
| mapping: dict[str, ValidAuiRecord], | |
| ) -> None: | |
| path.parent.mkdir(parents=True, exist_ok=True) | |
| payload = {cui: asdict(rec) for cui, rec in mapping.items()} | |
| path.write_text(json.dumps(payload), encoding="utf-8") | |
| def load_cui_to_valid_aui_map( | |
| path: Path = CUI_TO_VALID_AUI_JSON, | |
| ) -> dict[str, ValidAuiRecord]: | |
| payload = json.loads(path.read_text(encoding="utf-8")) | |
| return { | |
| cui: ValidAuiRecord( | |
| cui=str(rec["cui"]), | |
| aui=str(rec["aui"]), | |
| graph_idx=int(rec["graph_idx"]), | |
| str=str(rec.get("str", "")), | |
| sab=str(rec.get("sab", "")), | |
| ) | |
| for cui, rec in payload.items() | |
| } | |
| class CuiToValidAuiLookup: | |
| """Polars-backed lookup over ``cui_to_valid_aui.parquet`` (memory-safe).""" | |
| def __init__(self, parquet_path: Path = CUI_TO_VALID_AUI_PARQUET) -> None: | |
| if not parquet_path.exists(): | |
| raise FileNotFoundError(f"CUI→AUI parquet not found: {parquet_path}") | |
| import polars as pl | |
| self._parquet_path = parquet_path | |
| self._scan = pl.scan_parquet(parquet_path) | |
| def count(self) -> int: | |
| import polars as pl | |
| return self._scan.select(pl.len()).collect().item() | |
| def lookup_many(self, cuis: Iterable[str]) -> dict[str, ValidAuiRecord]: | |
| unique = list(dict.fromkeys(str(c) for c in cuis if c)) | |
| if not unique: | |
| return {} | |
| import polars as pl | |
| df = self._scan.filter(pl.col("cui").is_in(unique)).collect() | |
| out: dict[str, ValidAuiRecord] = {} | |
| for row in df.iter_rows(named=True): | |
| cui = str(row["cui"]) | |
| out[cui] = ValidAuiRecord( | |
| cui=cui, | |
| aui=str(row["aui"]), | |
| graph_idx=int(row["graph_idx"]), | |
| str=str(row["str"]) if row["str"] is not None else "", | |
| sab=str(row["sab"]) if row["sab"] is not None else "", | |
| ) | |
| return out | |
| def close(self) -> None: | |
| return None | |
| def ensure_cui_to_valid_aui_lookup( | |
| *, | |
| parquet_path: Path = CUI_TO_VALID_AUI_PARQUET, | |
| rebuild: bool = False, | |
| id_maps_parquet: Path = GRAPH_ID_MAPS_PARQUET, | |
| mrconso_parquet: Path | None = None, | |
| ) -> CuiToValidAuiLookup: | |
| if not parquet_path.exists() or rebuild: | |
| build_cui_to_valid_aui_parquet( | |
| parquet_path, | |
| id_maps_parquet=id_maps_parquet, | |
| mrconso_parquet=mrconso_parquet, | |
| ) | |
| return CuiToValidAuiLookup(parquet_path) | |
| def ensure_cui_to_valid_aui_map( | |
| *, | |
| path: Path = CUI_TO_VALID_AUI_JSON, | |
| rebuild: bool = False, | |
| id_maps_parquet: Path = GRAPH_ID_MAPS_PARQUET, | |
| mrconso_parquet: Path | None = None, | |
| ) -> dict[str, ValidAuiRecord]: | |
| lookup = ensure_cui_to_valid_aui_lookup( | |
| rebuild=rebuild, | |
| id_maps_parquet=id_maps_parquet, | |
| mrconso_parquet=mrconso_parquet, | |
| ) | |
| try: | |
| import polars as pl | |
| cuis = ( | |
| pl.scan_parquet(CUI_TO_VALID_AUI_PARQUET) | |
| .select("cui") | |
| .collect() | |
| .get_column("cui") | |
| .to_list() | |
| ) | |
| return lookup.lookup_many(cuis) | |
| finally: | |
| lookup.close() | |
| def detect_entities_for_text( | |
| text: str, | |
| *, | |
| linker: QueryEntityLinker, | |
| lookup: CuiToValidAuiLookup, | |
| ) -> list[dict[str, object]]: | |
| """Return kept CUI/AUI entities for one query text.""" | |
| linked = linker.link_cuis(text) | |
| records = lookup.lookup_many(cui for cui, _ in linked) | |
| entities: list[dict[str, object]] = [] | |
| for cui, confidence in linked: | |
| record = records.get(cui) | |
| if record is None: | |
| continue | |
| entities.append( | |
| { | |
| "cui": record.cui, | |
| "aui": record.aui, | |
| "confidence": round(float(confidence), 4), | |
| "graph_idx": record.graph_idx, | |
| "str": record.str, | |
| "sab": record.sab, | |
| } | |
| ) | |
| return entities | |
| def export_entity_detections( | |
| rows: list[dict[str, object]], | |
| *, | |
| linker: QueryEntityLinker, | |
| lookup: CuiToValidAuiLookup, | |
| text_key: str = "query_text", | |
| include_code: bool = True, | |
| ) -> list[dict[str, object]]: | |
| """Build export records aligned to input JSONL rows.""" | |
| return list( | |
| iter_entity_detection_entries( | |
| rows, | |
| linker=linker, | |
| lookup=lookup, | |
| text_key=text_key, | |
| include_code=include_code, | |
| ) | |
| ) | |
| def iter_entity_detection_entries( | |
| rows: list[dict[str, object]], | |
| *, | |
| linker: QueryEntityLinker, | |
| lookup: CuiToValidAuiLookup, | |
| text_key: str = "query_text", | |
| include_code: bool = True, | |
| ) -> Iterator[dict[str, object]]: | |
| for row in rows: | |
| text = str(row.get(text_key, "")).strip() | |
| entry: dict[str, object] = {"query_text": text} | |
| if include_code and "code" in row: | |
| entry["code"] = row["code"] | |
| entry["entities"] = detect_entities_for_text( | |
| text, | |
| linker=linker, | |
| lookup=lookup, | |
| ) | |
| yield entry | |
| def write_entity_detections_json( | |
| entries: Iterable[dict[str, object]], | |
| output: Path, | |
| *, | |
| pretty: bool = False, | |
| ) -> tuple[int, int, int]: | |
| """Stream entries to JSON without holding the full payload in memory.""" | |
| output.parent.mkdir(parents=True, exist_ok=True) | |
| n_entries = 0 | |
| with_entities = 0 | |
| total_entities = 0 | |
| with open(output, "w", encoding="utf-8") as f: | |
| f.write('{\n "entries": [\n') | |
| first = True | |
| for entry in entries: | |
| if not first: | |
| f.write(",\n") | |
| else: | |
| first = False | |
| f.write(" ") | |
| if pretty: | |
| rendered = json.dumps(entry, ensure_ascii=False, indent=2) | |
| f.write(rendered.replace("\n", "\n ")) | |
| else: | |
| f.write(json.dumps(entry, ensure_ascii=False)) | |
| n_entries += 1 | |
| n_ent = len(entry.get("entities", [])) | |
| if n_ent: | |
| with_entities += 1 | |
| total_entities += n_ent | |
| f.write("\n ]\n}\n") | |
| return n_entries, with_entities, total_entities | |