Universal Multi-Lab Mapping
This mapper must work for a laboratory that appears once, not only for a laboratory that has a hand-maintained local map. A local map is helpful evidence, but it is never the sole route to a normal laboratory result.
The Core Idea
LOINC codes are not selected from a flat list of similar names. A laboratory row states facts that can be compared to the six LOINC axes:
| Row fact | LOINC axis it constrains | Example |
|---|---|---|
Creatinine, Urine |
Component and System | Creatinine; Urine |
Sodium 24 hour urine |
Component, Time, System | Sodium; 24H; Urine |
LDL calculated |
Component and Method | LDL cholesterol; Calculated |
LDLP, nmol/L |
Component and Property/unit family | particle count; substance concentration |
The mapper extracts those facts before broad semantic retrieval. It then asks
the pinned LOINC release for candidates whose six-axis signatures agree with
the report. This works whether the source laboratory is Labcorp, Quest, a
hospital laboratory, or an unseen laboratory name.
LabObservation
-> AxisFactExtractor
-> UniversalSemanticIndex (release-derived component and axis retrieval)
-> deterministic aliases / FTS / Parts
-> ScispaCy + local UMLS + FAISS/SapBERT recall
-> hard UCUM and six-axis validation
-> AxisSignature grouping
-> ranker and calibrated confidence gate
The original OCR text is never replaced. Every fact, candidate, rejected
sibling, and final decision is retained in MappingResult.provenance.
Example: Creatinine In Urine
These labels should mean the same thing even when they come from different laboratories:
Creatinine, Urine
Urine creatinine
CREATININE - URINE
For Creatinine, Urine with mg/dL, the extractor retains facts similar to:
{
"component": [{"value": "Creatinine", "source": "raw_name"}],
"system": [{"value": "urine", "source": "raw_name"}]
}
The universal index retrieves active result-row candidates from the local
LOINC release. The hard validator rejects serum/plasma siblings because the
row explicitly says urine. It also rejects a 24-hour urine sibling unless the
row explicitly states 24 hour or equivalent duration. It does not need a
Wellspan or Quest registry record to reach the ordinary point-in-time urine
candidate.
collection_specimen="Blood, Venous" is intentionally different from
specimen="Urine". A collection source says how material was obtained; it
does not prove whether the analyzer used whole blood, serum, plasma, or urine.
The mapper keeps collection evidence in provenance and only fills the LOINC
System axis from an analytical specimen stated by the row or its label.
What The Universal Index Contains
UniversalSemanticIndex is generated once per mapper worker from the pinned
SQLite LOINC catalog. It does not create a second authoritative terminology
database. It uses active laboratory Observation/Both terms plus the
catalog's release-compiled sources:
- LOINC term names and Components.
- LOINC Parts and primary/supplementary Part links.
- Consumer names,
RELATEDNAMES2, and enabled linguistic variants. - Release aliases and token/FTS retrieval.
- UMLS aliases only as additional candidate-recall evidence.
COMMON_TEST_RANK is a small tie-breaker after compatibility checks. It does
not suppress rare tests and never overrides a unit, specimen, property, time,
scale, or method conflict.
Candidate Signatures
Several LOINC codes can have similar text but clinically different meanings. Before confidence scoring, candidates are grouped by:
Component | Property | Time | System | Scale | Method
The strongest code within an indistinguishable signature is retained for ranking, while the full group remains in provenance. A candidate in a different signature remains only if it is compatible with every explicit report fact. This makes a failure explainable:
Creatininein serum is rejected when the row says urine.- A 24-hour candidate is rejected without an explicit collection duration.
- A calculated LDL candidate is rejected when the row states direct assay.
- LDL particle candidates are rejected for an
mg/dLmass-concentration row.
If two safe, clinically distinct signatures remain and the row lacks the fact
that separates them, the correct behavior is abstain, not a guessed code.
CLASS And Panel Context
LAB_RESULT is a hard scope: only active laboratory (CLASSTYPE=1) result
terms are eligible. LOINC CLASS, a panel name, and a report section are not
clinical axes. They are advisory ranking/retrieval evidence only.
For example, an OCR parser can incorrectly put CRP, TSH, or an endocrine
result under a CBC or CMP section. The mapper records the class disagreement,
searches laboratory-wide when needed, and lets unit/property/specimen/method
validation decide safety. A class mismatch can never make an otherwise valid
laboratory candidate unsafe by itself.
Governed Registry Layers
The active registry supports two explicit layers:
| Layer | Purpose | Runtime behavior |
|---|---|---|
universal_mapping_templates |
Expert-confirmed, clinically invariant display forms with explicit constraints | Available for every laboratory after unit and axis validation |
source_mapping_evidence |
Local test IDs, vendor methods, or report displays that need source context | Participates only for the named laboratory; a matching source_test_id may disambiguate a documented distinct assay, while lab-name-only evidence only boosts retrieval and cannot suppress a universal template |
An approved universal template can retain
supporting_source_laboratory for audit without requiring that laboratory at
runtime. Entries with source_laboratory remain source evidence until an
expert explicitly promotes them.
When both layers propose different active codes, the mapper evaluates both
through the same unit and six-axis safety gate. An exact local
source_test_id, when the report actually supplies one, can outrank a
universal template for a clinically distinct documented assay. Without that
identifier, a verified clinician universal template outranks source-laboratory
display evidence. A laboratory name alone is never a veto over source-neutral
LOINC retrieval.
Audit the current registry without changing it:
python -m loinc_mapper audit-registry-scopes `
--registry config/mapping_registry.json `
--output evaluation/registry_scope_audit.json
Export the same records in the governed two-layer format when a main project is ready to consume it:
python -m loinc_mapper export-layered-registry `
--registry config/mapping_registry.json `
--output config/mapping_registry.layered.json
Neither command changes the live registry or promotes a mapping. The legacy
mappings format remains supported so an existing integration can upgrade
without a big-bang configuration migration.
Review-Gated Learning
Uncertain rows go to a review queue with their axis facts, signatures, candidates, units, source laboratory, and full provenance. Cluster recurring rows before review:
python -m loinc_mapper cluster-review-queue `
--input evaluation/review_queue.jsonl `
--output evaluation/review_clusters.json
Clusters from one source default to source_evidence. A recurring surface from
multiple sources may be proposed as universal_template, but only a reviewer
can approve it. No cluster creates a live mapping automatically.
Debugging A Row
For an abstained or rejected row, inspect these fields before adding any registry record:
result = mapper.map(observation)
stages = result.provenance["stages"]
print(stages["universal_axis_facts"])
print(stages["axis_signature_groups"])
print(stages["registry_evidence"])
print(stages["routing_behavior"])
print(result.candidates)
print(result.axis_evidence)
print(result.unit_evidence)
If the needed specimen, duration, method, or unit is absent from the report, adding a broad global alias is not the solution. Keep the result in review or obtain the laboratory's test definition. This preserves safety across the many laboratories the fax automation system will encounter.
registry_evidence separates universal_templates from
source_mapping_evidence, even if an entry later fails validation or loses to
another candidate. routing_behavior explicitly records that class was
advisory and that laboratory-wide retrieval remained enabled.
Validation Status
The source-neutral regression fixture verifies ordinary rows with their original source and a previously unseen source have identical results. It is a software regression test, not a clinical accuracy claim. The production gate remains at least 95% accepted precision and at least 85% coverage on an expert-reviewed multi-laboratory holdout, with zero dangerous unit/property/specimen false positives in the safety suite.