Время прочтения: 6 мин.

В данном посте я разберу задачу причинно-следственного (Causal Inference) и затрону отдельные аспекты её решения с помощью ациклических направленных графов с использованием инструментов языка программирования R, не погружаясь в сложную математическую терминологию.

Ациклических направленный граф (Directed Acyclic Graph) или сокращенно DAG – это однонаправленный граф, в котором отсутствуют циклы. Данный инструмент хорошо подходит к решению задач причинно-следственного вывода, так как граф является удобным средством визуализации взаимосвязей между объектами или явлениями.

 В рамках задачи каждое ребро графа будет представлять собой причинно-следственную связь между соответствующими вершинами и в графе будут отсутствовать пути, ведущие от любой вершины к ней же самой.

Теперь рассмотрю пакет dagittyязыка R для работы с ациклическими направленными графами и ggdag – пакет, ориентированный на гибкую отрисовку графов, основанный на упомянутом ранее dagitty и графических пакетах ggplot2 и ggraph.

Рассмотрю основные методы для построения графа (DAG) и анализа взаимосвязей между вершинами. В начале подключу данные библиотеки.

library(dagitty)
library(ggdag)

Для определения графа используется текстовый синтаксис (см. код далее).

gdag <- dagitty("dag{A->B;A->C;A->E;B->D;C->D;D->E}")

В примере выше я определил граф в рамках отношения причинно-следственной связи каждой из вершин графа, но можно сделать более кратко, определив отношения вершины с набором других вершин.

gdag <- dagitty("dag {A->{B C E};B->D;C->D;D->E}")

Ключевое слово dag необходимо, т.к. dagitty работает с несколькими разновидностями графов.

Далее выведу изображение самого графа (Граф 1). Это можно сделать с помощью метода graphLayout, который автоматом строит макет графа, но я задам координаты самостоятельно и воспользуюсь пакетом ggdag  для построения графа.

coordinates(gdag) <- list (
  x=c(A=2, B=1, C=3, D=2, E=3),
  y=c(A=5, B=4, C=4, D=3, E=2))
tidy_dag <- tidy_dagitty(gdag)
ggdag(tidy_dag) +
  theme_dag()

Получу следующий результат:

Граф 1

Dagitty содержит методы для определения отношений между вершинами графа: parents – родители, ancestors — предки, children — дети, descendants – потомки. Найду всех родителей, предков, детей и потомков вершины (переменной) D:

parents(gdag, "D")
[1] "A" "B" "C"
ancestors(gdag, "D")
[1] "D" "C" "B" "A"
children(gdag, "D")
[1] "E"
descendants(gdag, "D")
[1] "D" "E"

Теперь обозначу на графе все родительские вершины графа по отношению к «D»

ggdag_parents(gdag, "D") + theme_dag_blank()

В качестве родительских вершин для D обозначены B и C. Такая вершина с более чем одним родителем называется коллайдером (collider).

Аналогично выведу все дочерние вершины графа для вершины «D».

ggdag_children(gdag, "D") + theme_dag_blank()

Как видно на графе, единственной дочерней вершиной для D является E.

В графе между двумя вершинами могут существовать альтернативные пути.

Найду все возможные пути в графе между вершинами A и E помощью метода paths.

paths(gdag, "A",  "E")
$paths
[1] "A -> B -> D -> E" "A -> C -> D -> E" "A -> E"
$open
[1] TRUE TRUE TRUE

Или можно воспользоваться методом ggdag_paths пакета ggdag. Задам A как причину (exposure) и E как результат (outcome).

exposures(gdag) <- c("A")
outcomes(gdag) <- c("E")
ggdag_paths(gdag)

В качестве результата видим 3 пути от вершины A до E. Метод является полезным для поиска «обходных» путей.

Теперь изображу граф (Граф 2) в котором есть  причинно-следственная связь  B->C и некий фактор (переменная) A, который оказывает влияние и на причину (B) и на результат (С). Такая переменная называется confounder. Подобные переменные в ходе анализа необходимо учитывать, т.к. их влияние может искажать исследование, либо вообще говорить о ложности предположения о причинно-следственной связи.

Граф 2

 Теперь представлю, что нужно провести исследование, в котором максимально точно определить эффект D->E (Граф 1). В ходе чего мне необходимо найти минимальный набор вершин (adjustment set), которые могут влиять на результат анализа взаимосвязи причины (exposure) — D и результата (outcome) – E для дальнейшего учета их влияния и получения более точных результатов. В качестве примера можно привести исследование, посвященное влиянию диеты на общее состояние здоровья, где в подобный набор вершин скорее всего войдут такие факторы как возраст, вес, уровень физической активности, которые могут повлиять на результат исследования.

Однако вернусь к примеру:

exposures(gdag) <- c("D")
outcomes(gdag) <- c("E")
tidy_dag <- tidy_dagitty(gdag)
ggdag_adjustment_set(tidy_dag, node_size = 14, text_col = "black")  +
  theme_dag()

В качестве результата получу два варианта – вершина A и вершины B и С.

Теперь перейду в более наглядному примеру и рассмотрю причинно-следственную связь между присутствием бобров и возникновением плотины на реке. Задам в комплексе набор факторов и построю граф (DAG). Для этого воспользуюсь методом dagify пакета ggdag. Здесь при задании отношений использую символ ‘~’, например, dam ~ beavers означает, что присутствие бобров является причиной построения плотины. В качестве причины (exposure) задам вершину обозначающую присутствие бобров (beavers), а в качестве результата (outcome) – бобровую плотину (dam). Уровень реки (river) и количество деревьев (trees) будут являться причиной и для присутствия бобров (beavers), и для построения плотины (dam).

beaver_effect_dag = dagify(
  beavers ~ river,
  beavers ~ trees,
  dam ~ river,
  dam ~ beavers,
  dam ~ trees,
  exposure = 'beavers',
  outcome = 'dam')

beaver_effect_dag%>%ggdag(node_size = 22)  +
  theme_dag()

Получу следующий граф:

Граф 3

Определю минимальный набор вершин для оценки эффекта beavers->dam

tidy_dag <- tidy_dagitty(beaver_effect_dag)
ggdag_adjustment_set(tidy_dag, node_size = 19, text_col = "black")  +
  theme_dag()

В качестве результата получу множество из вершин уровень реки (river) и количество деревьев (trees). Их необходимо учитывать в ходе дальнейшего анализа взаимосвязи присутствия бобров и возникновения плотины на реке.

В заключение хотелось бы подчеркнуть: несмотря на то, что DAG довольно прост для восприятия, при этом имеет строгую математическую основу. Основное преимущество ациклических направленных графов (DAG) заключается в возможности явного изложения причинно-следственных предположений и дальнейшего их изучения.

Я рассмотрел ряд базовых методов для анализа ациклических направленных графов с помощью библиотек dagitty и ggdag, вместе с тем тема Causal Inference невероятно обширна и интересна.