Однажды к новоиспечённому аналитику компании «Линейные уравнения» обратились коллеги из HR-блока с просьбой проверить гипотезу: влияет ли запущенная ими программа обучения на эффективность сотрудников?
Аналитику передали файл с данными [1], содержащий информацию об эффективности сотрудников и количестве баллов, набранных в рамках программы обучения. Недолго думая, он строит простую линейную регрессию, где эффективность объясняется успеваемостью сотрудников в рамках программы обучения. Модель показывает, что каждый набранный в рамках программы обучения балл связан с увеличением эффективности на 0.53%, результат статистически значим (p < 0.05). Радостный аналитик докладывает заказчикам: «Ура, обучение работает!»
И тут, конечно, любой опытный аналитик скажет: «Постой! Наличие связи — это ещё не причинность. Так нельзя!». Справедливое замечание. Без эксперимента причинные выводы делать рискованно — если вы не уверены в модели. Однако если вы знаете структуру данных и выполняются критерии идентификации, причинный анализ возможен даже без рандомизации.
Но не спешите думать, что ошибка нашего героя — только в поспешной интерпретации результата. Всё хуже. Проблема в том, что даже сама численная оценка эффекта неверна.
Вы спросите: откуда я это знаю? Отвечаю: потому что весь этот кейс — вымышленный. Я сам его придумал и сгенерировал данные, заложив в них «истинный» эффект обучения на уровне 0.1% за один балл [2]. Эти данные, с которыми работает наш аналитик, лежат по ссылке [1]. Но, как видите, он даёт завышенную почти в пять раз оценку. Почему? Давайте разбираться.
Откуда же взялись эти внушительные 0.53%, да ещё и со статистической значимостью? Это не мультиколлинеарность — в модели всего один предиктор. Не множественные гипотезы — гипотеза одна. Не гетероскедастичность и не отклонения от нормальности. Все стандартные тревожные сигналы, которым учат на курсах аналитики, — молчат.
Значит, причина глубже. Настало время познакомиться с самым изощрённым убийцей аналитических выводов — конфаундингом.

Содержание
Введение
Будучи большим поклонником Ричарда МакЭлрита, я опираюсь в этой статье преимущественно на его выдающуюся научную и преподавательскую работу: книгу Statistical Rethinking [3] и одноимённый курс на YouTube [4, 5]. Настоятельно рекомендую вам ознакомиться с этими материалами — особенно если вы всерьёз хотите разобраться в причинно-следственном анализе.
Моя же скромная задача — поместить плоды этой масштабной работы в контекст HR-аналитики и бизнеса. Кроме того, я сознательно выбрал фреймворк проверки нулевой гипотезы (NHST), а не байесовский подход, как у МакЭлрита, чтобы избежать лишней сложности для неподготовленного читателя.
Первое, с чего стоит начать, — это с определений. Среди читателей наверняка найдутся те, кто уже сталкивался с понятием конфаундер (confounder) — спутывающей переменной или общей причины. Однако в этой статье я опираюсь не на классическое определение, а на расширенную трактовку, предложенную МакЭлритом. В ней общая причина — лишь один из четырёх типов искажающих конструкций.
Конфаундинг — это любая ситуация, при которой ассоциация между результирующей переменной Y и интересующим предиктором X отличается от той, которую мы бы наблюдали, если бы значения X были заданы экспериментально [3, стр. 183].
Проще говоря, конфаундинг — это всё, что может исказить причинность. Хотя классическим конфаундером считается только "вилка", МакЭлрит объединяет и другие структуры в обобщённую категорию конфаундинга (confounding), что, на мой взгляд, является удобным и дидактически полезным решением. Известный в кругах HR-аналитики Кит МакНалти, ссылаясь на ту же систему, называет их аккуратнее — ловушки (booby traps) [6].

