ベイズ流T検定 -BEST-
ここ最近pymcを使ったベイズ推論のことをずっと考えています(2018年6月執筆当時)。pymc3を扱った本(http://amzn.asia/1TWcfTI)が先週発売されて買ってしまったのですが、まだ前のpymc本(http://amzn.asia/8CPISDp)を読み切っていなかったので急いで読み切りました。読み切った記念に一個面白そうなものがあったのでまとめました。
概要¶
2群の平均値に差があるかどうか比べたいというよくある問いに対しては一般にt検定が使われます。当分散性が言えるとき(2郡の分散が同じである確信が持てるとき)はStudent's t-testを使います。
当分散性が言えないときは(自信がないときも保守的に)Welch's t-testを使います。余談ですが、どちらのt-testも平均値が正規分布に従うことを仮定しているので、その仮定が怪しいときはノンパラメトリックな検定を選びます。この辺りは誰かがコード付きでまとめてくれると信じてます。
このt検定ですが、ベイズ風のアレンジの論文が存在するようです。 BEST(Bayesian estimation supersedes the t-test) というかっこいい名前です。
Kruschke, John. (2012) Bayesian estimation supersedes the t-test. Journal of Experimental Psychology: General.
2群それぞれを独立にt分布に従うと仮定し、それぞれ3つのパラメーター(平均、分散、外れ値) を推論します。つまり、6つのパラメーターを推論します。ベイズ推論に落とし込むことで、2群それぞれの平均、分散、外れ値の解釈が容易になります。
Bayesian estimation supersedes the t-test (BEST)¶
以下ではpymc3を用いてBESTを行います。まず、pymc3をpipでインストールしておきましょう:pip install pymc3
(参考)
- https://docs.pymc.io/notebooks/BEST.html
- 「Pythonで体験するベイズ推論」 Cameron Davidson-Pilon / 玉木 徹
%matplotlib inline
import numpy as np
import pymc3 as pm
import pandas as pd
import matplotlib.pyplot as plt
plt.style.use('seaborn-darkgrid')
print('Running on PyMC3 v{}'.format(pm.__version__))
drug = (101,100,102,104,102,97,105,105,98,101,100,123,105,103,100,95,102,106,
109,102,82,102,100,102,102,101,102,102,103,103,97,97,103,101,97,104,
96,103,124,101,101,100,101,101,104,100,101)
placebo = (99,101,100,101,102,100,97,101,104,101,102,102,100,105,88,101,100,
104,100,100,100,101,102,103,97,101,101,100,101,99,101,100,100,
101,100,99,101,100,102,99,100,99)
y1 = np.array(drug)
y2 = np.array(placebo)
y = pd.DataFrame(dict(value=np.r_[y1, y2], group=np.r_[['drug']*len(drug), ['placebo']*len(placebo)]))
y.hist('value', by='group')
y.head()
mu_m = y.value.mean()
mu_s = y.value.std() * 2
with pm.Model() as model:
group1_mean = pm.Normal('group1_mean', mu_m, sd=mu_s)
group2_mean = pm.Normal('group2_mean', mu_m, sd=mu_s)
sigma_low = 1
sigma_high = 10
with model:
group1_std = pm.Uniform('group1_std', lower=sigma_low, upper=sigma_high)
group2_std = pm.Uniform('group2_std', lower=sigma_low, upper=sigma_high)
with model:
nu = pm.Exponential('nu_minus_one', 1/29.) + 1
pm.kdeplot(np.random.exponential(30, size=10000),
fill_kwargs={'alpha':0.5})
with model:
lam_1 = group1_std**-2
lam_2 = group2_std**-2
group1 = pm.StudentT('drug', nu=nu, mu=group1_mean, lam=lam_1, observed=y1)
group2 = pm.StudentT('placebo', nu=nu, mu=group2_mean, lam=lam_2, observed=y2)
with model:
diff_of_means = pm.Deterministic('difference of means', group1_mean - group2_mean)
diff_of_stds = pm.Deterministic('difference of stds', group1_std - group2_std)
effect_size = pm.Deterministic('effect size',
diff_of_means / np.sqrt((group1_std**2 + group2_std**2) / 2))
model
パラメータの解釈¶
t分布は3つのパラメータ$\mu$, $\sigma$, $\nu$ がある。
- $\mu$ : 平均値。正規分布からサンプルする。
- $\sigma$ : 分散。一様分布からサンプルする。
- $\nu$ : 外れ値の観測しやすさ。指数分布を+1shiftした分布からサンプルする。
このt分布をgroup1, group2に対して推論する。
with model:
trace = pm.sample(2000, cores=2)
pm.plot_posterior(trace, var_names=['group1_mean','group2_mean', 'group1_std', 'group2_std', 'nu_minus_one'],
color='#87ceeb');
pm.plot_posterior(trace, var_names=['difference of means','difference of stds', 'effect size'],
ref_val=0,
color='#87ceeb');
difference of meansは95%以上が0を超えているので、優位にgroup1のほうが高い事がわかる。
pm.forestplot(trace, var_names=['group1_mean',
'group2_mean']);
pm.forestplot(trace, var_names=['group1_std',
'group2_std',
'nu_minus_one']);
pm.summary(trace, var_names=['difference of means', 'difference of stds', 'effect size'])
pm.summary(trace)
事後分布の生成¶
サンプルしたパラメーターを用いてt分布の事後分布を作成する。pm.sample_posterior_predictive
を使う。
ppc = pm.sample_posterior_predictive(trace, samples=50000, model=model)
# import seaborn as sns
# sns.kdeplot(ppc['drug'].flatten(), bw = 10)
# sns.kdeplot(ppc['placebo'].flatten(), bw = 10)
# plt.hist(ppc['drug'], bins=1000, density=True, label='drug', alpha=.5)
# plt.hist(ppc['placebo'], bins=1000, density=True, label='placebo', alpha=.5)
plt.hist(ppc['drug'][:, 0], bins=1000, density=True, label='drug', alpha=.5)
plt.hist(ppc['placebo'][:, 0], bins=1000, density=True, label='placebo', alpha=.5)
plt.xlim(70,150)
plt.legend()
plt.show()
普通のT検定¶
分散が違うことがわかっているのでWelch's t-testをやってみます。
y1
from scipy import stats
res = stats.ttest_ind(y1, y2, equal_var = False)
res
優位性示せず。
import seaborn as sns
plt.figure(figsize=(4,8))
sns.violinplot(x='group', y='value', data=y)#, scale="count", inner="stick")
- 前の記事 : いまさらGoogle Colaboratory入門
- 次の記事 : Kallistoを用いたRNA-seq解析パイプライン
- 関連記事 :