よちよちpython

独習 python/Qpython/Pydroid3/termux/Linux

PandasとNumpyでの相関関係の出し方

相関関係について。

NumpyとPandasを使ったそれぞれの方法。



目次




相関関係とは


一方が増えると片方が増える(正の相関)、または減る(負の相関)ような関係をいう。

正の相関関係の例)
車のスピードが速くなると制動距離(停止するまでの距離)も伸びる。

負の相関関係の例)
気温が高ければ氷の溶ける時間が短くなる。

関係をおおざっぱに調べるときに用いる。



相関関係と因果関係は違うことに注意。



相関係数とは


相関係数は相関関係を数値的に示す。
相関係数の公式は「x と y の共分散」を「x の標準偏差と y の標準偏差の積」で割った値。

難しいことは相関係数 - Wikipedia に任せる。

  • 相関係数の値は-1~1の範囲の実数。
  • -1に近いと負の相関関係。
  • 0に近いと相関関係はない。
  • 1に近いと正の相関関係。


とりあえずNumpyとPandasでのやり方を覚えます。



Pandasでの相関係数の出し方


適当なデータを用意してやってみます。

# ライブラリのインポート
import pandas as pd
import matplotlib.pyplot as plt

# スピード
v = [0.0, 16.1, 32.2, 48.3, 64.4, 96.5, 112.6, 128.7]
# 制動距離
l = [0.0, 1.5, 6.1, 13.7, 24.4, 54.9, 74.7, 97.5]
     
# データフレームに変換
df = pd.DataFrame([v,l])
# 表示
df
0 1 2 3 4 5 6 7
0 0.0 16.1 32.2 48.3 64.4 96.5 112.6 128.7
1 0.0 1.5 6.1 13.7 24.4 54.9 74.7 97.5

データはこちらブレーキが効きはじめてから停止するまでに車が走行した距離(制動距離)を推定しよう。 | カシオ教育情報ステーション | CASIO から拝借しました。カシオの関数電卓の使い方のページ。

グラフで見てみる

plt.scatter(v,l)
plt.savefig("seidou.png")
plt.show()

f:id:chayarokurokuro:20200805062315j:plain

二次関数っぽいグラフになっている。



方法1: df.corr()


データフレームオブジェクトでcorr()メソッドを呼び出すと簡単に相関係数が出せる。

df.corr()
0 1 2 3 4 5 6 7
0 NaN NaN NaN NaN NaN NaN NaN NaN
1 NaN 1.0 1.0 1.0 1.0 1.0 1.0 1.0
2 NaN 1.0 1.0 1.0 1.0 1.0 1.0 1.0
3 NaN 1.0 1.0 1.0 1.0 1.0 1.0 1.0
4 NaN 1.0 1.0 1.0 1.0 1.0 1.0 1.0
5 NaN 1.0 1.0 1.0 1.0 1.0 1.0 1.0
6 NaN 1.0 1.0 1.0 1.0 1.0 1.0 1.0
7 NaN 1.0 1.0 1.0 1.0 1.0 1.0 1.0

あれww サクッと相関係数が出せると思ったが…。

データフレームを転置してみる。

df_tenchi = df.T # 転置(行と列の入れ替え)

print("転置")
print(df_tenchi)

print("\n\n相関係数")
print(df_tenchi.corr())
転置
       0     1
0    0.0   0.0
1   16.1   1.5
2   32.2   6.1
3   48.3  13.7
4   64.4  24.4
5   96.5  54.9
6  112.6  74.7
7  128.7  97.5


相関係数
          0         1
0  1.000000  0.966714
1  0.966714  1.000000

今度は上手く行った。
それぞれのSeriesデータは列にする必要がある。

右上と左下の0.966714相関係数です。1に近いので正の相関関係があることがわかる。

3列でも出来るか確認しておきます。

ランダムな8個の値をSeriesで作って、上で転置させた行列の3列目に追加し、同様にcorr()メソッドを呼び出す。