Посмотрим на эти структуры подробнее:
Вилка (the fork)
X ← Z → Y
Классический конфаундер — общая причина. Если её не учесть, возникнет ложная ассоциация между X и Y. Такие переменные нужно включать в модель, чтобы перекрыть обратный путь (backdoor path).Труба / Цепь / Медиатор (the pipe, the chain)
X → M → Y
Включая медиатор в модель без необходимости, вы рискуете получить ошибку из-за контроля после воздействия (post-treatment bias). Однако здесь всё сложнее, чем с вилкой: контроль за медиатором искажает общий эффект (total effect), но может быть уместен, если нас интересует прямой эффект (direct effect).Коллайдер (the collider)
X → Z ← Y
Источник ошибки отбора (selection bias). Здесь ошибка возникает при контроле. Учёт коллайдера создаёт ложную зависимость между X и Y. Главное правило: никогда не контролируй коллайдер — и всё будет хорошо!Потомок (the descendant)
X → Z ← Y → D
Особый случай, сочетающий черты трёх других. "Паразитирующий" тип искажения. Может открыть путь (то есть создать ложную зависимость), если он является потомком коллайдера, или унаследовать искажение от вилки или медиатора.

Как определить, что перед нами ловушка? Для этого существует мощный инструмент, разработанный отцом современного каузального анализа Джудой Перлом — направленный ациклический граф, или просто DAG (directed acyclic graph) [7], также называемые причинно-следственной диаграммой (causal diagram).
DAG не просто картинка — это логическая модель, которая помогает: определить, какие переменные нужно контролировать и какие — категорически нельзя; увидеть, открыты ли нежелательные пути; оценить, возможна ли идентификация причинного эффекта.
В рамках DAG существует несколько ключевых критериев:
Критерий обратного пути (backdoor criterion): позволяет определить, можно ли заблокировать все нежелательные пути от X к Y путём контроля за набором переменных.
Критерий прямого пути (frontdoor criterion): реже используемый, но позволяет идентифицировать причинный эффект даже при наличии неконтролируемого конфаундера — при определённых условиях.
D-разделение (d-separation): формальный способ определить, какие переменные условно независимы друг от друга при условии контроля за другими.
Я ограничился кратким теоретическим описанием DAG и четырех видов конфаундинга, если вы хотите углубиться в детали, рекомендую статью Causal Inference: DAG [8], опубликованную на Хабре. Закончив с теорией, мы же сфокусируемся на практике.

