CUBE SUGAR CONTAINER

技術系のこと書きます。

Python: SciPy で特徴量の相関を調べる

相関というのは、ふたつの特徴量の間の線形な関係性を指す。 要するに、こちらが上がればこちらは下がる、みたいなもの。 また、ふたつの特徴量の間にどのくらいの相関があるかを示す数値を相関係数と呼ぶ。 そして、相関係数は「ピアソンの相関係数 (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:id:momijiame:20150928212405p:plain

相関が取れるのはデータが線形な場合のみ

前述した通り、相関はふたつの特徴量の間の線形な関係性を指すため、特徴量が非線形な場合には適用できないらしい。

試しに先ほどのサンプルコードの関数 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

描画されたグラフはこんなかんじ。 f:id:momijiame:20150928212326p:plain

ぜんぜんだめだね。

まとめ

今回は SciPy を使って特徴量の相関を調べてみた。 相関を調べることで、ふたつの特徴量にどれだけ線形な関係性があるかを確認できる。 そして、有意確率 p からそれが偶然なのかはたまた何か意味があるのかを知ることができる。

参考文献