# Как нормализовать медицинские данные

> Учебное пособие: превратите медицинские данные из множества источников в одну сравнимую форму — поиск по LOINC, единицы UCUM, даты ISO, эталонные диапазоны, отсутствие дубликатов при повторных попытках и проверка по европейскому лабораторному профилю.

Source: https://developers.anpheros.com/ru/guides/normalize-medical-data

**Нормализация медицинских данных означает преобразование значений, поступающих в различных формах, в одну сравнимую форму: один и тот же код для одного и того же теста, одна и та же система единиц измерения, даты в одном формате, каждое значение с его эталонным диапазоном и источником, а также отсутствие дубликатов.** В этом учебном пособии показано, как это сделать до того, как данные попадут в запись HL7 FHIR R4: поиск кодов LOINC и ATC, использование единиц UCUM, отказ от неоднозначных дат, сохранение диапазонов, избежание дубликатов при повторных попытках и проверка результата по европейскому лабораторному профилю. Каждый показанный ответ получен из Anpheros API.

## Почему это важно

Один и тот же результат HbA1c может поступить к вам в виде "HbA1c 6,4 %", "Hemoglobina glicozilata: 6.4", "Glycated haemoglobin 46 mmol/mol" или только в виде лабораторного кода, понятного одной конкретной лаборатории. Пока эти значения не будут представлены в виде одного кода с одной единицей измерения:

- график HbA1c пациента за определенный период будет отображать три несвязанные линии;
- оповещение "HbA1c выше 6,5 %" пропустит два из значений;
- модель ИИ увидит четыре факта вместо одной тенденции;
- другая система, получающая запись, не сможет ее интерпретировать.

Нормализация должна выполняться в точке входа данных в запись, единожды, а не в каждом приложении, которое считывает ее позже.

## Что такое нормализованное значение

| Часть | Нормализованная форма | Пример |
|---|---|---|
| Что было измерено | код из стандартной системы | LOINC `4548-4` (HbA1c), RxNorm `6809` (metformin), ICD-10 `E11` (type 2 diabetes) |
| Значение | число или текст, только если результат не является числовым | `6.4` |
| Единица измерения | UCUM | `%`, `mg/dL`, `mm[Hg]`, `kg` |
| Когда | дата-время в формате ISO 8601, с часовым поясом, если время имеет значение | `2026-10-01T07:30:00Z` |
| Нормальный диапазон | нижняя и верхняя границы в той же единице измерения | `4.0`–`5.6` |
| Источник | кто произвел и из какой системы | `import` из `labx` |
| Идентификатор | что-то, что делает повтор узнаваемым | ключ идемпотентности или идентификатор источника |

