相関というのは、ふたつの特徴量の間の線形な関係性を指す。 要するに、こちらが上がればこちらは下がる、みたいなもの。 また、ふたつの特徴量の間にどのくらいの相関があるかを示す数値を相関係数と呼ぶ。 そして、相関係数は「ピアソンの相関係数 (Pearson correlation coefficient)」という方法で計算できる
必要なパッケージをインストールする
今回必要なパッケージはすべて Python のパッケージマネージャの pip でインストールできる。
$ pip install numpy scipy matplotlib
サンプルコード
まず、相関係数はふたつの配列を元に scipy.stats.pearsonr() 関数で計算できる。 返り値はタプルで、第一要素が相関係数、第二要素が有意確率 P 値になる。 相関係数は 1 に近いほど強い相関があることを指していて、有意確率 P 値は 0 に近いほどデータが偶然にそうなった可能性が低いと言える。 一般に、有意確率 P 値は 0.05 (5%) 未満のとき偶然ではないと考えることができるっぽい。
ということで以下が実際のサンプルコード。 データは matplotlib を使って可視化している。
#!/usr/bin/env python # -*- coding: utf-8 -*- from matplotlib import pyplot as plt import scipy as sp from scipy.stats import pearsonr import numpy as np from numpy import random def f(x): ''' y = f(x) x をそのまま返す一次関数 ''' return x def main(): # データ点数 N = 100 # 関数の値を散らす範囲 random_range = 10 # x 軸を -N/2 ~ N/2 の範囲で作る x = np.array(list(range(-(N // 2), (N // 2)))) # y 軸は関数 f() にランダムな値 (ノイズ) をのせたもので作る y = np.array([ f(i) + random.randint(-random_range, random_range + 1) for i in x ]) # x, y 軸の相関係数 r と有意確率 p を求める r, p = pearsonr(x, y) print('相関係数 r: {r}'.format(r=r)) print('有意確率 p: {p}'.format(p=p)) print('有意確率 p > 0.05: {result}'.format(result=(p < 0.05))) # データをグラフにプロットする for i in range(N): plt.scatter(x, y) # 一次関数としてカーブフィッティングしたものもグラフに描いておく fp1, _residuals, _rank, _sv, _rcond = sp.polyfit(x, y, 1, full=True) f_dash = sp.poly1d(fp1) plt.plot(x, f_dash(x), linewidth=2) # グラフを表示する plt.autoscale() plt.grid() plt.show() if __name__ == '__main__': main()
実行結果は次の通り。 データにはランダムな値をノイズとして混ぜているので実行ごとに数値は異なる。 とはいえ有意確率 p からすれば、こんなデータが偶然にできるはずがないということがわかる。
$ python pearsonr1.py 相関係数 r: 0.978627944763307 有意確率 p: 3.9693635369468217e-69 有意確率 p > 0.05: True
描画されるグラフはこんなかんじ。
相関が取れるのはデータが線形な場合のみ
前述した通り、相関はふたつの特徴量の間の線形な関係性を指すため、特徴量が非線形な場合には適用できないらしい。
試しに先ほどのサンプルコードの関数 f() を二次関数にしてみよう。
#!/usr/bin/env python # -*- coding: utf-8 -*- from matplotlib import pyplot as plt import scipy as sp from scipy.stats import pearsonr import numpy as np from numpy import random def f(x): ''' y = f(x) x を自乗して返す二次関数 ''' return x ** 2 def main(): # データ点数 N = 100 # 関数の値を散らす範囲 random_range = 10 # x 軸を -N/2 ~ N/2 の範囲で作る x = np.array(list(range(-(N // 2), (N // 2)))) # y 軸は関数 f() にランダムな値 (ノイズ) をのせたもので作る y = np.array([ f(i) + random.randint(-random_range, random_range + 1) for i in x ]) # x, y 軸の相関係数 r と有意確率 p を求める r, p = pearsonr(x, y) print('相関係数 r: {r}'.format(r=r)) print('有意確率 p: {p}'.format(p=p)) print('有意確率 p > 0.05: {result}'.format(result=(p < 0.05))) # データをグラフにプロットする for i in range(N): plt.scatter(x, y) # 一次関数としてカーブフィッティングしたものもグラフに描いておく fp1, _residuals, _rank, _sv, _rcond = sp.polyfit(x, y, 1, full=True) f_dash = sp.poly1d(fp1) plt.plot(x, f_dash(x), linewidth=2) # グラフを表示する plt.autoscale() plt.grid() plt.show() if __name__ == '__main__': main()
実行結果は次の通り。
$ python pearsonr2.py 相関係数 r: -0.03850082615411235 有意確率 p: 0.7037161946676354 有意確率 p > 0.05: False
描画されたグラフはこんなかんじ。
ぜんぜんだめだね。
まとめ
今回は SciPy を使って特徴量の相関を調べてみた。 相関を調べることで、ふたつの特徴量にどれだけ線形な関係性があるかを確認できる。 そして、有意確率 p からそれが偶然なのかはたまた何か意味があるのかを知ることができる。
参考文献
スマートPythonプログラミング: Pythonのより良い書き方を学ぶ
- 作者: もみじあめ
- 発売日: 2016/03/12
- メディア: Kindle版
- この商品を含むブログを見る