Практика
Практический блок этой статьи написан на языке R. Для вашего удобства на моём GitHub доступны оба варианта кода: полный пример на R [10] и адаптация на Python [11].
Думаем, перед тем как считать
Вернёмся к нашему гипотетическому кейсу — оценке эффекта корпоративного обучения на эффективность сотрудников. В отличие от юного аналитика из компании «Линейные уравнения», мы начнём с самого важного — анализа контекста и структуры данных.
В нашем распоряжении 1000 наблюдений, я опущу часть, связанную с разведочным анализом и подобной важной аналитической гигиеной, так как подобного, итак, достаточно на просторах интернета, в то время как рассматриваемая в статье тема не столь популярна.
Описание датасета:
Название |
Кодировка |
Шкала |
Описание |
Эффективность |
Efficiency (E) |
от 0 до 100 |
эффективность сотрудников, в процентах |
Обучение |
Learning (L) |
от 0 до 100 |
балл по программе обучения |
Мотивация |
Motivation (M) |
Т-баллы |
уровень мотивации |
Культура взаимного обучения |
Peer learning culture (C) |
от 1 до 5 |
культура взаимного обучения |
Автономность |
Autonomy (A) |
Т-баллы |
автономность сотрудника |
Опыт |
Experience (X) |
Годы |
опыт работы в компании |
Формат работы |
Work format (F) |
офис / гибрид / удалёнка. |
формат работы |
Оценка руководителя |
Manager rating (R) |
от 1 до 5 |
оценка руководителя по результатам обучения |
Чтобы построить корректную модель, нам нужно задуматься: каковы возможные причинные связи между переменными?
Разумеется, для этого необходимо обладать предметной экспертизой. Вы можете быть таким экспертом сами — или пригласить коллег, обладающих знанием контекста. Лично я предпочитаю собирать мини-сессию с командой: обсуждаем гипотезы и вместе рисуем DAG на доске. Это очень полезно, особенно когда мнения расходятся.
В классическом определении, к примеру у Старбака [9, стр. 69], неэкспериментальные исследования не претендуют на причинные выводы, а лишь описывают взаимосвязи в данных.
Однако, как показали современные работы в области причинно-следственного вывода, возможность построения причинной модели всё же существует — даже без эксперимента, если мы чётко сформулируем наши допущения и логически опишем структуру данных через DAG.
Именно поэтому я начинаю с графа: не с регрессии, не с p-value, а с простой схемы «что на что влияет». Это не только упрощает объяснение, но и позволяет избежать дорогостоящих ошибок в моделях.
Прежде чем рисовать, давайте подумаем о нашем кейсе:
Обучение влияет на эффективность (основная гипотеза) L → E.
Мотивированный сотрудник и учится, и работает лучше M → L, M → E.
Сильная культура взаимного обучения помогает усвоению знаний C → L.
Автономность способствует обучению и назначению гибкого формата работы A → L, A → F.
Опытный сотрудник может быть эффективнее и претендовать на удалёнку X → E, X → F.
Формат работы может влиять на эффективность и обучение F → L, F → E.
Оценка руководителя зависит от результата обучения и некой скрытой переменной L → R, U → R.
Эта же скрытая переменная (назовём её U) напрямую влияет на эффективность. К примеру, это может быть харизма U → E.
Уф! Представить всё это в голове — сложно. Поэтому давайте визуализируем структуру в виде DAG.
Загружаем данные и рисуем DAG
# Библиотеки
library(dagitty)
library(ggdag)
library(ggplot2)
# Загрузка данных
url <- https://raw.githubusercontent.com/alexander-botvin/h0h1_about_hr_analytics/main/Confounding%20Or%20How%20an%20Analyst%20Walks%20Into%20a%20Trap/data.xlsx
GET(url, write_disk(temp_file <- tempfile(fileext = ".xlsx")))
df <- read_excel(temp_file)
df$WorkFormat <- factor(df$WorkFormat, levels = c('office', 'hybrid', 'remote'))
# Определение DAG
dag <- dagitty("dag {
L [exposure]
E [outcome]
U [latent]
M -> L
M -> E
A -> L
A -> F
X -> E
X -> F
C -> L
F -> L
F -> E
L -> E
U -> R
L -> R
U -> E
}")
coordinates(dag) <- list(
x = c(M = 3, A = 2, X = 4, C = 1, U = 3.3,
F = 3, L = 2, E = 4, R = 2.8),
y = c(M = 0, A = 2, X = 2, C = 1, U = 1.2,
F = 2, L = 1, E = 1, R = 1.2)
)
ggdag_status(dag) +
geom_dag_point(aes(color = status), size = 10) +
geom_dag_text(color = "white", size = 4) +
scale_color_manual(
values = c("exposure" = "steelblue", "outcome" = "firebrick", "latent" = "gray4")
) +
theme_dag()

Теперь, когда у нас есть DAG мы можем использовать его для идентификации конфаундинга. Посмотрите на график: сможете ли вы определить, все ли четыре базовых конструкции там присутствуют? Подумайте сами. А если сомневаетесь — давайте выделим основные из них вместе:

Теперь зададимся вопросом: какие переменные нужно учесть, чтобы честно оценить эффект обучения на эффективность. Возможных правильных ответа два. Мы можем взять в качестве контрольных переменных мотивацию, автономность и формат работы или мотивацию, опыт и формат работы. И мы ни в коем случае не должны брать в модель оценки руководителя, так как это коллайдер, который также влияет на эффективность!