Какие системы кодирования соответствуют каким типам ресурсов, указано в разделе [FHIR code systems](https://developers.anpheros.com/ru/guides/fhir-code-systems).

## Шаг 1: Поиск кода

Ищите терминологию платформы по английскому или румынскому названию, или по аббревиатуре:

```bash
curl "https://platform.anpheros.com/v1/terminology/loinc?q=hba1c" -H "Authorization: Bearer $KEY"
curl "https://platform.anpheros.com/v1/terminology/loinc?q=hemoglobina%20glicozilata" -H "Authorization: Bearer $KEY"
```

Оба варианта возвращают одно и то же понятие:

```json
{"data": [{"system": "http://loinc.org", "code": "4548-4", "display": "Hemoglobin A1c/Hemoglobin.total in Blood",
           "display_ro": "Hemoglobină glicozilată (HbA1c)", "category": "laboratory", "unit": "%"}], "total": 1}
```

Для лекарств [Anpheros Search](https://developers.anpheros.com/ru/guides/anpheros-search) находит вещество по названию или бренду и предоставляет его RxNorm ingredient code и EMA product numbers:

```bash
curl "https://platform.anpheros.com/v1/search?q=metformin&type=treatment" -H "Authorization: Bearer $KEY"
```

Поле `unit` — это единица измерения, которую ожидает LOINC для данного теста. Используйте его для проверки полученных данных. Создайте сопоставление собственных имен каждого источника с кодами один раз для каждого источника и сохраните его в вашей интеграции. Не угадывайте во время записи: когда имя неоднозначно и ничего в источнике не проясняет, оставьте его без сопоставления и обратитесь к источнику. [Как импортировать лабораторные результаты в запись FHIR](https://developers.anpheros.com/ru/guides/import-lab-results-fhir) показывает, как хранятся и сообщаются некартографированные лабораторные маркеры.

## Шаг 2: Запись с кодами, единицами измерения и диапазонами

```bash
curl -X POST "https://platform.anpheros.com/v1/patients/$PID/observations" -H "Authorization: Bearer $KEY" \
     -H "Content-Type: application/json" -H "Idempotency-Key: lab-77-hba1c" -d '{
  "code": "4548-4", "category": "laboratory",
  "value": 6.4, "unit": "%", "effective_at": "2026-10-01T07:30:00Z",
  "reference_range": {"low": 4.0, "high": 5.6},
  "author_type": "import"
}'
```

```json
{"id": "3296315c-…", "code": "4548-4", "system": "http://loinc.org", "display": "Hemoglobin A1c/Hemoglobin.total in Blood",
 "category": "laboratory", "effective_at": "2026-10-01T07:30:00Z", "value": 6.4, "unit": "%",
 "reference_range": {"low": 4.0, "high": 5.6}, "status": "final", "fhir": "Observation/3296315c-…"}
```

Когда вы отправляете только код, платформа заполняет стандартное имя LOINC как в REST API, так и в FHIR API (`/fhir/R4`), поэтому каждое приложение читает одно и то же `display`. `display`, который вы отправляете самостоятельно, сохраняется как есть.

Сохраняйте оригинальную формулировку. Для лабораторного отчета `code.text` в FHIR Observation содержит название лаборатории ("Hemoglobina glicozilata (HbA1c)") рядом с кодировкой LOINC. Пациент по-прежнему узнает свой бумажный отчет, и ничего не будет потеряно, если сопоставление когда-либо будет поставлено под сомнение.

## Шаг 3: Единицы измерения

Используйте коды UCUM: `mg/dL`, `mmol/L`, `%`, `mm[Hg]`, `kg`, `cm`, `/min`, `Cel`. Два правила помогают избежать большинства ошибок:

- **Не конвертируйте молча.** Если лаборатория сообщает уровень глюкозы в `mmol/L`, а ваше приложение отображает `mg/dL`, сохраняйте то, что измерила лаборатория, и конвертируйте при отображении. Сохраненное значение остается прослеживаемым до отчета.
- **Проверяйте единицу измерения по коду.** Коды LOINC специфичны: HbA1c в `%` — это `4548-4`, в то время как HbA1c в `mmol/mol` имеет другой код LOINC. Значение, единица измерения которого не соответствует ожидаемой единице измерения кода, является ошибкой сопоставления, а не проблемой конвертации.

Anpheros не конвертирует единицы измерения за вас; он сохраняет ту единицу, которую вы отправляете.

## Шаг 4: Даты

Преобразуйте даты в формат ISO 8601 перед записью, используя известный формат источника. `01.10.2026` — это 1 октября в Румынии и многих европейских странах, но `10/01/2026` — это 1 октября только в Соединенных Штатах. API отклоняет все, что не соответствует ISO, вместо того чтобы угадывать:

```json
{"error": {"type": "invalid", "message": "Input should be a valid datetime or date, invalid character in year (at effective_at)", "field": "effective_at"}}
```

Лабораторный коннектор является исключением: он принимает `DD.MM.YYYY` и `DD/MM/YYYY`, потому что знает, что читает европейский отчет.

Для измерений, где время суток имеет значение (артериальное давление, уровень глюкозы, дозы лекарств), отправляйте время с его часовым поясом. Показание в 23:30 в Бухаресте приходится на предыдущий день по UTC, и пациент ожидает его в тот день, когда он был сделан.

## Шаг 5: отсутствие дубликатов

Дубликаты возникают из-за повторных попыток, отправки одного и того же отчета дважды и получения одного и того же значения по двум разным каналам.

- **Повторные попытки:** отправляйте `Idempotency-Key`, который идентифицирует значение в источнике, например, идентификатор отчета и тест. Повторная отправка того же запроса с тем же ключом возвращает первый ответ и не сохраняет ничего нового. SDK добавляют единицу к каждому созданию.
- **Один и тот же отчет дважды:** лабораторный коннектор создает стабильный идентификатор для каждой строки из отчета, кода, даты и значения, а также пропускает уже импортированные строки.
- **Два канала:** сохраняйте собственный идентификатор исходной системы с `X-Anpheros-Origin-Id` (или в `identifier` в ресурсах FHIR) и ищите его перед записью значений, которые могут вернуться по другому пути.

## Шаг 6: Проверка по профилю

Для лабораторных результатов запросите у платформы, насколько ресурс соответствует профилю HL7 Europe для лабораторных отчетов, прежде чем полагаться на него:

```bash
curl -X POST "https://platform.anpheros.com/fhir/R4/Observation/\$validate?profile=eu-lab" -H "Authorization: Bearer $KEY" \
     -H "Content-Type: application/fhir+json" -d '{
  "resourceType": "Observation", "status": "final",
  "category": [{"coding": [{"system": "http://terminology.hl7.org/CodeSystem/observation-category", "code": "laboratory"}]}],
  "code": {"text": "HbA1c"}, "subject": {"reference": "Patient/'$PID'"},
  "valueQuantity": {"value": 6.4, "unit": "%"}
}'
```

Этот ненормализованный результат получает точный список:

```json
{"resourceType": "OperationOutcome", "issue": [
  {"severity": "error", "code": "invalid", "diagnostics": "Observation.code must carry a LOINC coding.", "expression": ["Observation.code"]},
  {"severity": "error", "code": "required", "diagnostics": "Observation.effective[x] is required.", "expression": ["Observation.effective"]},
  {"severity": "warning", "code": "business-rule", "diagnostics": "valueQuantity should carry a UCUM system and code.", "expression": ["Observation.valueQuantity"]},
  {"severity": "warning", "code": "informational", "diagnostics": "No referenceRange: interpretation will need the laboratory's ranges.", "expression": ["Observation.referenceRange"]},
  {"severity": "information", "code": "informational", "diagnostics": "No performer (laboratory) recorded.", "expression": ["Observation.performer"]},
  {"severity": "information", "code": "informational", "diagnostics": "No issued time (when the laboratory released the result).", "expression": ["Observation.issued"]}]}
```

Каждая проблема соответствует одному из шагов выше. Запустите `$validate` в ваших интеграционных тестах с реальными образцами из каждого источника.

## Шаг 7: сохранение источника

Нормализация изменяет способ записи значения, но не то, откуда оно взялось. Отправляйте `author_type` (`device`, `import`, `practitioner`, `patient`) и `X-Anpheros-Source-System` при каждой записи. Это позволяет врачу, аудитору или модели ИИ отличить лабораторный результат от значения, введенного дома. См. [Объяснение происхождения медицинских данных](https://developers.anpheros.com/ru/guides/medical-data-provenance).

## Чек-лист

- [ ] Каждый источник имеет сопоставление своих наименований с LOINC / ATC / ICD-10, которое хранится в вашей интеграции
- [ ] Неоднозначные наименования остаются без сопоставления и сообщаются, никогда не угадываются
- [ ] Единицы UCUM, соответствующие коду; без скрытого преобразования
- [ ] Даты в формате ISO 8601, с часовыми поясами, если время имеет значение
- [ ] Диапазоны референсных значений сохраняются с лабораторными значениями
- [ ] Оригинальная формулировка сохранена (`code.text`)
- [ ] Ключи идемпотентности или идентификаторы источника при каждой записи
- [ ] `author_type` и система-источник при каждой записи
- [ ] `$validate` в тестах для лабораторных результатов

## Часто задаваемые вопросы

### Что такое нормализация медицинских данных?
Преобразование данных о состоянии здоровья из различных источников в одну стандартную форму: общие коды (LOINC для тестов и измерений, ICD-10 для диагнозов, ATC для лекарств), единицы UCUM, даты ISO, референсные диапазоны и зарегистрированный источник, чтобы значения из разных систем могли сравниваться, объединяться и обмениваться.

### Следует ли преобразовывать все значения в одну единицу измерения?
Храните единицу измерения, в которой измерял источник, если это допустимая единица UCUM, соответствующая коду, и преобразуйте при отображении или анализе. Это позволяет отслеживать каждое сохраненное значение до его первоначального отчета.

### Может ли нормализация быть полностью автоматической?
В основном. Коды в источнике, известные названия и правильно сформированные единицы измерения могут быть сопоставлены автоматически. Неоднозначные названия и неизвестные локальные коды требуют однократного человеческого решения для каждого источника, после чего сопоставление становится автоматическим.

## Связанные материалы

- [FHIR code systems](https://developers.anpheros.com/ru/guides/fhir-code-systems)
- [Как импортировать лабораторные результаты в запись FHIR](https://developers.anpheros.com/ru/guides/import-lab-results-fhir)
- [Объяснение происхождения медицинских данных](https://developers.anpheros.com/ru/guides/medical-data-provenance)
- [Интероперабельность медицинских данных](https://developers.anpheros.com/ru/guides/interoperability)
- [API медицинских данных](https://developers.anpheros.com/ru/guides/medical-data-api)
