Health Dashboard, часть 6: измерять стресс без самообмана
Часть 6 моего build log по Health Dashboard: почему единая «оценка стресса» структурно нечестна, что доступные сигналы могут и не могут сказать, как очевидную формулу заменила каноническая после ревью шестью LLM и какая rubric не даёт системе автоматически настраиваться на шум.
В части 5 редизайн EnergyBank оставил placeholder: drain = α · active_kcal + β · sustained_hr_load, где β = 0 до тех пор, пока per-user validation rubric не вернёт validated. Этот член уравнения — drain от autonomic load — относится к v2.2. И именно с ним я хотел быть особенно осторожным, потому что «стресс» — самая переобещанная метрика в consumer health.
Эта часть — о том, что стресс вообще означает физиологически, что мои данные могут и не могут о нём сказать, почему очевидная формула была неправильной и зачем нужна validation rubric.
Чем «стресс» не является
Большинство consumer wearables поставляют «stress score»: Garmin Body Battery, Whoop strain, Fitbit Stress Management. Это удобно, но, как говорит Марко Альтини — PhD по физиологии и создатель HRV4Training, — это made-up scores: закрытые алгоритмы без peer review, с результатами, которые плавают между физиологически похожими днями. Литература по validation носимых устройств в этом единодушна:
Raw HRV1 и RHR показывают значимую связь с валидированными мерами стресса. Proprietary composite algorithms непрозрачны и не имеют независимой validation. Composite scores дают сомнительную добавочную ценность по сравнению с raw signals.
Методологическая проблема не в коммерческой тайне. Проблема в том, что «стресс» — не одно состояние. Это спектр биологических signatures с разными временными масштабами и разными маркерами:
| Тип | Паттерн маркеров | Временной масштаб | Есть в моих данных? |
|---|---|---|---|
| Acute sympathetic spike — испуг, кофеин, нервы перед deploy | HR↑, HRV↓, EDA↑ | секунды–минуты | HR — да; HRV — частично; EDA — нет |
| Sustained autonomic load — весь день переживал за собаку | HR смещён на +5–10 bpm, HRV подавлен, дыхание быстрее | часы | target v2.2 |
| Allostatic load / chronic stress — burnout, долгий sleep debt | baseline RHR ползёт вверх, HRV вниз | недели–месяцы | да, через 30–90d trends |
| Illness response — грипп, COVID prodrome | RHR↑ на 5–10 bpm, wrist temp↑ на 0.3°C, HRV↓↓ | дни | частично: wrist_temperature только с 2025-10 |
| Recovery debt — тяжёлая тренировка вчера | overnight HRV↓, лёгкий RHR↑ | следующее утро | да |
Схлопывание всего этого в одно число скрывает, какой именно процесс нагружен. «Stress score 67/100» не говорит мне, тревожусь ли я, борюсь с вирусом или сижу на трёх ночах плохого сна. Действия в каждом случае разные.
Поэтому дизайн v2.2 начинается с ограничения: несколько сигналов, несколько классифицированных flags, никакого composite score. Каждый flag уходит в своё downstream-поведение.
Что есть в данных и чего нет
Количество samples в базе на 2026-05-12:
heart_rate 1 916 248 samples 2015-01-11 → present high-density (every few min)
respiratory_rate 35 954 samples 2021-09-24 → present ~daily aggregates
blood_oxygen_saturation 32 830 samples 2020-12-31 → present
heart_rate_variability 13 373 samples 2018-04-29 → present sparse (~5/day, event-triggered)
wrist_temperature 191 samples 2025-10-08 → present nightly only, Apple Watch S8+
Трёх сигналов там нет:
- EDA / GSR2. Apple Watch это не измеряет. Fitbit Sense и Whoop 4.0+ — измеряют.
- Кортизол. В 2026 году нет неинвазивного consumer wearable способа его измерять.
- Continuous HRV. Apple измеряет HRV в конкретных событиях: Breathe sessions, после exercise, иногда ночью. Но не отдаёт continuous overnight aggregate, хотя сенсор технически имеет данные. Whoop и Oura это делают.
Практический вывод: heart rate — единственный плотный continuous signal. Методология должна в первую очередь опираться на HR, а HRV, respiratory rate и temperature использовать как confirmatory channels, когда они есть. Любой дизайн, которому нужно много HRV, просто не работает на моём dataset.
Personal baselines с variance floor
Та же baseline-конвенция из части 5 — rolling 30-day, не сохранять постоянно, state machine cold / warmup / steady — переносится сюда. Неочевидное дополнение касается variance:
sd_hr_awake[d] = max(MAD-based SD [^mad-sd] of last 30 days, SD_FLOOR_HR)
SD_FLOOR_HR = 3.0 bpm. Без floor пользователи с очень стабильным дневным HR могут иметь MAD-derived SD всего 1.5–2 bpm. Тогда обычный чуть более занятый день с +5 bpm читается как z ≈ 2.5 — ложная «аномалия». Floor 3 bpm выбран эмпирически на моих 90-дневных данных; N=1 — документированное ограничение. План — пересмотреть это, когда методология будет работать на когорте больше трёх пользователей.
Три blocker-а до того, как β может стать ненулевой
План фиксирует три preconditions, которые должны быть выполнены до того, как β начнёт что-либо умножать.
daily_scores.rhr_avg— это не RHR. Это среднее по всем heart rate samples за день, а не resting baseline. v2.2 не должен использовать эту колонку какRHR_baseline. Либо колонка чинится и начинает считаться из overnight low-HR windows, либо появляется отдельныйbaseline_hr_overnight: медиана HR за последние 3 часа основного sleep segment, с fallback03:00–06:00только если sleep data imputed или stale. Нужно выбрать один путь, а не вести оба параллельно.Awake window должен выводиться из
sleep_analysis, а не из фиксированных часов. Первая версия v2.2 использовала07:00–22:00как stopgap. Это ломается на siesta, night shift, jet lag и поздних ночах «уснул в 02:00». Исправление — алгоритм, который берёт самый длинный continuous asleep segment, чей midpoint попадает в ±6 часов от local midnight для датыd, ставит wake-time в его конец, а sleep-onset — в начало следующего qualifying segment. Fallback к fixed window срабатывает только если main sleep не найден, и ставит flagimputed_awake_window.
Существующий helper internal/storage/typical_wake.go::GetTypicalWakeTime(days) возвращает типичное, усреднённое wake time. Это неправильная форма для этой задачи; helper легитимно нужен для MorningCapTime. Здесь нужен новый WakeTimeForDate(date).
3. Coverage gate. День с менее чем 8 часами HR-covered awake time — часы сняты, на зарядке, sync gap — не должен считать sustained_hr_load. Иначе дни, когда часы сняты с 10:00 до 16:00, систематически under-drain-ятся. Gate ставит flag stale_stress и откатывается на v2.0 kcal-only drain для этого дня.
Каждая из этих деталей не меняет headline formula. Но именно они определяют, считает ли формула то, что обещает считать.
Формула, которая почти ушла в production
Первый draft v2.2 предлагал простое решение:
drain_hr = β · ∫ max(0, HR(t) − RHR) dt # bpm·hours
Интегрировать площадь, где HR выше RHR. Умножить на β. Добавить к drain. При β ≈ 0.12 относительно bpm·hours3 цель была получить примерно 10–15 drain points в «стрессовый сидячий день». Выглядело чисто. Это именно вариант, который с большой вероятностью реализовал бы человек, если бы двигался быстро.
2026-05-12 я сделал то, чего раньше в этом проекте не делал. Я вставил draft v2.2, канонический STRESS_MEASUREMENT.md и окружающий контекст в шесть разных LLM подряд — Gemini Flash, ChatGPT, Manus, Grok, Gemini Pro и GLM 5.1 — и попросил каждую независимо провести review. Затем я вставил каждый review обратно в обычную Claude Code-сессию и попросил синтезировать: где все шесть согласны, где конфликтуют, что каждая предлагает уникального.
Синтез получился структурированным. Шесть пунктов, где все reviewers согласились независимо от model family: daily_scores.rhr_avg неправильно названа и небезопасна; default awake-window 07:00–22:00 ломается на shift work и jet lag; illness не должна раздувать drain; β как константа имела unit mismatch между двумя документами; draft и канонический документ конфликтовали; asymptotic restore и bootstrap convergence сильные, их трогать не надо. Затем появился three-way fork по самой drain formula: разные reviewers предпочитали raw HR − RHR в bpm·hours (Gemini Flash), daily-averaged z-shift (ранний draft STRESS_MEASUREMENT) или hourly sustained z-load (Manus).
Синтез сошёлся на трёх конкретных проблемах draft v2.2.
Он опирается на daily_scores.rhr_avg, которая не является RHR. Blocker (1) выше. Формула почти начала использовать колонку, смысл которой тихо уехал от её названия.
Он смешивает физическую и postural components со stress signal в одну raw bpm delta. Стоя HR примерно на 10 bpm выше, чем лёжа. День, в котором человек больше стоял, создаёт apparent overshoot, который не является autonomic load. Интеграл не умеет отличать одно от другого.
На daily-average granularity он теряет временную структуру дня. Четыре часа sustained shift и один час sharp spike с последующими 14 нормальными часами дают одинаковый daily-average overshoot. Физиологически это разные события.
Draft сохранён в репозитории как ENERGY_BANK_V2_2_DRAFT.md с banner SUPERSEDED. Я храню такие файлы по той же причине, по которой храню Phase 1 feasibility verdicts: формула, которую я отверг сегодня, снова покажется разумной через три месяца, когда я забуду конкретную причину, почему она была неправильной.
Отдельно про multi-model review, потому что опыт нетипичный. После пятого review предложения начали повторяться; шестой — GLM 5.1 — добавил четыре реально новых actionable points, и после этого я остановился. Каждый следующий проход давал бы одну придирку и много validating text. Паттерн устойчивый, и я записал его как heuristic: 3–4 model reviews — полезный sweep, 5–6 — diminishing returns, больше — bikeshedding. Правильный следующий шаг после diminishing returns — implementation или domain-expert outreach, а не ещё одна модель.
Формула, которая её заменила
Каноническая формула v2.2 — hourly sustained z-load относительно personal awake baseline:
sustained_hr_load[d] = Σ_h max(0, hr_z_hour[h] − Z_THRESHOLD)
for h ∈ awake_window[d]
only if hr_coverage_hours ≥ MIN_COVERAGE
hr_z_hour[h] = (hour_hr[h] − baseline_hr_awake[d])
/ sd_hr_awake[d] # MAD-based, floored
hour_hr[h] = median of 5-min minima within hour h
Каждая часть этой формулы закрывает один из трёх выводов review.
Медиана 5-минутных минимумов за час, а не средние. Минуты вставания и стояния отражаются на максимумах, поэтому median of minima сохраняет autonomic floor каждого часа. Postural noise перестаёт выглядеть как stress.
Z-shift относительно personal baseline, а не raw bpm. Один и тот же bpm означает разные вещи при разном уровне fitness. Z-shift нормализует absolute baseline, а MAD floor (3 bpm) не даёт z-score взорваться у пользователей с естественно узким HR distribution.
Hourly integral, а не daily average. Day-average дал бы одинаковый drain четырём часам sustained +10 bpm и одному короткому spike плюс четырнадцати нормальным часам. Hourly integral сохраняет temporal structure: считаются только часы, которые реально отклонились. Стартовый Z_THRESHOLD = 0.5 — setting (settings.energy.z_threshold), а не hard-coded constant. При cognitively demanding desk work HR может системно сидеть на z = 0.5–0.8 без autonomic stress, и настройка этого порога — calibration step, а не изменение кода.
Две величины попадают в components JSONB для прозрачности. Drain использует только sustained_hr_load_z, но audit trail также сохраняет hr_overshoot_bpm_hours = Σ max(0, hour_hr[h] − baseline_hr_awake) в raw units. Вторая величина удобна для пользовательской фразы: «HR был примерно на 8 bpm выше вашей нормы около 4 часов». Эти числа коррелируют, но не дублируют друг друга.
Stratified flags вместо composite score
Из z-scored channels получаются пять flags, каждый указывает на другой физиологический процесс:
acute_stress[d] = exists window <2h where hr_z_hour > +2
sustained_load[d] = hr_z_hour > +1 sustained ≥4h consecutive
illness_signature[d] = (temp_shift > +1) AND (resp_shift > +1) AND (hrv_drop > +1)
recovery_debt[d] = overnight (hrv_drop > +1) AND (rhr_shift > +0.5)
parasympathetic_rebound[d] = (hr_shift > +1) AND (hrv_drop < −1)
Каждый уходит в свой слой системы:
acute_stress→ без action. Transient, нет смысла дёргать пользователя.sustained_load→ питает EnergyBank drain через формулу выше.illness_signature→ не питает drain. Рост HR, который flag обнаружил, уже находится вsustained_hr_load; illness multiplier дал бы double-counting. Flag идёт в verdict layer: переопределяет текст verdict — «body fighting infection, rest aligns with this» — и suppresses любые AI-рекомендации в стиле «push harder».recovery_debt→ влияет на next-day readiness baseline, территория части 4.parasympathetic_rebound→ только interpretation flag. HR up и HRV up — реальная физиология: vagal rebound4 после sustained stress или heavy training. Рост HR был настоящим autonomic expenditure и остаётся в drain; flag добавляет контекст в verdict narrative: «autonomic state mixed, HR high, HRV high, likely recovery phase».
Разделение drain и verdict важно. Drain — физиологическая цена дня. Verdict — что пользователю делать завтра с учётом этой цены и trend. Если их смешать, sick days будут искусственно раздувать drain: рост HR от illness уже учтён внутри drain term.
Почему в validation нет места self-report
Правило, которое в итоге попало в документ — «subjective input cannot validate the stress formula», — не пришло сверху как декрет. Оно родилось за примерно пять минут back-and-forth.
Изначальный validation plan v2.2 включал узкий self-report. Когда я впервые набросал его с ассистентом, логика была такая: свободная daily score 1–5 будет шумной, но targeted anchor — нет. Одна кнопка в Telegram для «today was genuinely hard», другая для «today was unusually calm», используется несколько раз в месяц. Идея: 5–10 high-confidence anchor points должны весить больше в Pearson r, чем 28 тёплых троек из daily slider. Это выглядело как аккуратный компромисс между нулевым self-report и шумным daily diary.
Я перечитал draft и сказал ассистенту примерно так: «Мне не очень нравится спрашивать пользователя. Люди врут».
Первая реакция ассистента — предложить другие anchors, которые не требуют, чтобы пользователь что-то набирал: calendar events — flight, interview, doctor; wrist-temperature spikes — confirmed illness; HealthKit-recorded workouts — confirmed training load; next-morning HRV — autonomic residual, которым пользователь сознательно не управляет; sleep-onset latency — physiological response, который нельзя вручную отредактировать. Аргумент: если нельзя доверять людям в оценке самих себя, использовать каналы, которые данные уже производят.
Я ответил, что идея с calendar не подходит. Apple Calendar не экспортируется через HAE, а добавлять Google Calendar integration только ради stress calibration — неправильная dependency для health-data проекта. Это убрало два anchor-а из пяти. Оставшиеся три — те, что сейчас в STRESS_MEASUREMENT.md: next-morning HRV residual как primary channel, RHR shift как secondary cross-check, sleep architecture как tertiary.
Так что no-self-report rule не был a priori позицией. Это результат короткого разговора, который начался с «давай добавим узкую self-report кнопку», а закончился на «давай использовать только сигналы, которые пользователь сознательно не генерирует». Выживший механизм — четыре objective channels и decision rubric.
Причины стоит оставить явными.
Subjective daily stress logs методологически отравлены как calibration signal. Люди забывают. Ставят «3» по умолчанию. Рационализируют post-hoc: «сегодня был deadline, значит, я был в стрессе, поставлю 4». Это textbook confirmation bias, он завышает Pearson r5 ровно в направлении, куда уже смотрит формула. Формула, auto-tuned на self-report, который сам auto-tuned на формулу, — это closed loop без external referent.
Это правило относится к validation физиологических формул. Оно не означает, что subjective input вообще не нужен. Оно означает, что subjective input не голосует за значение β. В части 8 я вернусь к тому, где subjective input действительно уместен: gating morning report, anchoring habit, marking days for narrative review later. Ничто из этого не калибрует формулу. Check-in row и stress-validation rubric живут в одной базе, но не смотрят друг на друга.
Validation harness использует только объективные сигналы, уже лежащие в базе. Никакого user input path. Четыре канала, отсортированные по качеству сигнала:
- Next-morning HRV residual — primary. Pearson(
sustained_hr_load[d],overnight_HRV[d+1]) на 30-day rolling window. HRV на следующее утро нельзя сознательно контролировать. На него влияет поведение — alcohol, late training, — но пользователь не пишет число вручную, так что post-hoc rationalisation его не гнёт. Ожидание:|r| ≥ 0.3с negative sign — high stress load → depressed next-morning HRV — чтобы формула была validated для пользователя. - Next-morning RHR shift — secondary. Cross-check для channel 1. Caveat: RHR может физиологически sign-flip. Vagal rebound после heavy load означает ниже next-morning RHR. Поэтому это agreement check, а не standalone pass criterion.
- Sleep architecture degradation — tertiary. Daytime autonomic load должен предсказывать longer sleep_onset_latency, больше sleep_awake, lower deep_pct в первой трети ночи. Три sub-correlations голосуют по sign agreement.
- Test-retest stability — sanity. Прогнать одну и ту же дату через формулу на d+1 и d+7.
sustained_hr_loadдолжен отличаться ≤1 z-load unit; bank — ≤2 points. Больший drift означает, что inputs не settled или формула overfits recent context. Это pinned в Go test (TestBankConvergence), чтобы convergence claim не регрессировал тихо при tuning.
У channel 3 есть Apple Watch caveat: Apple Watch известен склонностью over-detect deep sleep — он путает immobility с slow-wave sleep. Для ночей, где picked source — Apple Watch, deep_pct_first_third downweighted, а основными остаются sleep_onset_latency и sleep_awake. Для RingConn / Oura-style sources этот caveat нужно калибровать отдельно, потому что их sleep staging bias другой.
Rubric и отказ от tuning
Validation rubric запускается через /api/admin/stress-validation. Нет request body, нет CSV upload. Только read поверх существующих таблиц. Идемпотентно.
| Channel 1 (HRV) | Channels 2+3 agreement | Action |
r ≤ −0.3 | At least one of {RHR, sleep} agrees in sign | Validated, β можно tuning-овать per user |
−0.3 < r < −0.1 | At least two channels agree in sign | Weak signal, β остаётся placeholder, flag calibration_weak, recheck weekly |
|r| < 0.1 on channel 1 | n/a | Inconclusive, вероятно HRV sparsity; β остаётся 0, data_quality_warn |
r > 0 on channel 1 | n/a | Wrong-direction, формула не ловит физиологию этого пользователя; β suppressed, manual review |
Внутри этой таблицы важны три guard rails.
HRV sparsity preflight. Channel 1 требует minimum sample density. Apple Watch HRV event-triggered. Пользователь без daily breathing habit может дать 0–2 overnight samples в неделю. Если count(overnight HRV samples) в 30-day window ниже 15, channel 1 получает cold, а rubric откатывается к channels 2+3+4 с требованием 2-of-3 agreement. Без этого guard Pearson r по трём-четырём точкам создаёт статистический шум, который rubric примет за signal.
Disagreement override. Channel 1 недостаточен сам по себе. Если channel 1 говорит r ≤ −0.3, но channel 2 (RHR) и channel 3 (sleep architecture) оба disagree in sign, verdict понижается до weak независимо от magnitude r. Single-channel result с двумя contradicting cross-checks скорее overfitting или artefact, чем реальная физиология.
Wrong-direction suppression. r > 0 трактуется как «формула измеряет не то для этого пользователя», а не как «tune harder, пока r не станет отрицательным». Wrong-direction case уходит в manual review.
Смысл rubric в том, что система отказывается калиброваться молча, когда signal слабый или противоречивый. Default behaviour — β_effective = 0, при этом sustained_hr_load_z всё равно пишется в components для audit. Переключение settings.energy.stress_drain_enabled = true происходит только после validated.
Что components JSONB позволит доказать позже
Дизайн v2.2 пишет в components две величины, хотя drain читает только одну: sustained_hr_load_z — канонический input — и hr_overshoot_bpm_hours — input из отвергнутого draft, сохранённый для human-readable audit и cohort comparison. Обе остаются до тех пор, пока cohort validation не покажет, что одной достаточно.
Через шесть месяцев, когда третий или четвёртый tenant проработает достаточно долго для usable distribution, вопрос «канонический signal должен был быть z-shift или bpm·hours?» можно будет ответить по данным, которые уже лежат на диске. Текущая архитектура не коммитится на этот ответ. Она коммитится на то, чтобы оба числа оставались честными.
Вот что «no opaque score» означает на практике. Это не отсутствие числа на дашборде. Число есть, и оно меняется. Смысл в том, что за каждым числом есть audit trail, который другой человек может пересчитать.
Чего не хватает в этой статье
Две недоделанные вещи лучше явно назвать, чем замести под ковёр.
Observability формулы в production. Документы описывают, как считать sustained_hr_load, валидировать его и gate-ить, но почти ничего не говорят о том, что мониторить, когда β станет ненулевой. Без counters для coverage-gate skips, per-channel calibration states, last rubric-run Pearson r и flag-fired distribution нельзя отличить «формула корректно молчит, потому что у пользователя не было stress days» от «формула молча выбрасывает большинство дней на coverage gate». Spec лежит в backlog отдельной задачей и ещё не в production, потому что β пока ни у кого не включена.
Domain-expert outreach. Эта статья цитирует Марко Альтини — HRV4Training — как физиолога, чья критика закрепляет позицию «no opaque scores». Естественный следующий шаг после six-LLM review — отправить каноническую формулу настоящему физиологу с острым вопросом: имеет ли смысл hourly z-load относительно MAD-based personal awake baseline как drain proxy при доступных сигналах? У меня есть Todoist-задача сделать это, и я её ещё не сделал. Причина отчасти трусость, отчасти то, что вопрос станет точнее, когда β будет provisional calibrated на реальных данных.
Настоящий урок
Stress — метрика, которая чаще всего ошибается в consumer wearable, и именно её я больше всего хочу сделать правильной в этом проекте. Редизайн существует, чтобы отказать трём распространённым failure modes: схлопнуть физиологический спектр в единый score, tuning-оваться на self-report и молча выдавать число, когда inputs отсутствуют.
Предыдущая статья была о доверии к молчанию формулы. Эта — о доверии к verdict. Нужно знать, что означает каждый flag, куда он routed и на какие доказательства система имеет право ссылаться.
Пока серия была о том, как сервер учится иметь мнения: об ingestion, колонках, targets, формулах и verdicts, которые эти формулы имеют право производить. Следующая статья берёт вопрос, который из этого следует. Когда сервер заслужил право на свои мнения, что разрешено делать iOS-клиенту? Короткий ответ: меньше, чем хочется.
Вариабельность сердечного ритма. Колебания интервалов между последовательными сердечными сокращениями на уровне миллисекунд. Строго говоря, чем больше вариабельность, тем лучше: стабильный метрономный ритм сердца указывает на доминирование симпатической нервной системы (стресс, болезнь, усталость), в то время как сердце, свободно меняющее темп, свидетельствует о восстановлении парасимпатической системы. Вариабельность сердечного ритма — канонический показатель автономного состояния в современных исследованиях носимых устройств. ↩︎
Электродермальная активность (также называемая гальванической реакцией кожи). Проведение потоотделительных желез измеряется с помощью небольшого напряжения, приложенного к двум электродам на коже. Является прямым маркером симпатической нервной системы: потовые железы иннервируются только симпатической ветвью, поэтому ЭДА отслеживает возбуждение без парасимпатического подавления, которое усложняет интерпретацию стрессовых реакций по сердечному ритму. У Apple Watch отсутствует этот датчик; Fitbit Sense, Whoop 4.0+ и исследовательские нагрудные ремни оснащены им. ↩︎
Единица измерения интегрированного превышения частоты сердечных сокращений: удары в минуту, умноженные на часы. Если ваш пульс держался на уровне 80 ударов в минуту при базовом покое в 60 ударов в минуту, вы накопили 20 уд/ч в час превышения. Интуитивно понятная, но методологически проблематичная, поскольку она смешивает физические и постуральные повышения ЧСС с автономным стрессом, поэтому каноническая формула перешла к з-оцененной часовой нагрузке (см.
[^mad-sd]). ↩︎Постстрессовое автономное состояние, при котором частота сердечных сокращений всё ещё повышена и вариабельность сердечного ритма также выше базового уровня. Блуждающий нерв переходит через компенсаторное торможение после периода симпатической активации. Физиологически реальное состояние, часто наблюдаемое после интенсивных физических нагрузок или продолжительного умственного стресса, и его легко неправильно классифицировать как либо «ещё в стрессе» (повышенная ЧСС), либо «полностью восстановившееся» (повышенная вариабельность сердечного ритма), если рассматривать только один сигнал. ↩︎
Коэффициент корреляции Пирсона. Стандартная мера линейной корреляции между двумя числовыми переменными, варьирующая от −1 (идеальная обратная корреляция) до 0 (отсутствие линейной связи) и до +1 (идеальная положительная корреляция). Для физиологических данных обычно считается, что |r| ≥ 0,3 — это «реальный сигнал, заслуживающий внимания», учитывая шум выборки; |r| < 0,1 — это «вероятный шум». ↩︎