Чтобы определить корректный набор переменных потребуется опыт и сноровка, однако есть готовые команды, которые могут облегчить нашу жизнь.
# Опеределяем корретный набор переменных
adjustmentSets(dag, exposure = "L", outcome = "E")
{ F, M, X }
{ A, F, M }
Правильные и неправильные ответы
Мы разобрались с конфаундингом — теперь пора взглянуть, к чему приводит его игнорирование на практике. Допустим, вы знаете, что истинный коэффициент влияния обучения на эффективность равен +0.1% за 1 балл [2]. А теперь сравните, что может «наизобретать» аналитик без DAG — просто полагаясь на чувство и статистическую значимость.
Начнём с неправильных ответов и воспроизведем модель от нашего аналитика из «Линейных уравнений», в которую мы включаем только обучение. В результате получаем совершенно неверный коэффициент равный 0.53%, в пять раз выше истины! К тому же статистически значим, да ещё как!
naive_model <- lm(Efficiency ~ Learning, data = df)
summary(naive_model)
Call:
lm(formula = Efficiency ~ Learning, data = df)
Residuals:
Min 1Q Median 3Q Max
-40.875 -9.221 -0.155 8.681 48.631
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 21.5976 1.5160 14.25 <2e-16 ***
Learning 0.5133 0.0281 18.27 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 13.66 on 998 degrees of freedom
Multiple R-squared: 0.2506, Adjusted R-squared: 0.2498
F-statistic: 333.7 on 1 and 998 DF, p-value: < 2.2e-16
Дальше больше, а что будет если к этой модели добавить культуру взаимного обучения. Ведь между этой переменной и эффективностью нет прямого пути, это ведь не может навредить. Думаете?
worse_model <- lm(Efficiency ~ Learning + PeerLearningCulture, data = df)
summary(worse_model)
Call:
lm(formula = Efficiency ~ Learning + PeerLearningCulture, data = df)
Residuals:
Min 1Q Median 3Q Max
-43.778 -8.957 -0.337 8.630 46.582
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 29.10578 1.95698 14.873 < 2e-16 ***
Learning 0.57907 0.02978 19.446 < 2e-16 ***
PeerLearningCulture -4.04399 0.68284 -5.922 4.37e-09 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 13.43 on 997 degrees of freedom
Multiple R-squared: 0.2761, Adjusted R-squared: 0.2746
F-statistic: 190.1 on 2 and 997 DF, p-value: < 2.2e-16

В этом случае мы получаем ещё более искаженный коэффициент 0.58%. Поздравляем, вы только что столкнулись с эффектом усиления смещения (amplification bias). Добавили переменную, не лежащую на причинном пути, — и получили ещё более искажённый результат.
А теперь кульминация неправильных ответов. Что будет, если просто включить все переменные, которые есть? Стандартный ML-рефлекс: чем больше фич — тем лучше!
completely_wrong_model <- lm(Efficiency ~ Learning + Motivation + Experience + WorkFormat + Autonomy + PeerLearningCulture + ManagerRating, data = df)
summary(completely_wrong_model)
Call:
lm(formula = Efficiency ~ Learning + Motivation + Experience +
WorkFormat + Autonomy + PeerLearningCulture + ManagerRating,
data = df)
Residuals:
Min 1Q Median 3Q Max
-24.1518 -5.6542 -0.1096 6.1061 29.8524
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -21.24872 2.29194 -9.271 < 2e-16 ***
Learning -0.20831 0.03574 -5.829 7.53e-09 ***
Motivation 0.46982 0.02533 18.551 < 2e-16 ***
Experience 1.80539 0.10245 17.623 < 2e-16 ***
WorkFormathybrid 9.35777 1.01474 9.222 < 2e-16 ***
WorkFormatremote 16.67436 2.41841 6.895 9.59e-12 ***
Autonomy -0.02104 0.02698 -0.780 0.436
PeerLearningCulture 0.42215 0.49605 0.851 0.395
ManagerRating 10.90802 0.50585 21.564 < 2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 8.571 on 991 degrees of freedom
Multiple R-squared: 0.7069, Adjusted R-squared: 0.7045
F-statistic: 298.8 on 8 and 991 DF, p-value: < 2.2e-16
Однако то, что хорошо работает для прогноза, может оказаться смертельным для причинного вывода. При R² = 0.71 модель действительно даёт отличный прогноз — но только до первой интервенции. Мы получили не просто смещённый коэффициент — он оказался отрицательным: -0.21% вместо истинных +0.1%! Не нужно просто так добавлять переменные в модель!

