🐍

Pythonで因果推論(1)~相関関係と因果関係と疑似相関~

2022/05/27に公開

はじめに

相関や因果、疑似相関の発生パターンを具体例やPythonによる実装を交えてまとめました。タイトルには"因果推論"とありますが、今回の記事は因果推論の手法に関する内容ではなく、因果推論を行うにあたっての事前知識の確認的なものとなっております。内容について誤り等がありましたら、コメントにてご指摘いただけますと幸いです。

相関関係

2つの変数の傾向を表した関係。

2つの変数X, Yがあったときに、

  • Xが大きいほど、Yも大きい」とき「正の相関がある」
  • Xが大きいほど、Yは小さい」とき「負の相関がある」

と表現します。

具体例: ビールの売上(X)とアイスクリームの売上(Y)の関係

擬似的に作成した、とある飲食店のビールの売上ととあるスーパーのアイスクリームの売上の散布図(下図)を見てみると、あたかも「ビールの売上(X)が高くなると、アイスクリームの売上(Y)が高くなる」ように見えますね。

しかし、「飲食店でビールを飲んだから、スーパーでアイスクリームを買った」とは考えづらいですよね。その逆も然りで、「スーパーでアイスクリームを買ったから、飲食店でビールを飲んだ」と考えるのも不自然です。

このような場合に、「ビールの売上とアイスクリームの売上には正の相関がある」と考えます。

因果関係

2つの変数において、一方が原因、もう一方が結果となっている関係。

2つの変数Z, Yがあったときに、

  • 他の条件を一定としたときに、Zを変化させるとYも変化する」とき「変数Zから変数Yに因果がある」

と表現します。

具体例: 学習時間(Z)と試験の成績(Y)の関係

疑似的に作成した、あるクラスの学生30人の学習時間と試験の成績の散布図(下図)を見てみると、「学習時間が長ければ長いほど、試験の成績も高くなっている」ように見えると思います。

先ほどの「ビールの売上とアイスクリームの売上の関係」とは異なり、「学習時間が長いから、試験の成績も高い」というのは直感的にも納得できると思います。

また、このように因果関係がある場合、(他の条件を全く同じにして)学習時間だけ増加させると、試験の成績も高くなります。例えば、もとの学習時間における試験の成績と、すべての学生が一律に学習時間を20時間増加させた時の試験の成績を箱ひげ図で比較してみると、下記の通りになります。

明らかに、学習時間を20時間増加させた方が、試験の成績が増加しているということが分かると思います。

疑似相関

直接の因果関係のない2つの変数に相関関係が見られること。

疑似相関の生じ方は、大きく以下の3パターンが存在します。

逆の因果

一見「変数Zから変数Yに因果がある」と思いきや、実際には「変数Yが変数Zに影響を与えている」というパターン。この場合、変数Zの値を変化させても変数Yの値は変化せず、。逆に変数Yの値を変化させると変数Zの値は変化します。

具体例: 警察官の数が多い(Z)地域と犯罪の発生件数(Y)の関係

逆の因果については「警察官が多い地域ほど、犯罪の発生件数が多い」という話が有名です。

この文章だけ読むと、まるで「警察官が多いから、犯罪の発生件数が多い」と言っているようですが、本当にそうなんでしょうか?いいえ、違います。逆なんです。「犯罪の発生件数が多いから、警察官をたくさん配置した」というのが正しい因果なんです。

Pythonによる実装

Pythonを使って、逆の因果によって発生する疑似相関を散布図で表現してみます。

2つの変数ZYのデータの詳細

  • Yは、標準正規分布に従うものとする(Zに依存しない)
  • Zは、Z = 2Y + noiseで表わされるものとする(Yに依存する)
  • noiseは一様分布(-1, 1)に従うものとする
# ライブラリのインポート
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import japanize_matplotlib

# シードの設定
np.random.seed(0)
# データのサイズ
size = 30

# 変数Zの生成(Yに依存しない)
Y = np.random.randn(size)
# 変数Yの生成(Zに依存する)
Z_noise = np.random.uniform(-1, 1, size)
Z = 2*Y + Z_noise

# 散布図をプロット
plt.scatter(Z, Y)
plt.xlabel("Z")
plt.ylabel("Y")
plt.show()

(出力結果)

散布図は変数Zと変数Yの正の相関を示していますが、因果の向きは判別できないことが分かると思います。

交絡因子

共通する別の変数Xから、原因を表す(と思われている)変数Zと結果を表す変数Yの両方が影響を受けているパターン。この共通変数Xのことを、交絡因子と呼びます。

具体例: メタボ診断(Z)と長生き(Y)の関係

「メタボ診断を定期的に受ける人ほど長生きする」というデータがあります。一見これは因果関係のようですが、実は違うんです。

もし、これが因果関係であるならば「メタボ診断を受けたから、寿命が伸びた」ということになります。しかし、よくよく考えてみるとメタボ診断そのものが寿命に何か働きかけるということはないですよね?このような場合には交絡因子が存在しているパターンが多々あります。

