因果推論

【因果推論】因果ダイアグラムの概要

【因果推論】因果ダイアグラムの概要

因果推論では因果ダイアグラムという因果関係を用いたモデルが重要になります。因果関係のグラフ構造を理解するための基本的な知識について解説します。

因果ダイアグラムとは

因果ダイアグラムとは、変数間の因果関係を視覚的に表現するためのツールであり、因果推論において非常に重要な役割をします。特に、因果関係の構造を理解し、適切な介入や分析を設計する際に役立ちます。例えば、因果ダイアグラムを用いることで、ある要因が結果にどのように影響を与えるかを明確に表現できます。

この記事では、因果ダイアグラムを理解するために必要な基本的なグラフの概念とPythonを用いた具体的な表現方法について解説します。

因果推論では、因果のはしごという重要な概念があります。因果のはしごについては「因果のはしごの概要」でまとめているので参考にしてください。

因果ダイアグラムの理解に必要なグラフの概念

因果ダイアグラムを正しく理解するためには、まずグラフの基本的な概念について理解を深めておく必要があります。

グラフとは、ノード(頂点)とエッジ(辺)を持った構造のことで因果推論などに限らず様々な分野で利用されています。以降では、グラフの特徴として押さえておくべき事項について説明します。

有向グラフと無向グラフ

種類概要
有向グラフ(directed graph)ノード間のエッジに方向性があるグラフのことです。例えば、A→Bは「AがBに影響を与える」という意味を持ちます。
無向グラフ(undirected graph)ノード間のエッジに方向性がないグラフです。例えばAーBは「AとBが関連している」という意味を持ちます。
有向グラフと無向グラフ

巡回グラフと非巡回グラフ

種類概要
巡回グラフ(cyclic graph)ノードをいくつかたどると、元のノードに戻ることができるパスを持つグラフです。
非巡回グラフ(acyclic graph)元のノードに戻ってくることができるパスがs存在しないグラフです。
巡回グラフと非巡回グラフ

有向非巡回グラフ(Directed Acyclic Graph:DAG)

因果ダイアグラムにおいて特に重要なグラフとして、有向非巡回グラフ(Directed Acyclic Graph:DAG)があります。このグラフでは、有向グラフでありながら、どのノードからも自身に戻る経路を持たないグラフです。

この性質により、因果関係を時間準や論理的な順序に従って表現することができます。

有向非巡回グラフ DAG (Directed Acyclic Graph)

因果ダイアグラムにおける構造パターン

因果ダイアグラムでは、変数間の関係性を表すための代表的な構造として以下の構造があります。

構造パターン概要
チェーン(Chain)A→B→Cのように、各変数が順に影響を与えるような構造です。
フォーク(Fork)A←B→Cのように、一つの変数(B)が二つの変数(A, C)に影響を与える構造です。
コライダー(Colider)A→B←Cのように、二つの変数(A, C)が一つの変数(B)に影響を与える構造です。

これらの構造の詳細や因果推論にそれぞれが与える影響については別途まとめてみたいと思います。まずは、因果推論で重要な構造パターンとして、チェーン、フォーク、コライダーがあることを理解しておいてください。

チェーン・フォーク・コライダー

グラフの表現方法

フラフは、コンピューター上で扱いやすいように表現することが多いです。ここでは、代表的な隣接行列GML(Graph Modelling Language)の2つの方法について説明します。また、Pythonでグラフのデータ構造やアルゴリズムを扱うためのライブラリであるNetworkXを使った実例も紹介します。

隣接行列

隣接行列は、グラフを行列形式で表現する方法です。ノードがn個ある場合には、n×nの行列を用意し、行列の(i, j)要素が1である場合には、ノードiからノードjへのエッジがあることを示しています。

上記の隣接行列は、A→B、A→C、B→D、C→Dといったエッジが存在する有向非巡回グラフの例です。

Pythonでの実装例

隣接行列を使った場合のPythonにおけるグラフ実装を見てみましょう。

import matplotlib.pyplot as plt
import networkx as nx
import numpy as np

# 隣接行列でグラフを表現する
adj_mat = np.array(
    [
        [0, 1, 1, 0],
        [0, 0, 0, 1],
        [0, 0, 0, 1],
        [0, 0, 0, 0],
    ]
)

