スピアマンの順位相関係数を実装 in Scala
この前ピアソンの相関係数を実装したので次はノンパラメトリックなスピアマンの順位相関係数を実装してみた。ピアソンの場合、外れ値の影響を大きく受けるのと、厳密にはデータが正規分布であると仮定している。つまりデータ数が小さい時は正確性に欠ける。翻ってスピアマンの方はノンパラメトリックなのでデータの分布は仮定せず、外れ値の影響を受けづらい。
- スピアマンの計算手順
1. 2つの変数の各要素に順位をつける(大きい順でも小さい順でも)
2. 対となる要素の順位の差を求める
3. それを基に相関係数を求める
4. 分析表と照らし合わせて有意かどうか判断(ここは実装してない)
相関係数の式。diは要素の順位差、nはデータ数。
単純な例を考えてみる。一日の睡眠時間と集中力が持続する時間の平均値 が相関を持っているのか考える。
- 一日の睡眠時間
8 , 4 ,5
- 集中力の時間平均
60 , 30 , 25
まずはそれぞれの変数の各要素に順位づけをする。
(8 , 3) , (4, 1) , (5, 2)
(60 , 3) , (30 , 2) , (25 , 1)
次にそれぞれの順位の差の2乗を求める
(3-3)^2 , (1-2)^2 , (2-1)^2
=0 , 1 , 1
後は相関係数の式に当てはめる。
r = (6 * 2) / (3^3-3)
= 12 / 24
= 0.5
要はこれをコードにするだけ。
def labeling(data:Stream[Double])={val placeI=data.sorted.zipWithIndex.map(a=>(a._1,a._2+1)).toMapdata.map(a=>placeI(a))}def rankzip(data1:Stream[Int],data2:Stream[Int]):Stream[(Int,Int)]=data1.zip(data2)//2つのデータをソートしてzipdef difsqured(zipped:Stream[(Int,Int)]):Stream[Double]=zipped.map(x=>math.pow(x._1-x._2,2))//対となる値の差をそれぞれ求めて2乗するdef spearman(dsdata:Stream[Double])=1-(6*dsdata.reduce{(a,b)=>a+b})/(math.pow(dsdata.length,3)-dsdata.length)//スピアマンの順位相関係数。同じ順位の場合は昇順にしているため若干本来と違う。a:4.5,b:4.5 => a:4,b:5 にしてる。
labelingメソッドが何とも効率悪そうだなあ