Pythonプログラミング(ステップ7・統計計算・相関係数の検定)
このページでは、相関係数の検定方法について考える。(書きかけ)
ピアソンの相関係数の検定
2つの量に線形な関係性がある場合のモデル:最小二乗法
ある変量$Y$が、平均値0のガウス的なゆらぎ$\xi$を伴いながら、$X$と線形な関係にある場合、$i$番目のデータ点 $(x_i,y_i)$ は $$ y_i = a x_i + b + \xi $$ とモデル化することができて、最小二乗法を使うと、2つのパラメータは $$ a = \frac{C_{XY}}{V_{X}} $$ および $$ b = \bar{Y} - a \bar{X} $$ となる。ここで、サンプル平均を $$ \bar{X} = \frac{1}{n} \sum_i x_i, \; \bar{Y} = \frac{1}{n} \sum_i y_i $$ 二乗偏差を $$ \begin{eqnarray} V_X = \sum_i (x_i-\bar{X})^2,\; V_Y = \sum_i (y_i-\bar{Y})^2, \\ C_{XY} = \sum_i (x_i - \bar{X}) (y_i - \bar{Y}) \end{eqnarray} $$ と、それぞれ置いた。
また、確率的なゆらぎ $\xi$ が正規分布する場合、パラメータ $a$ も正規分布し、その不偏分散は $$ \begin{eqnarray} {U_a}^2 & = & \frac{1}{n-2} \frac{\sum_i (y_i - a x_i - b)^2}{V_X} \\ & = & \frac{1}{n-2} \frac{1}{V_X} \sum_i \left( y_i - \bar{Y} - \frac{C_{XY}}{V_{X}} (x_i - \bar{X}) \right)^2 \end{eqnarray} $$ となる(こちらを参照)。 ここでは、残差が正規分布することを想定しているので、${U_a}^2$(を母分散で正規化した量)はカイ二乗分布に従う。 その際、パラメータ $a$, $b$ を決める過程で $\{y_i\}$ に拘束条件が2つ課せられていることから、その自由度は$n-2$となる。
「無相関=勾配の期待値が0」とみなして相関の有無を検定
ここで、もし$X$と$Y$の間に線形な相関関係が存在しない場合、傾き $a$ の期待値は0となるはずである。 そこで、帰無仮説として『$a$の平均が0である』を設定してみよう。そして、 $$ t = \frac{a - 0}{U_a} $$ によってt統計量を定義し、それを使って平均値の検定を行う。 ${U_a}^2$ は自由度 $n-2$ のカイ二乗分布に従うことから、$t$ は自由度 $n-2$ のt分布に従う。
ピアソンの相関係数 $r$ が $$ r = \frac{C_{XY}}{ \sqrt{V_X V_Y} } $$ であることに注意しながら、上記のt統計量を変形すると(少々煩雑な計算を経て)、 $$ t = \frac{r \sqrt{n-2}}{\sqrt{1-r^2}} $$ を得る。すなわち、相関係数 $r$ とデータの件数 $n$ を使って、帰無仮説(相関係数0)の検定を行うことができる。
スピアマンの順位相関係数の検定
スピアマンの順位相関についても、データの件数が十分大きければ($n \gt 20$ がひとつの目安と書かれている文献が多い)、 ピアソンの相関係数の検定と同様に、t統計量 $$ t = \frac{r_s \sqrt{n-2}}{\sqrt{1-{r_s}^2}} $$ によって検定が行われている。 ただし、正確には順位差のゆらぎは正規分布しないので、件数が少ない場合は、こうした扱いをすることはできない。
「順位」に見立てた1から$N$までの自然数がランダムに並んだ2つのリストを用意し、順位差の自乗和から
スピアマンの順位相関係数$r_s$を求める試行を多数回行って、$r_s$の分布を求めるコードの例を以下に示す。
リストは毎回シャッフルされるので、順位に相関は無い。
そのような状況で、観測値 robs
よりも大きな値が得られた回数をカウントし、p値を推定している。
原理的にこの方法ではp値が 1/(試行回数) 以下であるようなケースを評価することはできない。
順位を無相関にするには、リストの一方をシャッフルすれば十分であるが、
右のコードはx, y
の両方をシャッフルしている。
# coding: utf-8 import matplotlib.pyplot as plt import math import numpy as np from scipy.stats import norm from scipy.stats import t N = 20 robs = 0.5 ntrial=10000 x = np.arange(1,N+1) y = np.arange(1,N+1) tab = [] count=0 for _ in range(ntrial): np.random.shuffle(x) np.random.shuffle(y) d2=0 for k in range(0,N): d2 += (x[k]-y[k])**2 rs = 1 - 6/(N**3-N)*d2 if abs(rs) > abs(robs): count += 1 tab.append(rs) print("#sample=",N) print("rs=",robs) print("p-value=",count/ntrial) xaxis = np.arange(-1, 1, 0.01) rv1 = norm(scale=1.0/math.sqrt(N-1)) pdf1 = rv1.pdf(xaxis) taxis = xaxis*np.sqrt(N-2)/np.sqrt(1-xaxis**2) rv2 = t(N-2) pdf2 = rv2.pdf(taxis) * np.sqrt(N-2)*(1 + 2*xaxis**2 * np.sqrt(1 - xaxis**2)**(-3)) plt.title("SPEARMAN'S RANK CORRELATION") plt.hist(tab, bins=60, density=True, color=(1.0,0,0.0), label="N="+str(N)) plt.plot(xaxis,pdf1,'-',color=(0.0,0,1.0), label="Normal") plt.plot(xaxis,pdf2,'-',color=(0.0,1.0,1.0), label="t") plt.legend() plt.xlim(-1,1) plt.xlabel('rs') plt.ylabel('p(rs)') plt.grid(True) plt.show()
順位が全く無相関な場合、データ数が多くなると、$r_s$ の分布は平均0、分散 $1/(n-1)$ の正規分布で近似できる。 上のPythonコードでは、$N=20$の場合について10000回の試行を行って得た$r_s$の分布と、対応する正規分布(青色)、および t分布(水色; $t$を$r_s$に変数変換)を重ねて描いている。右図はその結果である。