Контекст
Один и тот же биомаркер у пациента может быть сдан в разных лабах: разные единицы измерения, разные методы, разные reference ranges (RR) — даже когда единицы совпадают. Чтобы сигнал тренда в main-biomarkers-detection (усугубление / улучшение / первый раз abnormal / разворот направления) считался корректно, нужно сравнить последнее измерение с предыдущими — а абсолютные значения не сравнимы между лабами без явного приведения.
Простой пример: HbA1c 7.0% по NGSP-методу и HbA1c 53 mmol/mol по IFCC-методу — одно и то же. ALT 35 U/L при upper_ref 40 и ALT 35 U/L при upper_ref 55 — разная клиническая позиция при идентичной единице и значении. Это решение фиксирует как мы приводим эти измерения к сравнимому виду для целей анализа тренда.
Охват — расчёт тренда внутри сигнала S3. Конкретно нужна функция, которая для пары измерений «то, что мы классифицируем сейчас + одно предыдущее» возвращает «дальше или ближе пациент к границе своего лабораторного коридора, чем раньше». Производные метрики — направление и величина тренда.
Что нужно от ответа на выходе:
- Не зависит от знания конвертаций единиц. Каталог ucumvert / fhir.unit не полный по всем биомаркерам; зависимость от него ломает классификатор на любом нестандартном маркере. Carry-over: собственный каталог конвертаций единиц может пригодиться отдельно — для UI-отображения и для panel-группировки; отдельная страница unit-conversion-catalog (не создана).
- Не предполагает клинического смысла у промежуточных значений. Утверждение не про «порог X означает Y», а только про «структурная позиция в коридоре каждого лаба сместилась туда-то».
- Корректно пропускает односторонние диапазоны (только upper или только lower) — это норма для troponin, BNP, ESR и многих tumor markers.
- Симметрично для high и low отклонений — формула не должна выделять одно направление.
- Полностью воспроизводимо для одного и того же входа: без случайной выборки, без LLM, без зависимости от внешнего состояния.
Рассматривали
Шесть подходов: структурно-позиционная формула вошла в принятое решение; пять отвергнуты.
Структурно-позиционная формула (выбрано)
Для high-отклонения:
position = (value − upper_ref) / (upper_ref − lower_ref)
Симметрично для low ((lower_ref − value) / (upper_ref − lower_ref)). Если position > 0 — за upper-границей; если < 0 — внутри коридора. Сравнение position_latest vs position_prior даёт усугубление (увеличилась) / улучшение (уменьшилась) / разворот (сменила знак).
Плюсы:
- Математически осмысленно при разных RR.
- Не требует знания конвертации единиц: RR в тех же единицах, что и value, — лаб всегда выдаёт согласованно.
- Работает при разных популяциях / методах, потому что RR — это собственная линейка лаба.
- Симметрично для high и low отклонений.
- Проверяемо: формула explicit, каждый шаг воспроизводим.
Минусы:
- Предполагает, что обе границы присутствуют.
- Односторонние диапазоны нужно обрабатывать отдельно (см. ниже в «Выбрали»).
- Не несёт абсолютного клинического смысла — только относительный.
- Числовая шкала не интуитивна (
position = 1.5сама по себе не говорит «как плохо»).
Конвертация единиц (отвергнуто)
Привести все измерения к каноническим единицам (SI / UCUM) через движок конвертации (например ucumvert или встроенный FHIR convert), потом сравнивать «сырые» значения.
Плюсы:
- Интуитивно — «привели всё в mmol/L, потом сравниваем».
- Числовая шкала сохраняет клиническую интуицию.
Минусы:
- Одинаковая единица ≠ сравнимое значение. Два лаба могут оба выдавать ALT в U/L, но при разных методах (IFCC vs Henry) один реально читается на 30% выше другого. Конвертация единиц это не лечит.
- Один и тот же метод ≠ один и тот же reference range. Разные популяции (возраст, этнос, регион), разные поколения опубликованных нормограмм — для одной единицы у двух лабов RR могут отличаться на 20-40%.
- RR — единственная надёжная линейка лаба. Сам лаб сравнивает свои собственные измерения со своим refRange — это и есть сравнимая шкала.
- Покрытие. Каталоги конвертации (UCUM-based) покрывают распространённые единицы, но нестандартные единицы лабов СНГ / Восточной Европы / regional labs часто отсутствуют. Зависимость от внешнего каталога добавляет точку отказа.
Почему отвергнуто: конвертация единиц остаётся полезной для отображения (показать пациенту HbA1c в обоих единицах) и для группировки в panel’и, но не как основной метод сравнения для тренда. Может пригодиться как вторичная проверка (если позиции противоречат) — но это усложнение для будущего, не для v0.1.
Сравнение без нормализации (отвергнуто)
Просто сравнить value_latest против value_prior, без приведения.
Плюсы:
- Нулевая сложность, нулевые зависимости.
Минусы:
- Работает только когда оба измерения в одной и той же единице И с одинаковым RR.
- Этого нельзя предполагать даже у одного и того же лаба между годами (RR обновляются); между лабами — почти никогда.
Почему отвергнуто: молча выдаёт неверные вердикты тренда в случаях, которые невозможно отличить от валидных во время исполнения. Это скрытая точка отказа — хуже чем явный пропуск, когда предыдущих нет.
z-score / SD-нормализация (отвергнуто)
z = (value − mean) / SD, где mean и SD — population statistics для этого биомаркера в этом лабе.
Плюсы:
- Статистически правильно для нормально-распределённых биомаркеров.
Минусы:
- Требует опубликованных population statistics, которые большинство лабов не выдают — только bounds.
- Mean/SD можно reverse-engineer из bounds в предположении нормальности (
mean = (upper+lower)/2,SD = (upper−lower)/4), но это assumes normality, а многие биомаркеры распределены skewed (ferritin, CRP, troponin) — для них z-score теряет смысл. - Дополнительная зависимость от стат-таблиц или от assumption о форме distribution.
Почему отвергнуто: нулевое практическое покрытие в общем случае. Если делать reverse-engineer из bounds — это становится hidden assumption нормальности и для skewed маркеров даёт неправильный verdict. Если требовать population statistics от лаба — лаб их не публикует.
Percentile rank в reference range (отвергнуто)
Перевести value в percentile внутри reference distribution (если она доступна).
Плюсы:
- Интуитивно — «где значение в фактической форме лаб-distribution».
- Меньше предположений чем z-score (не требует нормальности).
Минусы:
- Требует данные distribution, не только bounds.
- Та же проблема покрытия что и у z-score.
Почему отвергнуто: та же — лаб не выдаёт distribution.
Quantile mapping между лабами (отвергнуто)
Построить отображение lab_A_distribution → lab_B_distribution через большой калибровочный набор (или public Bayesian posterior).
Плюсы:
- Статистически наиболее строго.
- Честно учитывает различия методов.
Минусы:
- Требует огромный калибровочный корпус на каждую пару лабов.
- Реалистично нельзя собрать.
- Избыточно для сигнала приоритета показа.
Почему отвергнуто: разрастание охвата на порядок, не оправдано для display-сигнала.
Выбрали
Структурно-позиционную формулу (value − upper_ref) / (upper_ref − lower_ref) (симметрично для low). Применяется парами «последнее vs предыдущее» при вычислении S3 усугубления / улучшения / разворота.
Граничные случаи в v0.1:
- Одностороннее RR (только upper или только lower) — S3 пропуск: «недостаточно данных для тренда», сигнал не срабатывает. Не выдумываем «implicit lower=0» — это меняет форму коридора и ломает симметрию.
- Предусловие симметрии:
upper_ref > lower_ref(sanity-проверка перед формулой). Если refRange degenerate (upper == lower) — пропуск. - Cross-method одного лаба — формула работает идентично; метод-specific RR из лаб-отчёта обеспечивает корректность.
Пропуск S3 — общий механизм: «нет предыдущих → S3 не срабатывает, S1+S2 остаются». Расширение этого пропуска на граничные случаи формулы — естественное продолжение.
За
- RR — собственная линейка лаба. Лаб публикует RR именно потому что свой RR — единственная сравнимая шкала для своих измерений. Использовать что-то другое — игнорировать что лаб говорит про собственную калибровку.
- Никаких внешних зависимостей по покрытию. RR всегда в отчёте; ucumvert / population stats / quantile mappings — нет.
- Симметрично и монотонно. Разворот направления определяется сменой знака
position; усугубление — увеличением; улучшение — уменьшением. Прозрачно и replayable. - Утверждения скромные. Не «X — пограничное значение», не «Y — критическое». Только «пациент сейчас дальше / ближе к границе коридора этого лаба, чем раньше». Формальная сравнимость, не клиническая эвристика.
Следствия
- Подача предыдущих измерений в main-biomarkers-detection. Сегодня health-report-pipeline Phase 1 свёртка отбрасывает предыдущие; для S3 надо либо подгружать параллельно, либо переделать свёртку. Open в defining-main-biomarker § Следствия.
- Одностороннее RR — частый случай, не граничный. Troponin, BNP, D-dimer, ESR, tumor markers (PSA / CA-125 / CEA) — почти все имеют только upper. S3 на них пропускает по дизайну. Это потеря покрытия; компенсируется S1+S2.
- Pediatric / pregnancy диапазоны — формула работает идентично, если RR соответствует cohort’у пациента на момент измерения. Вопрос не математический, а на стороне данных: правильный ли RR отдал лаб для возрастной группы. См. region-aware-ranges.
- Отображение единиц ≠ нормализация для тренда — это две разные задачи. Конвертация единиц нужна для UI (test page показывает значения в предзаданных единицах региона пациента: например, mmol/L вместо mg/dL для глюкозы) — задача читаемости результата. Нормализация для тренда — это про сравнимость двух измерений между собой через структурную позицию (задача корректности вердикта worsening/improving). Разные задачи, разные методы, разный момент применения; не путать.
Открытые вопросы
- Порог для «значимого» тренда. Насколько большое изменение
positionсчитается усугублением vs шумом?Δposition > 0.1(10% ширины коридора)? Эмпирический вопрос для рабочего цикла с врачом на ~50 doctor-reviewed отчётов одновременно с калибровкой N=2 для S1. - Чувствительность разворота направления. Один разворот из low в high — значимо? Два подряд? Паттерн не зафиксирован, ждёт рабочего цикла с врачом.
- Односторонние диапазоны — расширить? Возможно ввести «soft lower» (
value × 0.1?) для маркеров типа troponin — но это уже синтетическая эвристика, выходит за обоснование формулы. Лучше оставить пропуск. - Покрытие pediatric/pregnancy диапазонов. Доходят ли pediatric RR из реальных лабов в нашу FHIR-структуру? Если нет — pediatric S3 не работает из-за данных, не из-за математики. См. region-aware-ranges.
Связано
- main-biomarkers-detection — модуль, в котором эта формула используется (S3 trend signal)
- defining-main-biomarker — родительская decision: три OR-сигнала, из которых S3 опирается на эту нормализацию
- health-report-pipeline — Phase 1 свёртка, которая сейчас отбрасывает предыдущие измерения; нужна доработка чтобы S3 работал
- medical-expert-loop — общий рабочий цикл с врачом, через который калибруется порог «значимого» тренда и чувствительность разворота
- region-aware-ranges — лаб-specific и cohort-specific reference ranges; источник RR для формулы
- biomarker-graph — содержит обоснование для биомаркер↔condition связей; не относится к нормализации напрямую, но read-only-контракт тот же
- biomarker — entity-страница про биомаркер
- normalization-service / loinc-harmonization-pipeline — другие «нормализации» (имена / коды) на других слоях, не путать с этой числовой