# ランダムな値のSeriesを生成
import numpy as np
s_random = pd.Series(np.random.rand(8))
print(s_random)

# 横に結合
df_new =  pd.concat([df_tenchi,s_random],axis=1)


# 3列のデータフレームの相関係数
df_new.columns = ["スピード","制動距離","ランダム値"] # 列名変更
df_new.corr()
0    0.155548
1    0.188312
2    0.026417
3    0.949294
4    0.201685
5    0.387201
6    0.228247
7    0.075770
dtype: float64
スピード 制動距離 ランダム値
スピード 1.000000 0.966714 -0.025239
制動距離 0.966714 1.000000 -0.150032
ランダム値 -0.025239 -0.150032 1.000000

できた。
リーグ表のように読めば良い。
(-0.025)と(-0.150)が追加されている。ランダムな値のSeriesなので0に近い相関係数になっている。

3列でできたので4列以上でも可能だろう(適当



方法2: SeriesA.corr(SeriesB)


SeriesのAとBの相関係数を求めるときは
A.corr(B)とすれば出る。

df # 元のデータフレームの表示
0 1 2 3 4 5 6 7
0 0.0 16.1 32.2 48.3 64.4 96.5 112.6 128.7
1 0.0 1.5 6.1 13.7 24.4 54.9 74.7 97.5
v = df.iloc[0] # 1行目を取り出す
l = df.iloc[1] # 2行目

print(type(v)) # 配列のタイプを表示
print(v.corr(l)) # 相関係数を表示
<class 'pandas.core.series.Series'>
0.9667137237755117

値が行列ではなく、一つの値で出て来ました。

Seriesではなくリストでも出来るんだろうか?

# スピード
v = [0.0, 16.1, 32.2, 48.3, 64.4, 96.5, 112.6, 128.7]
# 制動距離
l = [0.0, 1.5, 6.1, 13.7, 24.4, 54.9, 74.7, 97.5]

print(type(v)) # 配列のタイプ
print(v.corr(l))
<class 'list'>



--------------------------------------------

AttributeErrorTraceback (most recent call last)

<ipython-input-167-783ca607f4fb> in <module>
      5 
      6 print(type(v)) # 配列のタイプ
----> 7 print(v.corr(l))


AttributeError: 'list' object has no attribute 'corr'

リストは不可でした。



Numpyでの相関係数の出し方


Pandasより簡単。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

複数の配列の相関係数を同時に求める

まずは適当なデータと散布グラフの作成。

# データを適当に作成
x = np.random.rand(10)
y = np.random.rand(10)
z = np.array(x*y)

print("x{}\n\ny{}\n\nz{}".format(x,y,z))

plt.scatter(x,y)
plt.title("x-y")
#plt.show() #グラフ表示省略

plt.scatter(x,z)
plt.title("x-z")
#plt.show() #グラフ表示省略

plt.scatter(y,z)
plt.title("y-z")
#plt.show() # グラフ表示省略
x[0.51932614 0.79372773 0.98804037 0.39314367 0.30385505 0.50589366
 0.16655163 0.65069951 0.78389342 0.86361316]

y[0.78918075 0.49719794 0.67570283 0.54131736 0.23025419 0.20290171
 0.52621891 0.21548246 0.17498025 0.35562021]

z[0.40984219 0.3946398  0.66762168 0.21281549 0.0699639  0.10264669
 0.08764261 0.14021433 0.13716586 0.30711829]


方法: np.corrcoef([配列1, 配列2, ...])


np.corrcoef([配列a,配列b,配列c,...])で一気に行ける。

np.corrcoef([x,y,z])
array([[1.        , 0.03399637, 0.6832166 ],
       [0.03399637, 1.        , 0.71608177],
       [0.6832166 , 0.71608177, 1.        ]])

見やすいようにデータフレームに変換して表で表示すると、

pd.DataFrame(np.corrcoef([x,y,z]),
            columns = ["ランダムx","ランダムy","z=x*y"],
            index = ["ランダムx","ランダムy","z=x*y"])
ランダムx ランダムy z=x*y
ランダムx 1.000000 0.033996 0.683217
ランダムy 0.033996 1.000000 0.716082
z=x*y 0.683217 0.716082 1.000000

これもリーグ表と同じ見方でOK。



外れ値が相関係数に与える大きな影響


ランダム値の配列同士の相関係数は0に近い筈ですが、たったひとつの外れ値を追加することで全体の相関関係が強いものに変化してしまう場合がある。要注意

まずはランダム値同士の相関関係を見てみる

import numpy as np
import matplotlib.pyplot as plt

# 適当にランダム値10個のNumpy配列を2つ生成
a = np.random.rand(10) 
b = np.random.rand(10)

print(a)
print("="*40)
print(b)

# グラフ作成
plt.scatter(a,b)
plt.savefig("hazure.png") #グラフ保存
plt.show() # グラフ表示

# 相関係数
print("\n\n相関係数")
print(np.corrcoef(a,b)[0,1])
[0.63967756 0.4040863  0.91230895 0.97249585 0.68248714 0.95395285
 0.64049097 0.37332778 0.96434299 0.94255304]
========================================
[0.20730458 0.56916243 0.4691992  0.15877408 0.38850535 0.56553429
 0.69204737 0.6063161  0.72459355 0.68380298]


20200805062601


相関係数
-0.06960120492893662

ランダム同士は相関係数が0に近い。aとbの相関関係はほとんどない。



外れ値を追加

2つの配列に外れ値を一つずつ追加する。このたったひとつの値が全体の相関関係を狂わせる。

# ランダム値10個の配列(上で作ったもの)
print(a)
print(b)


# 外れ値(a,b)=(10,10)を末尾に追加
a_new = np.append(a,10)
b_new = np.append(b,10)
print(a_new)
print("="*40) # 表示用の区切り線
print(b_new)

# グラフ作成
plt.scatter(a_new, b_new)
plt.savefig("hazure_2.png")
plt.show()

# 相関係数
print(np.corrcoef(a_new, b_new))
[0.63967756 0.4040863  0.91230895 0.97249585 0.68248714 0.95395285
 0.64049097 0.37332778 0.96434299 0.94255304]
[0.20730458 0.56916243 0.4691992  0.15877408 0.38850535 0.56553429
 0.69204737 0.6063161  0.72459355 0.68380298]
[ 0.63967756  0.4040863   0.91230895  0.97249585  0.68248714  0.95395285
  0.64049097  0.37332778  0.96434299  0.94255304 10.        ]
========================================
[ 0.20730458  0.56916243  0.4691992   0.15877408  0.38850535  0.56553429
  0.69204737  0.6063161   0.72459355  0.68380298 10.        ]

f:id:chayarokurokuro:20200805062543j:plain

[[1.         0.99433687]
 [0.99433687 1.        ]]

グラフの左下に散らかる点のグラフに、右上(10,10)に一つの点が追加された。これにより、1に近い相関係数が出た。
外れ値をたった一つ追加しただけで、相関関係が「ない」⇒「強い正の相関関係」と、評価が真逆に変わってしまった。

元は相関関係がないので無視してよい特徴量だったのを、外れ値のせいで強い相関関係があると見誤ってしまうと不要な計算をする羽目になるということか。
毎回散布図で確認した方が良さそうです。
データの前処理がいかに重要かですね。



でも、もしかしたらその外れ値はブラックスワンかも…
群雄割拠の中に現れた一筋の光かも…



関連リンク(追記)

qiita.com

こちらで、相関係数が0.63になるデータを出力する関数を作って実験しておられます。更にそこから、データを1つ削除すると相関係数が0.84になる関数も。
データがたった1つ減った(または加えられた)だけで相関係数が大きく変化してしまうと、係数や分析の信頼ってどうなのよ?と考えさせられます。統計学をやっとかないといかんですね。リアルな現場で使えない。



以上。