点を 1 つ足すと相関が 0.98 から -0.29 へ……Python で散布図を描く
10 個の点から相関係数を出すと 0.980238 でした。ここに点を 1 つ足しただけで、値が になります。
正の強い関係が、負の弱い関係に変わりました。データは 10 個から 11 個になっただけです。
数字を出す前に散布図を描く理由が、この 1 例に入っています。
相関係数を出す
NumPy には相関行列を返す関数があります。
import numpy as np
x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
y = np.array([38, 38, 42, 54, 53, 58, 69, 67, 73, 83])
print(np.corrcoef(x, y))[[1. 0.980238]
[0.980238 1. ]]の行列が返ります。対角線は自分自身との相関なので 、知りたい値は非対角の位置にあります。
print(round(float(np.corrcoef(x, y)[0, 1]), 6)) # 0.980238
print(round(float(np.corrcoef(x, y)[0, 1]) ** 2, 6)) # 0.9608672 乗すると決定係数になります。この 10 点では、 のばらつきの 96% が で説明できる形です。
pandas でも同じ値
表として持っているなら、pandas のメソッドが使えます。
import pandas as pd
frame = pd.DataFrame({'x': x, 'y': y})
print(frame.corr())x y
x 1.000000 0.980238
y 0.980238 1.000000列が 3 つ以上あっても、すべての組み合わせが一度に出ます。列名がそのまま行と列の見出しになるので、読みやすい。
順位で測る相関
値そのものではなく順位で計算する方法もあります。引数で切りかえます。
print(frame.corr(method='spearman'))x y
x 1.000000 0.972649
y 0.972649 1.0000000.972649 で、ふつうの相関係数 0.980238 とわずかに違います。
順位に直してから計算するので、外れ値の影響が小さくなります。値が 200 でも 2000 でも、順位が同じなら結果は変わらない。
値の大きさをそのまま使う。直線の当てはまり具合を測る
順位だけを使う。単調に増えているかどうかを測る
曲がっていても単調に増えていれば、順位相関は に近くなります。直線かどうかを問わない形です。
差がはっきり出る例を作ってみます。 を 1 から 10、 を とすると、順位はぴったり対応しているのに、値は右へ行くほど急に増える。
x = np.arange(1, 11)
y = 2 ** x
f = pd.DataFrame({'x': x, 'y': y})
print(round(float(f.corr().loc['x', 'y']), 4)) # 0.7988
print(round(float(f.corr(method='spearman').loc['x', 'y']), 4)) # 1.0ふつうの相関係数は 0.7988、順位相関は 1.0 でした。順位で見れば完全に対応しているので になります。
0.7988 という値だけを見ると「そこそこの関係」に見えますが、実際は が決まれば が一意に決まる関係です。どちらの数字も嘘ではなく、測っているものが違う。
散布図を描く
matplotlib で点を打ちます。
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(6, 4))
ax.scatter(x, y)
ax.set_xlabel('x')
ax.set_ylabel('y')
fig.savefig('scatter.png', dpi=100)matplotlib.use(‘Agg’) は、画面を持たない環境で画像だけを作るための指定です。ファイルに保存するだけなら、これで足ります。
figsize は横と縦の長さをインチで、dpi は 1 インチあたりの画素数を指定します。 画素の横幅になる。
回帰直線を重ねる
最小二乗の直線は np.polyfit で出せます。次数に 1 を渡すと 1 次式になります。
slope, cut = np.polyfit(x, y, 1)
print(round(slope, 4), round(cut, 4)) # 5.0 30.0
ax.plot(x, cut + slope * x)傾き 5.0、切片 30.0 でした。散布図の上にこの直線を重ねると、点が直線のまわりにどう散っているかが見えます。
相関係数 0.980238 という 1 つの数は、この散らばり方を要約したものです。要約する前の形は、図でしか確かめられません。
点が多いときの描き方
数千点を打つと、点が重なって形が見えなくなります。大きさと透明度を下げると、密度が読めるようになる。
rng = np.random.default_rng(0)
x = rng.normal(50, 10, 2000)
y = 0.6 * x + rng.normal(0, 8, 2000)
fig, ax = plt.subplots(figsize=(6, 4))
ax.scatter(x, y, s=8, alpha=0.2)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.grid(True, linewidth=0.3)
fig.tight_layout()
fig.savefig('dense.png', dpi=100)
print(round(float(np.corrcoef(x, y)[0, 1]), 4)) # 0.593s は点の面積、alpha は透明度です。alpha を 0.2 にすると、5 個重なったところでちょうど濃くなります。
default_rng(0) の 0 は乱数の種で、これを固定すると毎回同じデータが出ます。結果を再現できるようにしておくと、あとで確かめ直せる。
grid の線は 0.3 と細くしています。目盛りの線が太いと、点より線のほうが目立ってしまう。
例:1 点で符号が変わる
さきほどの 10 点に を足してみます。
x3 = np.append(x, 30)
y3 = np.append(y, 20)
print(round(float(np.corrcoef(x3, y3)[0, 1]), 4)) # -0.28820.9802 が になりました。 が大きいのに が小さい点を 1 つ置いたので、全体の傾きが引きずられています。
同じ位置に を足すと、こんどは 0.9968 まで上がります。 の平均から遠い点ほど、傾きを決める力が強い。

相関が でも関係はある
反対に、関係があるのに相関係数が になる場合もあります。
curve = np.array([-3, -2, -1, 0, 1, 2, 3])
print(round(float(np.corrcoef(curve, curve ** 2)[0, 1]), 4)) # 0.0なので、 は から完全に決まります。それでも相関係数はちょうど 。
左半分の負の積と右半分の正の積が打ち消し合うためです。直線としてはどちらへも傾いていない、というのが相関係数の言い分になります。
散布図を描けば放物線が見えます。数字だけを見て「関係なし」と結論すると、この形を見のがす。
相関係数が測っているのは直線の当てはまり具合だけです。関係の有無ではありません。
に近い値が出たら、まず図を見る。放物線でも周期的な形でも、直線でなければ に近づきます。
描いてから数える
手順としては、図が先です。
散布図を描く
形と外れ値を目で確かめる
そのうえで相関係数を出す
外れ値が見つかったら、それが測定の誤りなのか、本当にそういう対象があるのかを調べます。落とすかどうかは、その判断のあとで決める。
図を描かずに数字だけを出すと、 という値をそのまま受けとることになります。1 点のせいだと分かるのは、図を見たときだけです。
見るべきところは決まっています。まず全体の形が直線に近いかどうか。次に、群れから外れた点があるかどうか。最後に、縦の散らばりが横の位置によって変わっていないか。
3 つ目は見のがしやすい。 が大きいところだけ縦に大きく散っている形は、回帰の前提が崩れている合図です。
この 3 つを目で確かめてから相関係数を出せば、その数字が何を要約しているかが分かった状態になります。
散布図を描いたら放物線の形をしていて、相関係数が 0.02 でした。どう読みますか。
- と には関係がない
- 直線の関係は弱いが、曲線の関係はある
- 計算をまちがえている
np.corrcoef で 1 行、matplotlib で数行。手間はほとんど同じなので、両方を出す習慣にしておくほうが安全です。














相関係数が測るのは直線の当てはまり具合だけです。対称な放物線では正と負の寄与が打ち消し合い、0 に近い値になります。関係がないという結論は出せません。