K_DM Book
用語集
対象者 標準
対象者別の入口 →

対象者の設定はこのブラウザの中にだけ保存されます。

言語 JA

相関係数

入門

03.2.1

相関係数

最終更新 2020-01-29 読了時間 4 分
まとめ
相関係数(ピアソンの積率相関係数)\(r\) は、2 つの変数の共分散をそれぞれの標準偏差の積で割った値で、散布図の点がどれだけ右上がり(または右下がり)の直線に沿って並んでいるかを −1〜1 で表します。np.corrcoef(x, y) が返す 2×2 行列の非対角成分がその値で、点が 1 本の直線に乗れば ±1、直線的な傾向がなければ 0 付近になります。測れるのは直線的な関係だけです。同心円や三日月形に並んだ点では明らかな構造があっても 0 付近になり、外れ値 1 個で値が大きく動きます。また、アイスと日焼け止めの売上のように「季節」という第三の要因で一緒に動くだけの擬似相関も 0.9 を超えるため、相関があっても因果があるとは言えません。

直感:十字を引いて符号を数える #

散布図に \(x\) の平均で縦線、\(y\) の平均で横線を引くと、平面は 4 つに分かれます。右上と左下の区画にある点は偏差の積 \((x_i - \bar{x})(y_i - \bar{y})\) が正、左上と右下の点は負です。全部足して平均したのが共分散で、右上・左下に点が多ければ正、4 区画に散らばっていれば打ち消し合って 0 に近づきます。これを \(x\) と \(y\) の標準偏差の積で割って単位を消したものが \(r\) で、だからどんなデータでも −1 から 1 の間に収まります。

定義と性質 #

標本相関係数は、2 つの変数 \(x, y\) の共分散を標準偏差で正規化した値です。

$$ r = \frac{\sum_{i=1}^n (x_i - \bar{x})(y_i - \bar{y})}{\sqrt{\sum_{i=1}^n (x_i - \bar{x})^2} \, \sqrt{\sum_{i=1}^n (y_i - \bar{y})^2}} $$

値は必ず \(-1 \le r \le 1\) に収まります。\(r > 0\) なら片方が大きくなるともう片方も増える直線的傾向、\(r < 0\) なら片方が大きくなるともう片方は減る傾向で、\(|r| = 1\) はすべての点が 1 本の直線に乗った完全な線形関係です。直線的な関係の強さしか測れないため、放物線や円のような非線形なパターンは捉えられず、外れ値 1 点の影響も強く受けます。


何が起きているか(ステップで見る) #

コードを読む前に、この手法が図の上で何をしているかを追ってみましょう。「再生」を押すか、スライダーを動かすと 1 手順ずつ進みます。

見るポイント:r は「直線に沿う強さ」。曲線的な関係や外れ値があると、r だけでは関係を見誤る(同心円で r ≈ 0.00)。

Python で相関係数を計算する #

まず、ほぼ直線に沿った 40 点を人工的に作り、np.corrcoef で相関行列を出します。

1
2
3
4
5
6
7
8
import numpy as np

np.random.seed(777)
x = np.linspace(0, 100, 40) + np.random.rand(40)
y = np.linspace(1, 50, 40) + np.random.rand(40)

coef = np.corrcoef(x, y)
print(np.round(coef, 3))

数値を見ただけでは「直線に沿っている」ことは分からないので、同じデータを散布図にします。

1
2
3
4
5
6
7
8
9
import matplotlib.pyplot as plt

plt.figure(figsize=(5, 5))
plt.scatter(x, y)
plt.xlabel("x")
plt.ylabel("y")
plt.title("Scatter plot and correlation coefficient")
plt.grid(alpha=0.3)
plt.show()
線形関係を持つ 2 変数の散布図

相関係数は右肩上がりの散布図で 1 に近い値になる。

np.corrcoef が返す行列のオフ対角成分が相関係数です。散布図が右肩上がりで値が 1 に近い場合、強い正の相関と判断できます。


複数変数の相関を俯瞰する #

変数が 4 つ以上あるときは、すべてのペアの相関係数を並べた相関行列を作ります。iris データの 4 つの数値列で試します。

1
2
3
4
5
import seaborn as sns

iris = sns.load_dataset("iris")
correlation = iris.corr(numeric_only=True)
correlation

pandas.DataFrame.corr で相関行列、Styler.background_gradient でヒートマップ表示が可能です。1 つの変数に注目するなら、その行を棒グラフにすると、どの変数と強く相関しているかが一目で分かります。

1
2
3
4
5
6
7
import matplotlib.pyplot as plt

correlation["sepal_length"].plot.bar(
    grid=True, ylabel="corr", color="#2563eb", title="Correlation with sepal_length"
)
plt.tight_layout()
plt.show()
アヤメデータの相関ヒートマップと棒グラフ