Что пошло не так? Мы включили в модель коллайдер – оценку руководителей, который соединяет обучение и латентную переменную, влияющую на эффективность. Вуаля — вы открыли путь, которого не должно быть. И это самое страшное, мы то с вами про латентную переменную знаем. Как часто мы про неё знаем в реальности?
Вы могли бы возразить: а разве нельзя было заметить, что модель странная, по диагностике?
Давайте посмотрим.
library(car)
# Оценим мультиколлинерность
vif(completely_wrong_model)
GVIF Df GVIF^(1/(2*Df))
Learning 4.105871 1 2.026295
Motivation 2.329798 1 1.526368
Experience 1.373338 1 1.171895
WorkFormat 1.871795 2 1.169673
Autonomy 1.663479 1 1.289759
PeerLearningCulture 1.504498 1 1.226580
ManagerRating 1.624431 1 1.274532
library(lmtest)
# Проверка гетероскедастичности
bptest(completely_wrong_model)
studentized Breusch-Pagan test
data: completely_wrong_model
BP = 4.5678, df = 8, p-value = 0.8026
Как видите, стандартные аналитические ритуалы в пределах нормы, и они не могут вас спасти от неверного вывода.
Закончив с неверными моделями, давайте наконец посмотрим на те, которые построены на основании DAG и критерия обратного пути. Как мы помним, у нас есть два корректных набора переменных для контроля, и оба они «разрубают» путь смещения между обучением и эффективность.
Начнём с первой модели — в неё мы включим автономность и формат работы, а также мотивацию. И вот результат: коэффициент при обучении составил 0.12% за 1 балл — наконец-то мы приблизились к истинной оценке.
right_model_one <- lm(Efficiency ~Learning + Motivation + Autonomy + WorkFormat, data = df)
summary(right_model_one)
Call:
lm(formula = Efficiency ~ Learning + Motivation + Autonomy +
WorkFormat, data = df)
Residuals:
Min 1Q Median 3Q Max
-38.435 -7.602 -0.286 7.975 35.377
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 10.86264 1.92521 5.642 2.19e-08 ***
Learning 0.11828 0.03549 3.333 0.000892 ***
Motivation 0.46060 0.03007 15.317 < 2e-16 ***
Autonomy -0.13448 0.03431 -3.919 9.49e-05 ***
WorkFormathybrid 16.63035 1.17795 14.118 < 2e-16 ***
WorkFormatremote 33.94133 2.89833 11.711 < 2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 11.43 on 994 degrees of freedom
Multiple R-squared: 0.4775, Adjusted R-squared: 0.4749
F-statistic: 181.7 on 5 and 994 DF, p-value: < 2.2e-16
А что будет, если запустить вторую правильную модель, где вместо автономности мы используем опыт в качестве контрольной переменной? В этом случае оценка эффекта обучения окажется ещё более точной — 0.11% за 1 балл.
right_model_two <- lm(Efficiency ~ Learning + Motivation + Experience + WorkFormat, data = df)
summary(right_model_two)
Call:
lm(formula = Efficiency ~ Learning + Motivation + Experience +
WorkFormat, data = df)
Residuals:
Min 1Q Median 3Q Max
-36.123 -6.508 -0.024 6.572 35.065
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -5.32080 1.56461 -3.401 0.000699 ***
Learning 0.10540 0.02976 3.541 0.000417 ***
Motivation 0.46351 0.02637 17.575 < 2e-16 ***
Experience 1.83041 0.12088 15.143 < 2e-16 ***
WorkFormathybrid 7.92217 1.12042 7.071 2.90e-12 ***
WorkFormatremote 15.19633 2.77933 5.468 5.77e-08 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 10.38 on 994 degrees of freedom
Multiple R-squared: 0.5689, Adjusted R-squared: 0.5667
F-statistic: 262.3 on 5 and 994 DF, p-value: < 2.2e-16
С чем это связано? Когда у вас есть выбор между несколькими корректными с точки зрения идентификации моделями, стоит отдавать предпочтение тем, в которых присутствуют дополнительные переменные, влияющие на зависимую переменную. Такие переменные помогают снизить остаточную дисперсию и тем самым повысить точность оценки эффекта. Говоря иначе: выбирайте те переменные, от которых идут стрелки в вашу целевую переменную, даже если они не являются обязательными для устранения конфаундинга.
Коэффициенты обеих моделей оказались удивительно близки к истинному значению — 0.12% и 0.11% против ожидаемых 0.1%. Но модели не дают истину. Они лишь помогают в неё прицелиться.
Подведем итоги визуализировав коэффициенты всех пяти моделей
library(dplyr)
library(broom)
extract_L <- function(model, label) {
tidy(model, conf.int = TRUE) |>
filter(term == "Learning") |>
mutate(Model = label)
}
coef_data <- bind_rows(
extract_L(naive_model, "Наивная модель (L)"),
extract_L(worse_model, "Плохая модель (L + C)"),
extract_L(completely_wrong_model, "Полностью неверная модель (все переменные)"),
extract_L(right_model_one, "Правильная модель 1 (L + M + A + F)"),
extract_L(right_model_two, "Правильная модель 2 (L + M + X + F)")
)
coef_data$Model <- factor(coef_data$Model, levels = rev(c(
"Наивная модель (L)",
"Плохая модель (L + C)",
"Полностью неверная модель (все переменные)",
"Правильная модель 1 (L + M + A + F)",
"Правильная модель 2 (L + M + X + F)"
)))
# Построение графика
ggplot(coef_data, aes(y = Model, x = estimate)) +
geom_point(size = 5, color = "steelblue", shape = 16) +
geom_errorbarh(aes(xmin = conf.low, xmax = conf.high), height = 0.3, size = 1.2, color = "steelblue") +
geom_text(
aes(label = round(estimate, 2)),
vjust = -1.2,
size = 4.5,
color = "steelblue",
fontface = "bold"
) +
geom_vline(xintercept = 0.1, linetype = "dashed", color = "gray40") +
labs(
title = "Оценка влияния обучения (L) на эффективность (E)",
x = "Оценка ± 95% ДИ",
y = "Модель"
) +
theme_minimal(base_size = 15) +
theme(
panel.grid.major.x = element_blank(),
panel.grid.minor.x = element_blank(),
axis.text.y = element_text(size = 14)
)