今回の例で言うと、「そもそも健康への意識が高いからメタボ診断を受けており、健康への意識が高いから長生きする」というように、メタボ診断と長生きに対して「健康への意識の高さ」という交絡因子が存在していると考えられるのです。

Pythonによる実装

Pythonを使って、交絡因子によって発生する疑似相関を散布図で表現してみます。

交絡因子Xと、2つの変数ZYのデータの詳細

  • Xは、標準正規分布に従うものとする
  • Zは、Z = 5X + noiseで表わされるものとする(交絡因子Xに依存する)
  • Yは、Y = 5X + noiseで表わされるものとする(交絡因子Xに依存する)
  • noiseは一様分布(-1, 1)に従うものとする
# ライブラリのインポート
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import japanize_matplotlib

# シードの設定
np.random.seed(0)
# データのサイズ
size = 30

# 交絡因子
X = np.random.randn(size)
# 変数Zの生成
Z_noise = np.random.uniform(-1, 1, size)
Z = 5*X + Z_noise
# 変数Yの生成
Y_noise = np.random.uniform(-1, 1, size)
Y = 5*X + Y_noise

# 散布図をプロット
plt.scatter(Z, Y)
plt.xlabel("Z")
plt.ylabel("Y")
plt.show()

(出力結果)

変数Zと変数Yには直接の因果関係にはないにもかかわらず、散布図は変数Zと変数Yの正の相関を示していることが分かると思います。

合流点での選抜

もともとは関係性がなく独立で、相関関係がなかったにもかかわらず、合流点で選抜されたデータにあたかも相関関係が生じてしまっている状態。

具体例: 合格者の数学(X)と英語(Y)の得点の関係

ある学校は、数学と英語の入学試験(各々100点満点)を課しており、その合計点が120点以上の受験者を合格としています。

このとき、受験者全体の数学と英語の得点間に相関関係が見られないにもかかわらず、合格者だけの数学と英語を見てみると、数学と英語の得点にまるで相関関係があるように見えることがあります。これを合流点での選抜と言います。

本当にそうなるのか、Pythonにて実装しながら確認していきます。

Pythonによる実装

Pythonを使って、合流点での選抜によって発生する疑似相関を散布図で表現してみます。

数学の得点Xと英語の得点Yのデータの詳細

  • 数学の得点Xは、平均55, 標準偏差20の正規分布に従うとする
  • 英語の得点Yは、平均65, 標準偏差15の正規分布に従うとする
  • 数学と英語の得点の合計が120点以上を合格とする

まずは、データを生成し、受験者全員の数学および英語の得点の散布図を描画してみます。

# ライブラリのインポート
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import japanize_matplotlib

# シードの設定
np.random.seed(0)
# データのサイズ
size = 100

# 数学の得点
X_noise = np.random.normal(55, 20, size)
X = np.clip(X_noise, 0, 100)
# 英語の得点
Y_noise = np.random.normal(65, 15, size)
Y = np.clip(Y_noise, 0, 100)

# 受験者全員の数学と英語の得点を散布図をプロット
plt.scatter(X, Y)
plt.title("受験者全員の数学と英語の得点")
plt.xlabel("数学の得点")
plt.ylabel("英語の得点")
plt.xlim(0, 105)
plt.ylim(0, 105)
plt.show()

(出力結果)

散布図を見ると、受験者全員の数学と英語の得点間には相関がありません。

次に、数学と英語の合計点が120点以上の受験者を合格者とし、合格者だけの数学と英語の得点の散布図をプロットしてみます。

# 合流点を作成
total = X + Y

# 合格者だけを格納するための空のarrayを用意
X_passed = np.array([])
Y_passed = np.array([])

# 合格者の数学と英語の得点をそれぞれ格納
for i in range(size):
    if total[i] >= 120:
        X_passed = np.append(X_passed, X[i])
        Y_passed = np.append(Y_passed, Y[i])
        
# 合格者の数学と英語の得点の散布図を描画
plt.scatter(X_passed, Y_passed)
plt.title("合格者の数学と英語の得点")
plt.xlabel("数学の得点")
plt.ylabel("英語の得点")
plt.xlim(0, 105)
plt.ylim(0, 105)
plt.show()

(出力結果)

こちらの散布図を見ると、まるで「数学の得点が高い人は、英語の得点が低い」あるいは「英語の得点が高い人は、数学の得点が低い」というような負の相関が存在しているように見えます。これが合流点による選抜によって現れる疑似相関です。

参考文献

おわりに

データ分析を行っていると、多かれ少なかれ「ただの相関関係なのか、はたまた因果関係なのか」を考えなければならない場面が出てきます。そのような場面で、どのようなメカニズムで疑似相関が発生するのかを理解していると、因果関係ミスリーディングリスクを大きく下げることができるはずです。

他にも下記のような記事を書いています。ご一読いただけますと幸いです。

また、過去にLTや勉強会で発表した資料は下記リンクにまとめてあります。ぜひ、ご一読くださいませ。
https://speakerdeck.com/s1ok69oo

Discussion