相関係数のサンプリング・シミュレーション
真の相関係数が0.3の場合
相関がある(真の相関係数ρ=0.3)場合の、サンプルサイズが小さいときのサンプル相関係数rの分布について調べる。サンプルサイズNが5の場合を扱うが、これは質的研究などにおいてサンプルをランダムにとって相関係数rを求めた場合、データ数が少ないと相関が認められないことがあるので、注意が必要であることを示すものである。シミュレーションに用いたスクリプトファイルは、圧縮ファイルfiles0p3.zipにまとめた。
真の相関係数がρ=0.3であるとき、サンプルサイズN=5のデータの相関係数を10000回のシミュレーションで求めると、図1のような分布になった。使用したスクリプトはリスト1に示すものである。
図1
ヒストグラムの横軸上に、下記の式(1)および(2)に基づいて求めた帰無仮説「ρ=0」の採択域を示している。
真の相関係数ρが0であるとき、サンプルサイズN=5の相関係数rを次式でtに変換した値
は、自由度のt分布に従う(岡本、2022)。
式(1)をrについて解くと式(2)を得る。
式(1)からt分布の棄却の臨界値を求めることができ、式(2)より対応するサンプル相関係数rの値が求まる。
サンプル相関係数の採択域はと相関係数の値の範囲とほぼ重なっていて、サンプル相関係数rの分布も採択域の上にほとんどがある。すなわち、真の相関係数がρ=0.3であっても、帰無仮説「ρ=0」が棄却されることはほとんどない。
リスト1 相関係数rの分布(r_smpl_distri.py)
import numpy
as np
import scipy.stats
as ss
import matplotlib.pyplot
as plt
N = 5
tp0975 = ss.t.ppf(0.975,
N-2)
def t_r(t, N):
return ((t**2) / ((N-2) +
(t**2))) ** 0.5
r0975 = t_r(tp0975,
N)
r_sample = []
for i in
range(10000):
smpls
= ss.multivariate_normal.rvs(mean= [0, 0],
cov = [[1, 0.3], [0.3, 1]], size = N)
results = ss.pearsonr(smpls.T[0], smpls.T[1])
r_sample.append(results[0])
plt.plot([-r0975, r0975], [0, 0], lw = 10, label = 'NonSig.Interval'
+
'\n[{0:.2f}, {1:.2f}]'.format(-r0975, r0975))
plt.hist(r_sample,
bins = 20)
plt.legend(fontsize =
16)
plt.xlabel('$r$', fontsize
= 20)
plt.title(r'$\rho=0.3,$' + ' ' + '$N=5$', fontsize
= 20)
plt.savefig('FigSmpls.png')
plt.show()
「真の相関係数ρ=0.3」のもとで、サンプルサイズN=5のデータに対して50回のシミュレーションをリスト2のスクリプトにより繰り返し、得られた事後分布のサンプルをリスト3のスクリプトにより分析すると図2のようになった。
図2
真の相関係数ρ=0.3に対応して、事後分布はrの正の領域への偏りが認められるが、負の領域の分布も無視できない。
各事後分布において、正の領域の確率Posと負の領域の確率Negを求め、ヒストグラムを描くと図3のようになった。
図3
事後分布の負の領域の確率が0.05以下である頻度は少ないので、ρの値が正であると推定できることはあまり期待できない。
リスト2のスクリプトによりサンプルに対してベイズ分析をStanで行っている。Stanによる事後分布からのサンプリングに基づいて、リスト3のスクリプトで事後分布の分析を行っている。
StanはPython用のインターフェースPyStan 3を用いている。PyStan 3の使用については、このウェブサイトで説明している。
PyStan3をWindowsで用いるときは、WSL (Windows Subsystem for
Linux)を利用する。WSLについては、このウェブサイトで説明している。
すなわち、Windowsにおいては、リスト2のスクリプトはAnaconda/Ubuntu/WSL/WindowsのPython 3.10仮想環境で実行して、その後、リスト3のスクリプトをWindows上で実行するという手順になる。
なお、PyStanについては拙著<岡本安晴(2019)>で説明している。
リスト2 事後分布をStanにより求めるスクリプト(PostDistCorr.py)
import numpy
as np
import scipy.stats
as ss
#import matplotlib.pyplot
as plt
import seaborn
as sb
import stan
import pickle
import arviz
as az
model_script = """
data {
int N;
vector[2] XY[N];
}
parameters {
real
mu1;
real
mu2;
real<lower = 0> sgm1;
real<lower = 0> sgm2;
real<lower=-1, upper=1> rho;
}
transformed parameters {
vector[2] mus;
matrix[2,2] cov;
mus[1] = mu1;
mus[2] = mu2;
cov[1][1] = sgm1^2;
cov[2][2] = sgm2^2;
cov[1][2] = sgm1*sgm2*rho;
cov[2][1] = cov[1][2];
}
model {
mu1
~ normal(0,10);
mu2
~ normal(0,10);
sgm1
~ exponential(0.01);
sgm2
~ exponential(0.01);
rho
~ uniform(-1, 1);
XY ~
multi_normal(mus, cov);
}
"""
N = 5
rhos = []
for i in
range(50):
smpls
= ss.multivariate_normal.rvs(mean= [0, 0],
cov = [[1, 0.3], [0.3, 1]], size = N)
Data = {'N':N, 'XY':smpls}
sm
= stan.build(model_script,
data = Data)
fit = sm.sample()
infer_data
= az.from_pystan(posterior=fit, posterior_model=sm)
smry
= az.summary(infer_data)
print(smry)
rhos.append(fit['rho'])
with open('fits.pkl',
'wb') as f:
pickle.dump(rhos, f)
リスト3 リスト3の実行結果の描画スクリプト(DrawFit.py)
import numpy
as np
import scipy.stats
as ss
import matplotlib.pyplot
as plt
import seaborn
as sb
import pickle
import arviz
as az
with open('fits.pkl',
'rb') as f:
fits = pickle.load(f)
for fit in fits:
sb.kdeplot(fit[0])
plt.xlabel('$r$', fontsize
= 18)
plt.title('$N = ${0}'.format(5), fontsize = 20)
plt.savefig('FigPosts.png')
plt.show()
p_pos = []
p_neg = []
for fit in fits:
cnt_pos
= [1 if v > 0 else 0 for v in fit[0]]
cnt_neg
= [1 if v < 0 else 0 for v in fit[0]]
p_pos.append(sum(cnt_pos) / len(cnt_pos))
p_neg.append(sum(cnt_neg) / len(cnt_neg))
plt.hist(p_pos,
color = 'y', alpha = 0.5, label = 'Pos')
plt.hist(p_neg,
color = 'b', alpha = 0.5, label = 'Neg')
plt.xlabel('Probability', fontsize
= 16)
plt.legend()
plt.savefig('FigHists.png')
plt.show()
引用文献
岡本安晴(2022)データを使いこなすための統計入門.22世紀アート (電子書籍)
岡本安晴(2019)いまさら聞けないPythonでデータ分析.丸善出版