Сколько это стоит?
В предыдущих разделах я показал, как неправильные модели могут искажать оценку эффекта обучения. Теперь давайте зададим вопрос, который особенно волнует бизнес: во что обходятся такие ошибки — в деньгах, ресурсах, управленческих решениях?
Начнём с того, что ещё раз взглянем на оценки коэффициента переменной обучения в пяти моделях и то, насколько они смещены относительно истины.
Кроме того, я добавил в таблицу оценки стандартизированных β-коэффициентов, которые интерпретируются как размеры эффекта (effect sizes) в терминах стандартных отклонений, согласно подходу, описанному в [12].
Модель |
Бета-коэффициент при обучении |
Размер эффекта для коэффициента |
Смещение относительно истины |
R² модели (справочно) |
Наивная модель (L) |
+0.51 |
Большой эффект |
Завышен в 5 раз |
0.25 |
Плохая модель (L + C) |
+0.58 |
Большой эффект |
Ещё большее завышение |
0.29 |
Полностью неверная модель (все переменные) |
-0.21 |
Малый эффект |
Ложный негативный эффект |
0.77 |
Правильная модель 1 (L + M + A + F) |
+0.12 |
Малый эффект |
Близко к истине |
0.48 |
Правильная модель 2 (L + M + X + F) |
+0.11 |
Малый эффект |
Почти точно |
0.57 |
Оценка коэффициента — это хорошо, но бизнесу важен другой вопрос: окупается ли программа обучения? Наши коллеги из HR-команды компании «Линейные уравнения» сообщили, что обучение одного сотрудника стоит 50 000 рублей. А финансы уточнили: каждый 1% прироста эффективности приносит компании в среднем 2 500 рублей выручки (это всё ещё вымышленный пример).
Вооружившись этой информацией, давайте проведем бутстрэп-моделирование для всех пяти моделей. Оно позволяет оценить, какова может быть чистая экономическая отдача от обучения на одного сотрудника, с учётом разброса в данных.
Расчёт экономического эффекта
beta_values <- setNames(coef_data$estimate, as.character(coef_data$Model))
# Параметры
set.seed(42)
N <- nrow(df)
n_boot <- 1000
cost_learning <- 50000
value_per_unit_E <- 2500
bootstrap_results <- data.frame()
for (model in names(beta_values)) {
beta <- beta_values[[model]]
gains <- numeric(n_boot)
for (i in 1:n_boot) {
sampled_L <- sample(df$Learning, size = N, replace = TRUE)
efficiency <- sampled_L * beta
gain <- efficiency * value_per_unit_E - cost_learning
gains[i] <- mean(gain)
}
ci <- quantile(gains, c(0.025, 0.975))
bootstrap_results <- rbind(bootstrap_results, data.frame(
Model = model,
MeanNetGain = mean(gains),
CI_Lower = ci[1],
CI_Upper = ci[2]
))
}
bootstrap_results <- bootstrap_results %>%
tibble::as_tibble()
bootstrap_results
# A tibble: 5 × 4
Model MeanNetGain CI_Lower CI_Upper
<chr> <dbl> <dbl> <dbl>
1 Naive model (L) 16374. 15145. 17646.
2 Worse model (L + C) 24840. 23470. 26157.
3 Completelty wrong model (All variables) -76921. -77417. -76410.
4 Right model one (L + M + A + F) -34706. -34994. -34426.
5 Right model two (L + M + X + F) -36383. -36632. -36134.
Что же мы получили в итоге? Если бы менеджмент компании опирался на модель юного аналитика, он жил бы в иллюзии: обучение в среднем увеличит выручку на 16 тысяч рублей на одного сотрудника. Помножив это на количество сотрудников, можно было бы радостно потирать руки и запускать программу. Но стоит сделать реальную интервенцию — запустить программу и потратить деньги — как иллюзия рассыпается. Потому что такая модель не устраняет конфаундинг и завышает эффект в несколько раз.
А теперь посмотрим на правильную модель. В ней всё честно: учтены мотивация, формат работы, опыт. И результат — неприятный: в среднем, обучение приносит убыток в 36 тысяч рублей на одного сотрудника. Эта программа обучения в своём текущем виде экономически нецелесообразна. Возможно, нам действительно нужно обучать — но точно не так и не за такие деньги.
В следующий раз юный аналитик из «Линейных уравнений» начнёт не с регрессии, а с доски и маркера — и с вопроса: «А что здесь с чем связано — и почему?» Пример, конечно, вымышленный, а вот ловушка, в которую он попал, — более чем настоящая.
Заключение
В этой статье я коснулся ряда ключевых аспектов причинно-следственного анализа, подробно показав, что такое конфаундинг и чего он может стоить бизнесу — в деньгах, управленческих решениях и подмене смысла.
Я намеренно не касался здесь контрфактического вывода, потенциальных исходов, инструментальных переменных и других направлений обширной темы каузального анализа. Не потому, что они не важны — напротив, именно они мне особенно интересны и заслуживают отдельного разговора. К примеру, контрфактический вывод – моё любимое развлечение, в то время как инструментальные переменные пока вызывают у меня лёгкое головокружение, но это приятный и продуктивный симптом: есть куда расти.
Современный каузальный анализ — это, без преувеличения, новый фронтир аналитики, особенно в HR, где A/B-тестирование почти неприменимо, а потребность в корректных выводах — критична. Здесь как никогда нужны DAG-и, идентификационные критерии, структурное мышление и уважение к причинной неопределённости.
Как я упоминал в начале, эта статья основана на подходах и классификации Ричарда МакЭлрита [3, 4, 5]. При этом важно отметить, что ключевая особенность его подхода — это байесовская статистика и мышление в вероятностных моделях, однако в рамках этой статьи я сознательно опустил байесовский компонент, чтобы сфокусироваться на графовой структуре и проблеме конфаундинга.
Тем, кто хочет углубиться в тему, я рекомендую также работы Ника Хантингтона-Клейна [13] и Матеуса Факуре [14], которые дают интуитивное и практическое понимание причинного анализа: один — через фокус на дизайне исследования, другой — через простоту, смелость в изложении и, конечно же, мемы.
Готовясь к этой публикации, я с удовольствием увидел, что и на Хабре тема каузального анализа уже развивается. Есть статьи про такие серьёзные подходы, как синтетический контроль [15] — и это вдохновляет!
Ссылки и литература
Richard McElreath. Statistical Rethinking (2nd ed.)
Judea Pearl. Causality: Models, Reasoning, and Inference
Craig Starbuck. The Fundamentals of People Analytics With Applications in R
Nick Huntington-Klein. The effect an introduction to research design

Меня зовут Александр Бóтвин
Я эксперт в области HR-аналитики, занимаюсь этим направлением уже более 10 лет — от операционной отчётности до построения причинно-следственных моделей и оценки экономического эффекта HR-инициатив. Работал в крупных компаниях в сферах большой энергетики, банковского сектора и e-commerce логистики.
Веду телеграм-канал H0H1: про HR-аналитику — делюсь кейсами, разборами и инструментами для тех, кто, как и я, хочет не просто строить отчёты, а понимать, что на самом деле происходит в данных.