相関行列と棒グラフを併用すると、どの変数と強く相関しているかが把握しやすい。


相関係数だけでは捉えられない関係 #

相関係数は線形性の指標なので、非線形構造やクラスター構造を持つデータでは、値が 0 付近でも明確なパターンが存在することがあります。同心円と三日月形の 2 つのデータで確かめます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets

circle, _ = datasets.make_circles(n_samples=1_000, factor=0.1, noise=0.05, random_state=0)
moon, _ = datasets.make_moons(n_samples=1_000, noise=0.05, random_state=0)

def scatter_with_corr(ax, data, title):
    corr = np.corrcoef(data[:, 0], data[:, 1])[0, 1]
    ax.scatter(data[:, 0], data[:, 1], s=10, alpha=0.6)
    ax.set_title(f"{title}\ncorrelation={corr:.2f}")
    ax.set_xticks([])
    ax.set_yticks([])

fig, axes = plt.subplots(1, 2, figsize=(10, 4))
scatter_with_corr(axes[0], circle, "Concentric circles")
scatter_with_corr(axes[1], moon, "Moon-shaped data")
plt.tight_layout()
plt.show()
非線形構造を持つデータと相関係数

視覚的には明らかな関係でも、相関係数は 0 付近になりがち。

どちらも視覚的には明らかな関係がありますが、相関係数は 0.00 と −0.45 で「関係がない」と誤解されかねません。曲線的・単調な関係を捉えたいならスピアマン相関、形を問わず依存を測りたいなら距離相関や相互情報量を検討します。


相関と因果は異なる #

相関係数が 0.9 を超えても、片方がもう片方を引き起こしているとは限りません。同じ背景要因を共有しているだけの「擬似相関」や、因果の向きが逆のケースがあります。次の例では、季節性(共通の隠れ要因)によって変化する 2 つの数列が 0.9 を超える正の相関を示しますが、互いに因果関係はありません。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(123)
days = np.arange(365)
season = np.sin(2 * np.pi * days / 365)  # 季節を表す共通要因

ice_sales = 50 + 30 * season + np.random.normal(scale=5, size=days.size)
sunscreen_sales = 40 + 35 * season + np.random.normal(scale=6, size=days.size)

correlation = np.corrcoef(ice_sales, sunscreen_sales)[0, 1]
print(f"Correlation = {correlation:.3f}")

plt.figure(figsize=(6, 5))
plt.scatter(ice_sales, sunscreen_sales, alpha=0.6, s=18, color="#f97316")
plt.xlabel("Ice cream sales")
plt.ylabel("Sunscreen sales")
plt.title("Spurious correlation driven by the same seasonality")
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()
季節性が生み出す擬似相関の散布図

共通の季節要因によってアイスクリームと日焼け止めの売上が同時に増える例。互いが互いを引き起こしているわけではない。

散布図は右上がりですが、両者を同時に増加させているのは「季節」という第三の要因です。相関は因果を示さない(Correlation does not imply causation)ので、原因を推定したい場合はランダム化実験や介入、DAG や傾向スコアなどの統計的因果推論を適用します。変数間の関係を説明するときは、共通要因、時間の前後関係、測定バイアスの 3 点を必ず確かめます。


実務での活用と注意点 #

前処理では、特徴量どうしの相関係数が 0.9 を超えるペアを多重共線性の候補として片方を落とす、といった特徴選択の目安に使います。外れ値 1 個で値が 0 から 0.8 まで動くことがあるため、数値だけで判断せず散布図や箱ひげ図を併せて確認します。サンプル数が 20 件程度と少ないと推定誤差が大きいので、信頼区間や p 値も見ます。ピアソン(線形)、スピアマン(順位・単調)、距離相関(非線形)と複数の視点で測ると、1 つの数値に引きずられずにモデル評価や説明に使えます。


まとめ #

相関係数 \(r\) は、共分散を標準偏差の積で割って −1〜1 に収めた「散布図の点が直線に沿う強さ」で、np.corrcoef(x, y)[0, 1] の 1 行で求まります。測れるのは直線的な関係だけです。同心円(\(r \approx 0.00\))や三日月形(\(r \approx -0.45\))のように形があっても 0 付近になり、外れ値 1 点で値が大きく動きます。アイスと日焼け止めの売上のように第三の要因(季節)で一緒に動くだけでも 0.9 を超えるため、相関があっても因果があるとは言えません。散布図・相関行列・スピアマン相関を併用し、「何と何が、どの形で、なぜ一緒に動くのか」まで確かめて解釈します。


ノイズと各指標の変化 #

ノイズ量を 0 から 3.0 まで動かすと、相関係数や R² などの各指標がどう変化するか確認できます。点の帯が太くなるほど \(r\) は 1 から離れていきます。