杉原データサイエンス事務所のロゴ Rによるデータ分析

RによるLiNGAM

LiNGAM のRによる実施例です。

モデルの係数を調べる分析

モデル式の係数を調べる方法です。

pcalgを使う場合

pcalgは、LiNGAMのためのパッケージです。

ここでは、以下の式で作ったデータを使っています。 eのところは、一様分布の乱数を使っています。
LiNGAM

library(pcalg) #ライブラリの読み込み
setwd("C:/Rtest") # 作業用ディレクトリを変更
Data <- read.csv("Data.csv", header=T) # データを読み込み
Str <- lingam(Data)$Bpruned # LiNGAM
rownames(Str) <- colnames(Data) # 行名を追記
colnames(Str) <- colnames(Data) # 列名を追記
Str
LiNGAM

データを作った時の係数とほぼ同じ値が求まりました。

pcalgを使わない場合

参考文献のところにありますが、pcalgを使うには、CRANにはないライブラリが先にインストールされている必要があります。 それが原因で、pcalgが使えないことがあります。

そこで、pcalgを使わずにLiNGAMを実行するためのコードが、下記になります。 メインのところは、ChatGPTで作っています。DirectLiNGAMを、Shimizu氏の2011年の論文に沿って作っているそうです。

R-EDA1 でも、このコードを使っています。

下記の実施例のように、pcalgと、ほぼ同じ結果になることは、確認しています。

setwd("C:/Rtest") # 作業用ディレクトリを変更
Data <- read.csv("Data.csv", header=T) # データを読み込み

### ここからpcalgと異なる部分 ###
independence_score <- function(x, r) {
abs(cor(tanh(x), r))
}

direct_lingam <- function(X) {
# ここからpcalgのlingam関数に相当する関数を作成
n <- nrow(X)
p <- ncol(X)
vars <- colnames(X)

causal_order <- c()
B <- matrix(0, p, p)
colnames(B) <- rownames(B) <- vars

X_work <- X
var_index <- 1:p

while (ncol(X_work) > 1) {
scores <- rep(0, ncol(X_work))

for (j in 1:ncol(X_work)) {
xj <- X_work[, j]
others <- X_work[, -j, drop = FALSE]

sc <- 0
for (k in 1:ncol(others)) {
fit <- lm(others[, k] ~ xj)
r <- resid(fit)
sc <- sc + independence_score(xj, r)
}
scores[j] <- sc
}

root <- which.min(scores)
root_name <- colnames(X_work)[root]

if (ncol(X_work) > 1) {
for (k in 1:ncol(X_work)) {
if (k != root) {
fit <- lm(X_work[, k] ~ X_work[, root])
B[var_index[k], var_index[root]] <- coef(fit)[2]
X_work[, k] <- resid(fit)
}
}
}

X_work <- X_work[, -root, drop = FALSE]
var_index <- var_index[-root]
}

list(
adjacency_matrix = B
)
}
Str <- direct_lingam(Data)$adjacency_matrix
# ここはpcalgのlingam関数を使う部分に相当
### ここまでpcalgと異なる部分 ###
Str

LiNGAM

変数の影響の大きさの分析

重回帰分析 で、各変数の影響の大きさを調べたい時は、単純に回帰式の各変数の係数を比べることは間違いで、偏回帰係数を比べます。 偏回帰係数は、各変数を 標準化 してから、重回帰分析をして求まる係数になります。

因果関係の分析にLiNGAMを使う場合も、単純にLiNGAMの出力を使おうとすると、同じ間違いが起きます。 ここでも標準化をしてから、LiNGAMを実行することにします。

そして、この係数を影響の大きさを表す数値として、ネットワーク構造の太さに使います。

LiNGAM+ネットワークグラフ

#のついた3行は、#を消せば、データの前処理に標準化をします。 筆者の経験上、標準化をすると、矢印の方向が間違った結果になることが多いです。

library(pcalg) #ライブラリの読み込み
library(igraph) #ライブラリの読み込み
setwd("C:/Rtest") # 作業用ディレクトリを変更
Data <- read.csv("Data.csv", header=T) # データを読み込み
#for (i in 1:ncol(Data)) {
# Data[,i] <- (Data[,i] - mean(Data[,i]))/sd(Data[,i]) #データを標準化
#}
Str <- lingam(Data)$Bpruned # LiNGAM
rownames(Str) <- colnames(Data) # 行名を追記
colnames(Str) <- colnames(Data) # 列名を追記
GM2 <- t(abs(Str)) # 絶対値にして転置する
GM3 <- GM2*5/max(GM2) # 値を適度な大きさにする
GM4 <- graph.adjacency(GM3,weighted=T, mode = "directed") # グラフ用のデータを作成
plot(GM4, edge.width=E(GM4)$weight) # グラフを作成

LiNGAM

矢印の元側の変数に誤差が加わって、矢印の先の変数になっていることを表しているグラフができます。

シュール分解を使う方法

行列の分解 の一種に、「シュール分解」と呼ばれるものがあります。

シュール分解では、上三角行列が求まります。

LiNGAMでは、非巡回有向グラフを、三角行列で表せるところがポイントになっているので、 シュール分解を使うLiNGAMのアルゴリズムができるのではないかと思ったのですが、 筆者は、うまく行きませんでした。

ちなみに、Rでは、分解したい行列がAの時に、以下のようにして、上三角行列が求まります。

library(expm)
Schur(A)$T

筆者が試したところ、上三角行列に近い行列が求まるのですが、完ぺきな上三角行列にはなりませんでした。



参考文献

つくりながら学ぶ! Pythonによる因果分析 因果推論・因果探索の実践入門」 小川雄太郎 著 マイナビ出版 2020
LiNGAMの実装は、この本を参考にさせていただきました。


「統計的因果探索」の一部を動かしてみた
山川信之氏のブログです。 pcalgをインストールする前に、別のライブラリのインストールが必要なことが書かれています。 また、LiNGAMの簡単な実施例もあります。
https://techblog.nhn-techorus.com/archives/13805 の3つを紹介しています



Rによるデータ分析



杉原データサイエンス事務所のロゴ
杉原データサイエンス事務所によるコンサルティングとセミナー