lingamr は、LiNGAM
系のアルゴリズムを使って、純粋な観測データから
因果構造(どの変数がどの変数の原因であり、どれだけ強く影響するか)を推定する
(清水研究室による Python lingam
パッケージのR移植版)。
この vignette では、LiNGAMの中核となる考え方を説明し、最小限のエンドツーエンドの ワークフローを一通り実行する。各手法の詳細なガイドはパッケージサイトにある (次に読むべきもの を参照)。
基本的な考え方:なぜ非ガウス性から因果の方向がわかるのか
相関だけでは「xがyの原因」なのか「yがxの原因」なのかを区別できない。どちらの モデルもまったく同じ共分散行列を生成しうるからである。したがって、2次モーメント (分散・共分散)に基づく古典的な手法が返せるのは、せいぜい構造の同値類までである。
LiNGAM(Linear Non-Gaussian Acyclic Model; Shimizu et al. 2006)は、誤差項が 非ガウスであるという仮定を1つ加えることで方向を解決する。各変数のモデルは
であり、誤差 は互いに独立な非ガウス分布に従い、構造はDAG(有向非巡回グラフ) をなす。これらの仮定の下で、因果構造は観測データから同値類ではなく一意に識別可能 になる。
直感は1つの実験で確認できる。真のモデルを (誤差は一様分布、 すなわち非ガウス)とし、両方向に回帰してみる。
n <- 1000
x <- runif(n, -1, 1)
y <- 1.5 * x + runif(n, -1, 1)
res_causal <- residuals(lm(y ~ x)) # correct direction: x -> y
res_anti <- residuals(lm(x ~ y)) # reverse direction: y -> x
oldpar <- par(mfrow = c(1, 2))
plot(x, res_causal, main = "Correct: residual of y ~ x", cex = 0.4)
plot(y, res_anti, main = "Reverse: residual of x ~ y", cex = 0.4)
par(oldpar)正しい方向では、残差は説明変数と独立である(左パネル:のっぺりした帯)。逆方向 ではそうならない(右パネル:残差の散らばりが に依存する)。Direct LiNGAMはこの 非対称性をアルゴリズムに変換したものである。残差が最も独立になる変数こそが最上流 (外生)の変数であり、それを取り除き、回帰で影響を除去し、同じ手続きを繰り返す。
誤差がガウス分布であれば、両パネルは同じに見えてしまう。これが非ガウス性が不可欠な 理由であり、基本の LiNGAMモデルが次の4つの仮定に立脚する理由である。
- 線形な関係
- 非巡回なグラフ(DAG)
- 非ガウスで互いに独立な誤差
- 潜在交絡変数がない(すべての共通原因が観測されている)
これに加えて観測はi.i.d.であることを仮定する。lingamr
には、これらの仮定を
それぞれ緩和した推定器も収録されている(vignette("method-selection-ja")
を参照)。
最小限のワークフロー
推定
generate_lingam_sample_6()
は既知の6変数LiNGAMモデルからデータを生成するので、
推定結果を真の構造と比較できる。lingam_direct() はDirect
LiNGAMを実行する。
デフォルトでは独立性は相互情報量で評価され、係数はadaptive
LASSOで推定される。
x1k <- generate_lingam_sample_6(n = 1000)
model <- lingam_direct(x1k$data)
model
#> Direct LiNGAM Result
#> Variables : 6
#> Causal order: x3 -> x2 -> x0 -> x4 -> x5 -> x1
#>
#> Adjacency matrix (row = to, col = from):
#> x0 x1 x2 x3 x4 x5
#> x0 0.000 0 0.000 3.033 0 0
#> x1 2.988 0 2.002 0.000 0 0
#> x2 0.000 0 0.000 5.993 0 0
#> x3 0.000 0 0.000 0.000 0 0
#> x4 8.000 0 -1.000 0.000 0 0
#> x5 4.015 0 0.000 0.000 0 0結果の2つの主要な要素:
# 推定された因果順序(上流が先頭)
colnames(x1k$data)[model$causal_order]
#> [1] "x3" "x2" "x0" "x4" "x5" "x1"
# 隣接行列: B[i, j] は x_j から x_i への直接効果
round(model$adjacency_matrix, 3)
#> x0 x1 x2 x3 x4 x5
#> x0 0.000 0 0.000 3.033 0 0
#> x1 2.988 0 2.002 0.000 0 0
#> x2 0.000 0 0.000 5.993 0 0
#> x3 0.000 0 0.000 0.000 0 0
#> x4 8.000 0 -1.000 0.000 0 0
#> x5 4.015 0 0.000 0.000 0 0可視化
plot_adjacency()
は因果グラフを描画する。true_B
に真の構造を渡すと、比較結果が 色分けされる(緑 = 正解、赤 =
誤検出、オレンジ破線 = 見逃し)。
model$adjacency_matrix |>
plot_adjacency(
labels = colnames(x1k$data),
true_B = x1k$true_adjacency,
title = "Estimated vs. true structure"
)介入の効果:総因果効果
総因果効果とは、ある変数を1単位動かしたとき、すべてのパスを通じて別の変数が最終的に どれだけ動くかであり、介入について考えるにはこちらが必要になる(重回帰係数は別の 問いに答えるものである。詳細は Direct LiNGAM記事 を参照)。
total_effects <- estimate_all_total_effects(x1k$data, model)
round(total_effects, 2)
#> x0 x1 x2 x3 x4 x5
#> x0 0.00 0 0.00 3.03 0 0
#> x1 2.87 0 1.94 21.06 0 0
#> x2 0.00 0 0.00 5.99 0 0
#> x3 0.00 0 0.00 0.00 0 0
#> x4 7.91 0 -1.13 18.28 0 0
#> x5 4.02 0 0.00 12.18 0 0仮定の確認
summary_lingam()
は2つの主要な診断をまとめて実行する。残差は互いに独立で
あるべきで(仮定4)、かつ非ガウスであるべきである(仮定3。したがって、ここでは
正規性が棄却されることが良い知らせになる)。
summary_lingam(x1k$data, model)
#> === Direct LiNGAM Model Summary ===
#> Variables: 6
#> Observations: 1000
#> Edges: 7
#> Causal order: x3 -> x2 -> x0 -> x4 -> x5 -> x1
#>
#> --- Assumption 1: Independence of residuals ---
#> Method: spearman
#> Dependent pairs: 0 / 15 (p < 0.050)
#> Min p-value: 0.0510
#> => Residuals appear mutually independent (assumption supported).
#>
#> --- Assumption 2: Non-Gaussianity of residuals ---
#> Method: shapiro
#> Non-Gaussian: 6 / 6 (p <= 0.050)
#> => All residuals are non-Gaussian (assumption supported).安定性の定量化
ブートストラップはリサンプルしたデータで推定を再実行し、各エッジがどれだけの頻度で 再現されるかを報告する。出現確率の低いエッジを過大解釈してはならない。
bs <- lingam_direct_bootstrap(x1k$data, n_sampling = 50L, seed = 42)
#> Bootstrap: 50 iterations, method=adaptive_lasso (sequential)
#> iteration 1 / 50
#> iteration 10 / 50
#> iteration 20 / 50
#> iteration 30 / 50
#> iteration 40 / 50
#> iteration 50 / 50
#> Completed in 1.7 seconds.
round(get_probabilities(bs), 2)
#> [,1] [,2] [,3] [,4] [,5] [,6]
#> [1,] 0.00 0.02 0.00 0.98 0.02 0
#> [2,] 0.98 0.00 0.98 0.00 0.00 0
#> [3,] 0.00 0.02 0.00 0.98 0.02 0
#> [4,] 0.00 0.00 0.02 0.00 0.00 0
#> [5,] 0.98 0.02 0.98 0.00 0.00 0
#> [6,] 1.00 0.00 0.00 0.00 0.00 0次に読むべきもの
時系列・潜在交絡・非線形関係・混合データ・欠測データなど、あなたのデータに合う
推定器の選び方は vignette("method-selection-ja")
で解説している。
詳細な実行例はパッケージサイトにある。
| 記事 | 内容 |
|---|---|
| Direct LiNGAM 詳説 | 事前知識、回帰手法、非ガウス性の実験、高次元データ、失敗するケース |
| ブートストラップと診断 | 安定性分析、仮定の確認、SEM適合度、broom連携 |
| 時系列 | VAR-LiNGAM、VARMA-LiNGAM |
| 潜在交絡変数 | BottomUpParceLiNGAM、RCD |
| 非線形の手法 | RESIT、CAM-UV |
| 特殊なデータ | 混合データ(LiM)、複数グループ、欠測値 |
英語版は vignette("lingamr")
を参照。サイトの記事にも英語版がある。