# 隣接行列からグラフを構築する
graph = nx.DiGraph(adj_mat)

# ノードにラベルを割り当てる
mapping = {0: "A", 1: "B", 2: "C", 3: "D"}
graph = nx.relabel_nodes(graph, mapping)

# 可視化のためのレイアウトを指定
pos = nx.spring_layout(graph, seed=42)

# ノードとエッジを描画する
nx.draw(
    graph,
    pos,
    with_labels=True,
    node_color="skyblue",
    node_size=1000,
    edge_color="gray",
    arrowsize=20,
)

# グラフを描画する
plt.show()

上記例では、隣接行列はNumPyのndarrayを使用して配列として表現しています。配列を用意したら、NetworkXパッケージのDiGraphに作成した行列を渡すことでグラフを簡単に作成できます。なお、無向グラフの場合はnx.Graphを使用します。

デフォルトではノードに0からの数字が割り当てられるので、A~Dに変更するmappingを作成し、relabel_nodesを使ってラベルを変更しています。今回はNetworkXで用意されているspring_layoutとMatplotlibを使用して描画しています。細かな描画設定の詳細は省略しますが、以下のようなグラフが生成できます。

DAGの可視化 隣接行列

GML(Graph Modeling Language)

GML(Graph Modelling Language)は、グラフをテキスト形式で記述するためによく使用されるフォーマットでグラフを扱うパッケージでよく利用されています。

graph [
  directed 1
  node [
    id 0
    label "A"
  ]
  node [
    id 1
    label "B"
  ]
  node [
    id 2
    label "C"
  ]
  node [
    id 3
    label "D"
  ]
  edge [
    source 0
    target 1
  ]
  edge [
    source 0
    target 2
  ]
  edge [
    source 1
    target 3
  ]
  edge [
    source 2
    target 3
  ]
]

上記において、directed 1の場合は、有向グラフであることを示します。0の場合は、無向グラフとなります。nodeがグラフにおけるノード(頂点)を、edgeがグラフにおけるエッジ(辺)を示しています。

nodeを定義する際には、識別するためのidlabelを定義します。edgeの定義でノード間をつなぐ場合には、sourceに矢印の元のノードのidを、targetに矢印の先のノードidを指定します。

Pythonでの実装例

GMLを使った場合のPythonにおけるグラフ実装を見てみましょう。

import matplotlib.pyplot as plt
import networkx as nx

# GMLでグラフを表現する
gml_sample = """graph [
  directed 1
  node [
    id 0
    label "A"
  ]
  node [
    id 1
    label "B"
  ]
  node [
    id 2
    label "C"
  ]
  node [
    id 3
    label "D"
  ]
  edge [
    source 0
    target 1
  ]
  edge [
    source 0
    target 2
  ]
  edge [
    source 1
    target 3
  ]
  edge [
    source 2
    target 3
  ]
]
"""

# GMLをパースしてグラフを構築する
graph = nx.parse_gml(gml_sample)

# ノードにラベルを割り当てる
mapping = {0: "A", 1: "B", 2: "C", 3: "D"}
graph = nx.relabel_nodes(graph, mapping)

# 可視化のためのレイアウトを指定
pos = nx.spring_layout(graph, seed=42)

# ノードとエッジを描画する
nx.draw(
    graph,
    pos,
    with_labels=True,
    node_color="skyblue",
    node_size=1000,
    edge_color="gray",
    arrowsize=20,
)

# グラフを描画する
plt.show()

GMLでグラフを表現した文字列をgml_sampleで用意しています。GMLを解釈してグラフを構築するにはNetworkXのparse_gmlを使うことで簡単に実現できます。

以降の可視化の部分は隣接行列の場合と同様でグラフを可視化することが可能です。

DAGの可視化 GML

まとめ

因果ダイアグラムを理解するための基本概念、有向非巡回グラフ(DAG)の重要性や因果ダイアグラムにおける代表的な構造パターン(チェーン、フォーク、コライダー)を紹介しました。また、Pythonを用いてグラフを表現する方法と実装例を紹介しています。

因果ダイアグラムの応用については、今後さらに紹介していく予定です。この記事で紹介した内容をもとに、ぜひ因果推論の基礎を理解してみてください。