ベイズファクターを用いた仮説検定を実践する~平均値の差の検定~

◆この記事のテーマ

ベイズファクターを使って平均値の差を検定する方法を実践します。 この手法は、例えば下図のようなシーンに適用できます。下図左側では、交通事故対策Aの実施前と実施後の単位時間あたり事故発生件数のデータを蓄積し、対策前後で事故発生件数の平均値に差があるかを検定しています。また、下図右側では交通事故対策Aが実施された交差点と実施されていない交差点の単位時間あたり事故発生件数の平均値に差があるかを検定しています。

※上記の事例は整数データであるからポアソン分布や負の二項分布等を使用するのが理想ですが、ここでは事故発生件数の平均値が十分大きく正規近似が可能であると仮定します。

※左側は対応のある(one-sample)グループ間の検定、右側は対応のない(two-sampe)グループ間の検定です。設定上、後者の設計では対策実施効果以外の要因(異なる交差点を比較に用いることによるランダム効果)が結果に絡む可能性が高くなるので、対策Aの効果を確かめることが分析の目的であれば前者の対応のある検定の設計がより望ましいといえます。

方針

理論編で整理した内容の再現を基本とします。ゆえに断りなく理論編で用いた変数を使用します。

事前分布はJZSの事前分布のみ試します。というものこの理論の先駆者であるRichard D. Moreyさんが開発しているBayesFactorパッケージではJZSの事前分布を用いており、氏もこちらを推しているようなので。

本記事で検証する仮説は以下3組です。$\delta$はスケーリングされた平均値の差と解釈できる値で、ベイズ版平均値の差の検定で着目する値になります。


両側検定に対応する仮説 $$ \begin{cases} H_0:\delta=0 \\\\ H_1:\delta \neq 0 \end{cases} $$


片側検定に対応する仮説 $$ \begin{cases} H_0:\delta=0\\\\ H_{1PO}:\delta \leq 0 \\\\ H_{1NO}:\delta \geq 0 \end{cases} $$ ※$PO$はpositive orderedの略、$NO$はnegatie orderedの略


2つの範囲仮説を検証する場合 $A = \{ \delta:\delta_l < \delta < \delta_u \}$としたとき、 $$ \begin{cases} H_{0Area}: \delta \in A \\\\ H_{1Area}: \delta \notin A \end{cases} $$

対応のある検定を実践

サンプルデータの可視化

データはこちらよりダウンロードできます。d$preは対策A実施前の交差点における事故発生件数、d$proは対策A実施後の交差点における事故発生件数とします。

load("data.RData")

head(d)

##    pre pro
## 1  32  38
## 2  25  26
## 3  29  33
## 4  30  25
## 5  31  30
## 6  32  33

これらの差をとり、可視化してみます。 視覚的にはなんと対策前の事故件数の方が多そうです。しかし可視化で分かるのは「多そう」に留まってしまいます。対策前の事故件数の方が本当に多いと言えるのか、さらに深掘ってみましょうか。

可視化コードはこちら。

p <- d %>% as.data.frame() %>% mutate(D = pre - pro ) %>% select(D) %>% ggplot(aes(x=D)) + geom_histogram(breaks=seq(min(d$pre-d$pro)+0.5,max(d$pre-d$pro)+0.5,1), fill="#4169e1") +
  theme_light(base_size=12) + xlab("pre - pro") + ylab(NULL)

両側検定

memo

筆者は本分析よりStanの実行環境を「Windows + Rstudio Desktop + rstan」から「Ubuntu(Linux) + Rstudio Server + cmdstanr」に移行しました。 rコード・Stanコードとも現在のrstanではエラーが起きるので留意を。

Stanコードを示します。

//model1.stan
//One-sample t-test

data{
  int N; //Common sample size
  array[2] vector[N] D_raw; //Raw data
  real<lower=0> r, Jeffreys_alpha, Jeffreys_beta;
  //r:scale factor
  //Jeffreys_alpha : mean of prior(sigma^2) (sufficiently small values)
  //Jeffreys_beta : variance of prior(sigma^2) (sufficiently small values)
}

transformed data{
  vector[N] D; //X - Y

  D = D_raw[1] - D_raw[2];
}

parameters{
  real delta; //effect size
  real<lower=0> sigma, g;
}

model{
  //Likelihood
  target += normal_lpdf(D | delta * sigma, sigma);

  //JZS prior
  target += normal_lpdf(delta | 0, sqrt(g));
  target += inv_gamma_lpdf(g | 0.5  , (r^2)*0.5);
  target += gamma_lpdf(sigma^2 | Jeffreys_alpha, Jeffreys_beta);
}

generated quantities{
  real mean_delta, variance_delta;
  //mean_delta:mean of CMDE(delta)
  //variance_delta:variance of CMDE(delta)
  real BF_01;
  // real BF_10;
  real logPostDensDelta, logPriorDensDelta;
  //logPostDensDelta:p(delta=0 | prior)
  //logPostDensDelta:p(delta=0 | posterior)


  mean_delta = sum(D) / (sigma * (N + (1 / g)));
  variance_delta = 1 / (N + (1 / g));

  logPostDensDelta = -0.5 * log(pi() * variance_delta * 2) - 0.5 * mean_delta^2 / variance_delta;
  logPriorDensDelta = -log(pi() * r);

  BF_01 = exp(logPostDensDelta - logPriorDensDelta);
  //BF_10 = exp(logPriorDensDelta - logPostDensDelta);
}

筆者の見解では、$BF_{01}$及び$BF_{10}$のうち、1より小さい値をとる方をgenerated quatities{}で推定するのが精度の都合上よさそうです。というのも1より大きい値をとると考えられる$BF$は、正の方向に分布の裾が重くなってしまい、平均値が大きい値に引っ張られてしまうから。また$\mathbb{E}[BF_{01}] = \cfrac{1}{\mathbb{E}[BF_{10}]}$は成り立たないので、$BF_{01}$と$BF_{10}$のMCMCサンプルを同時に得ることもおすすめしません。データに応じてreal BF_01real BF_10を使い分けましょう。

これを走らせるコードが以下です。

library(cmdstanr)
library(rstan)
library(tidyverse)

stan_data <- list(D_raw=t(d), N = nrow(d), r =sqrt(2)/2, Jeffreys_alpha=1e-10, Jeffreys_beta=1e-10)

model1_c <- cmdstan_model("/model/model1.stan")

fit_c1 <- model1_c$sample(
  data = stan_data,
  parallel_chains = 4,
  chains = 4,
  iter_warmup = 1000,
  iter_sampling = 20000
)

収束を確認し、ベイズファクターを計算しましょう。

stanfit1 <- rstan::read_stan_csv(fit_c1$output_files())
print(stanfit1)

## Inference for Stan model: model1-202106190132-1-6686ed.
## 4 chains, each with iter=21000; warmup=1000; thin=1;
## post-warmup draws per chain=20000, total post-warmup draws=80000.
##
## mean se_mean     sd    2.5%     25%     50%     75%   97.5% n_eff Rhat
## delta                0.27    0.00   0.10    0.07    0.20    0.27    0.34    0.47 62133    1
## sigma                8.15    0.00   0.58    7.11    7.75    8.12    8.53    9.39 58529    1
## g                    3.49    0.81 217.23    0.08    0.21    0.42    1.00   11.79 71142    1
## mean_delta           0.27    0.00   0.02    0.23    0.26    0.27    0.29    0.31 62414    1
## variance_delta       0.01    0.00   0.00    0.01    0.01    0.01    0.01    0.01 75686    1
## BF_01                0.23    0.00   0.13    0.06    0.13    0.20    0.29    0.54 54358    1
## logPostDensDelta    -2.44    0.00   0.56   -3.62   -2.80   -2.41   -2.05   -1.42 61613    1
## logPriorDensDelta   -0.80    0.00   0.00   -0.80   -0.80   -0.80   -0.80   -0.80     2    1
## lp__              -379.97    0.01   1.28 -383.31 -380.56 -379.64 -379.03 -378.50 32010    1
##
## Samples were drawn using NUTS(diag_e) at Sat Jun 19 01:32:05 2021.
## For each parameter, n_eff is a crude measure of effective sample size,
## and Rhat is the potential scale reduction factor on split chains (at
##                                                                   convergence, Rhat=1).

ms1 <- as.matrix(stanfit1)

res1 <- ms1 %>% as.data.frame() %>% select(starts_with("BF")) %>%
  summarise_all(list(mean = mean)) %>% summarise_all(list(round),digits=8) %>% pivot_longer(everything())

cat(sprintf("Bayesfactor is\n\tBF_01:%.8f\tBF_10:%.8f",res1$value, 1 / res1$value))

## Bayesfactor is
##   BF_01:0.22509123	BF_10:4.44264310

Rhatで収束を確認。結果は

$$ \begin{cases} BF_{01} \fallingdotseq 0.225 \\
BF_{10} \fallingdotseq 4.443 \end{cases} $$

となりました。Kass and Raftery(1995)の評価基準では、$H_0$に対する反証の強さは十分であると判断できます。つまり、$\delta = 0$を棄却し$\delta \neq 0$を採択し、対策後の事故件数は対策前から変化したと判断してよさそうです。

$BF_{01}$の分布も確認しておきましょう。

plot_title <- ggtitle("Posterior distribution of BF_01",
                      "with mean and 80% interval")
p <- mcmc_areas(ms1,
           pars = c("BF_01"),
           prob = 0.8, point_est = c("mean")) +
  geom_text(data=res1[2], aes(x=value, y="BF_01", label=value), nudge_y =-0.03) + plot_title

一応、以上の結果がまともなのかどうか、BayesFactorパッケージで確認します。

library(BayesFactor)
bf <- ttestBF(x=d$pre, y=d$pro, paired=T)
bf

## Bayes factor analysis
## --------------
##   [1] Alt., r=0.707 : 4.297253 ±0%
##
## Against denominator:
##   Null, mu = 0
## ---
##   Bayes factor type: BFoneSample, JZS


1 / bf

## Bayes factor analysis
## --------------
##   [1] Null, mu=0 : 0.2327068 ±0%
##
## Against denominator:
##   Alternative, r = 0.707106781186548, mu =/= 0
## ---
##   Bayes factor type: BFoneSample, JZS

BayesFactor::ttestBF()による結果は、

$$ \begin{cases} BF_{01} \fallingdotseq 0.233 \\
BF_{10} \fallingdotseq 4.297 \end{cases} $$

でした。概ね俺の実装による結果と整合してそうです。

片側検定

間髪を入れず片側検定に移ります。モデル構成は両側検定のときから変わらないので、先ほど得たMCMCサンプルをこねくり回します。 $H_{1PO}$、$H_{1NO}$に対応するモデルをそれぞれ$M_{1PO}$、$M_{1NO}$とおくと、

$$ \begin{cases} p(\delta = 0 | M_{1NO}) = p(\delta = 0 | M_{1PO}) = \cfrac{2}{\pi r} \\
p(\delta=0 | \mathcal{D}, M_{1PO}) = \cfrac{p(\delta=0 | \mathcal{D}, M_1)}{\Phi(0 | \mu_{\delta}, \sigma_{\delta})} \eqsim \cfrac{1}{T} \sum_{t=1}^{T} \cfrac{p(\delta=0 | \boldsymbol{\psi}^{(t)},\mathcal{D},M_1)}{\Phi(0 | \mu_{\delta}^{(t)}, \sigma_{\delta}^{(t)})} \\
p(\delta=0 | \mathcal{D}, M_{1NO}) = \cfrac{p(\delta=0 | \mathcal{D}, M_1)}{1 - \Phi(0 | \mu_{\delta}, \sigma_{\delta})} \eqsim \cfrac{1}{T} \sum_{t=1}^{T} \cfrac{p(\delta=0 | \boldsymbol{\psi}^{(t)},\mathcal{D},M_1)}{1 - \Phi(0 | \mu_{\delta}^{(t)}, \sigma_{\delta}^{(t)})} \\
\end{cases}
$$

だから、Savage-Dickey法より

$$ BF_{01PO} = \cfrac{p(\delta=0 | \mathcal{D}, M_{1PO})}{p(\delta=0 | M_{1PO})} \eqsim \cfrac{\pi r}{2} \cfrac{1}{T} \sum_{t=1}^{T} \cfrac{p(\delta=0 | \boldsymbol{\psi}^{(t)},\mathcal{D},M_1)}{\Phi(0 | \mu_{\delta}^{(t)}, \sigma_{\delta}^{(t)})} = \cfrac{1}{T} \sum_{t=1}^{T}\cfrac{BF_{01}^{(t)}}{2\Phi(0 | \mu_{\delta}^{(t)}, \sigma_{\delta}^{(t)}) } $$

$$ BF_{01NO} = \cfrac{p(\delta=0 | \mathcal{D}, M_{1NO})}{p(\delta=0 | M_{1NO})} \eqsim \cfrac{\pi r}{2} \cfrac{1}{T} \sum_{t=1}^{T} \cfrac{p(\delta=0 | \boldsymbol{\psi}^{(t)},\mathcal{D},M_1)}{1 - \Phi(0 | \mu_{\delta}^{(t)}, \sigma_{\delta}^{(t)})} = \cfrac{1}{T} \sum_{t=1}^{T}\cfrac{BF_{01}^{(t)}}{2\left(1 - \Phi(0 | \mu_{\delta}^{(t)}, \sigma_{\delta}^{(t)})\right) } $$

です(*´Д`)ハァハァ。

res_ordered <- ms1 %>% data.frame() %>% mutate(BF_01_NO = BF_01 / (2 * (1-pnorm(0, mean=mean_delta, sd=sqrt(variance_delta)))),
                                               BF_01_PO = BF_01 / (2 * pnorm(0, mean=mean_delta, sd=sqrt(variance_delta))),
                                               BF_10_NO = (2 * (1-pnorm(0, mean=mean_delta, sd=sqrt(variance_delta))) / BF_01),
                                               BF_10_PO = (2 * pnorm(0, mean=mean_delta, sd=sqrt(variance_delta)))/ BF_01) %>%
  summarise(BF_01_NO = mean(BF_01_NO),
            BF_01_PO = mean(BF_01_PO),
            BF_10_NO = mean(BF_10_NO),
            BF_10_PO = mean(BF_10_PO))

res_ordered

##    BF_01_NO BF_01_PO BF_10_NO   BF_10_PO
## 1 0.1130584 34.61659 12.16943 0.02898778

# 推定精度の良い(BF<1となる)方を用いる
cat(sprintf("Bayesfactor is\n\tBF_01_NO:%.8f\tBF_10_NO:%.8f\n\tBF_01_PO:%.8f\tBF_10_PO:%.8f",
            res_ordered$BF_01_NO, 1 / res_ordered$BF_01_NO,
            1 /res_ordered$BF_10_PO, res_ordered$BF_10_PO))

## Bayesfactor is
## BF_01_NO:0.11305844	BF_10_NO:8.84498327
## BF_01_PO:34.49729577	BF_10_PO:0.02898778

結果は

$$ \begin{cases} BF_{01PO} \fallingdotseq 34.497 \\
BF_{10PO} \fallingdotseq 0.029 \\
BF_{01NO} \fallingdotseq 0.113 \\
BF_{10NO} \fallingdotseq 8.845 \\
\end{cases} $$

でした。Kass and Raftery(1995)の評価基準では、$H_{1PO}$に反する証拠は強く、$H_{0NO}$に反する証拠は十分にあると判断できます。つまり、$\delta \leq 0$と$\delta=0$の比較では$\delta=0$が採択されることから、事故件数平均値は対策後増加していないと判断され、さらに$\delta \geq 0$と$\delta=0$の比較では$\delta \geq 0$が採択されることから、やはり対策後の事故件数の平均値は減少したのだと判断されます。

BayesFactorパッケージとの整合を確認します。

# bayesfactorパッケージでordered restrictionのベイズファクターを計算
bf <- ttestBF(x=d$pre, y=d$pro, nullInterval = c(0,Inf) ,paired=T)
bf

## Bayes factor analysis
## --------------
## [1] Alt., r=0.707 0<d<Inf    : 8.564529   ±0%
## [2] Alt., r=0.707 !(0<d<Inf) : 0.02997665 ±0.01%
##
## Against denominator:
##   Null, mu = 0
## ---
##   Bayes factor type: BFoneSample, JZS

1/bf

## denominator
## numerator    Alt., r=0.707 0<d<Inf Alt., r=0.707 !(0<d<Inf)
## Null, mu=0             0.1167606                 33.35929

BayesFactor::ttestBF()による結果は、

$$ \begin{cases} BF_{01PO} \fallingdotseq 33.359 \\
BF_{10PO} \fallingdotseq 0.030 \\
BF_{01NO} \fallingdotseq 0.117 \\
BF_{10NO} \fallingdotseq 8.565 \\
\end{cases} $$

でした。やはり俺の実装による結果と整合しているな(;´Д`)スッ、スバラスィ …ハァハァ。

2つの範囲仮説を検証

範囲仮説については理論編でもこれまでも説明していませんでした。Richard D. Morey氏らの論文で説明されているもので、帰無仮説が$\delta$のパラメータ空間上の1点ではなく、一定の範囲をもつ場合になります。微小な平均値の差は効果が無いに等しいものとして扱いたいときに有効な方法です。

範囲仮説同士のベイズファクターは、両仮説に対応するモデル$M_0$、$M_1$を包含したモデル(encompassing model)$M_E$を設定すれば、 $$ BF_{01} = \cfrac{p( \mathcal{D} | \delta \in A, M_E )}{p(\mathcal{D} | \delta \notin A, M_E )} = \left. \cfrac{p( \delta \in A | \mathcal{D},M_E)}{p(\delta \notin A| \mathcal{D}, M_E)} \middle/ \cfrac{p( \delta \in A | M_E)}{p(\delta \notin A | M_E)} \right. $$

と計算できます。

このうち、事前分布の範囲確率$p( \delta \notin A| \mathcal{D}, M_E)$、$p(\delta \in A| \mathcal{D}, M_E)$はまがうことなきClosed formで計算できるとして、引っかかるのが尤度$p(\delta \in A | \mathcal{D}, M_E)$、$p(\delta \notin A | \mathcal{D}, M_E)$です。要は$\delta$の累積分布関数を$\Phi(\delta)$とおいて、

$$ \begin{cases} p(\delta \in A | \mathcal{D}, M_E) \eqsim \cfrac{1}{T} \sum_{t=1}^{T} \left( \Phi(\delta_u | \mu_{\delta}^{(t)}, \sigma_{\delta}^{(t)}) - \Phi(\delta_l | \mu_{\delta}^{(t)}, \sigma_{\delta}^{(t)}) \right) \\
p(\delta \notin A | \mathcal{D}, M_E) \eqsim 1 - p(\delta \in A | \mathcal{D}, M_E) \end{cases} $$

を計算すればいいのですが、$\delta$の密度関数の平均$\mu_{\delta}^{(t)}$、分散$\sigma_{\delta}^{(t)}$を精度よく推定してやる工夫が必要と考えられます。 しかしこれも理論編をしっかり抑えていれば、理論編$(18)$式、$(20)$式を用いてやればよいと分かるはずです(*´Д`)ハァハァ。

これもモデル構成は両側検定のときから変わらないので、両側検定のMCMCサンプルをこねくり回します。2つの仮説の境界は$\delta_l = -2 / \sigma$、 $\delta_u < -2 / \sigma$、つまり事故発生件数$\pm2$程度の微小な増減は無視したいという志向で仮説を設定します。


# Area null

upper_effect <- 2
lower_effect <- -2
r <- stan_data$r

res_Area <- ms1 %>% data.frame() %>%
  mutate(upper_delta = upper_effect / sigma, lower_delta = lower_effect / sigma,postAreadelta = pnorm(upper_delta, mean=mean_delta, sd=sqrt(variance_delta)) -pnorm(lower_delta, mean=mean_delta, sd=sqrt(variance_delta)),
         priorAreadelta = pcauchy(upper_delta, 0, r) - pcauchy(lower_delta, 0, r)) %>%
  mutate(BF_10_Area = (1-postAreadelta) / postAreadelta / ((1-priorAreadelta) / priorAreadelta)) %>%
  summarise(BF_10_Area = mean(BF_10_Area)) %>% mutate(BF_01_Area = 1 / BF_10_Area)

  ## res_Area <- ms1 %>% data.frame() %>%
  ##   mutate(upper_delta = upper_effect / sigma, lower_delta = lower_effect / sigma,postAreadelta = pnorm(upper_delta, mean=mean_delta, sd=sqrt(variance_delta)) -pnorm(lower_delta, mean=mean_delta, sd=sqrt(variance_delta)),
  ##          priorAreadelta = pcauchy(upper_delta, 0, r) - pcauchy(lower_delta, 0, r)) %>%
  ##   mutate(BF_01_Area = postAreadelta/(1-postAreadelta) / (priorAreadelta/(1-priorAreadelta))) %>%
  ##   summarise(BF_01_Area = mean(BF_01_Area)) %>% mutate(BF_10_Area = 1 / BF_01_Area)

res_Area

## BF_10_Area BF_01_Area
## 1  0.4152557   2.408155

結果は、

$$ \begin{cases} BF_{01Area} \fallingdotseq 2.408 \\
BF_{10Area} \fallingdotseq 0.415 \\
\end{cases} $$

となりました。$H_{0Area}$の方が支持されるものの、Kass and Raftery(1995)の評価基準では、$H_{1Area}$に反する証拠は十分でないと判断されます。つまり、$\pm2$以上の事故件数の増減はなかったといえそうだが、その証拠は十分ではない、ということです。

BayesFactorパッケージとの整合を確認します。

X <- scale(d$pre - d$pro)

bf <- ttestBF(x=d$pre, y=d$pro, nullInterval = c(-2,2)/attr(X, "scaled:scale") ,paired=T)

bf[1]/bf[2]

## Bayes factor analysis
## --------------
##   [1] Alt., r=0.707 -0.245057417614946<d<0.245057417614946 : 2.472662 ±0%
##
## Against denominator:
##   Alternative, r = 0.707106781186548, mu =/= 0 !(-0.245057417614946<d<0.245057417614946)
## ---
##   Bayes factor type: BFoneSample, JZS

bf[2]/bf[1]
## Bayes factor analysis
## --------------
##   [1] Alt., r=0.707 !(-0.245057417614946<d<0.245057417614946) : 0.4044224 ±0%
##
## Against denominator:
##   Alternative, r = 0.707106781186548, mu =/= 0 -0.245057417614946<d<0.245057417614946
## ---
##   Bayes factor type: BFoneSample, JZS


BayesFactor::ttestBF()による結果は、

$$ \begin{cases} BF_{01Area} \fallingdotseq 2.473 \\
BF_{10Area} \fallingdotseq 0.404 \\
\end{cases} $$

やはり俺の実装はうまくいってそうだ(;´Д`)スッ、スバラスィ …ハァハァ。

対応のない検定を実践

対応のない検定は、尤度が異なる点を除き対応のある検定と同じです。

サンプルデータの可視化

データは対応のある検定のときと同じものを用います。命名と実際の意味が少し異なっていて分かりづらいですが、d$preは対策A未実施の交差点における事故発生件数、d$proは対策A実施済みの交差点における事故発生件数とします。

視覚化はこんな感じです。

可視化コードはこちら。

p1 <- ggplot(data.frame(X = d$pre), aes(x=X)) + theme_light(base_size=12) + geom_histogram(breaks=seq(min(c(d$pre,d$pro)), max(c(d$pre,d$pro)), 1),
                                                                                           fill="#ffb6c1") + xlab("pre") + ylab(NULL)
p2 <- ggplot(data.frame(X = d$pro), aes(x=X)) + theme_light(base_size=12) + geom_histogram(breaks=seq(min(c(d$pre,d$pro)), max(c(d$pre,d$pro)), 1),
                                                                                           fill="#90ee90") + xlab("pro") + ylab(NULL)
library(gridExtra)
p <- grid.arrange(p1,p2, nrow=2)

両側検定

Stanコードは以下のとおり。

//model2.stan
//Two-sample t-test

data{
  int N; //sample size og group1
  int M; //sample size of group2
  vector[N] D1; //data of group1
  vector[M] D2; //data of group2
  real<lower=0> r, Jeffreys_alpha, Jeffreys_beta;
  //r:scale factor
  //Jeffreys_alpha:mean of prior(sigma^2) (sufficiently small values)
  //Jeffreys_beta:variance of prior(sigma^2) (sufficiently small values)
}

parameters{
  real delta, mu; //delta:effect size mu:overall mean
  real<lower=0> sigma, g;
}


model{

  //Likelihood
  target += normal_lpdf(D1 | mu - delta * sigma / 2, sigma);
  target += normal_lpdf(D2 | mu + delta * sigma / 2, sigma);

  //JZS prior
  target += normal_lpdf(delta | 0, sqrt(g));
  target += inv_gamma_lpdf(g | 0.5  , (r^2)*0.5);
  target += gamma_lpdf(sigma^2 | Jeffreys_alpha, Jeffreys_beta);

}

generated quantities{
  real mean_delta, variance_delta;
  //mean_delta:mean of CMDE(delta)
  //variance_delta:variance of CMDE(delta)
  real BF_01;
  // real BF_10;
  real logPostDensDelta, logPriorDensDelta;
  //logPostDensDelta:p(delta=0 | prior)
  //logPostDensDelta:p(delta=0 | posterior)


  mean_delta = 2 * g * (sum(D2) - sum(D1) + (N - M) * mu ) / ((4 + ( N + M ) * g ) * sigma);
  variance_delta = 4 * g / (4 + (N + M) * g);

  logPostDensDelta = -0.5 * log(pi() * variance_delta * 2) - 0.5 * mean_delta^2 / variance_delta;
  logPriorDensDelta = -log(pi() * r);

  BF_01 = exp(logPostDensDelta - logPriorDensDelta);
  //BF_10 = exp(logPriorDensDelta - logPostDensDelta);
}

これを走らせるRコードはこちら。


stan_data <- list(D1=d$pro, D2=d$pre, N = length(d$pro), M = length(d$pre), r =sqrt(2)/2, Jeffreys_alpha=1e-10, Jeffreys_beta=1e-10)

model2_c <- cmdstan_model("/model/model2.stan")

fit_c_area <- model3_c$sample(
  data = stan_data,
  parallel_chains = 4,
  chains = 4,
  iter_warmup = 500,
  iter_sampling = 2000
)

以降のコードは対応のある場合と基本的に同じなので、結果だけ載せていきますね |ω·`)

## Inference for Stan model: model3-202106202142-1-61dec5.
## 4 chains, each with iter=2500; warmup=500; thin=1;
## post-warmup draws per chain=2000, total post-warmup draws=8000.
##
##                      mean se_mean    sd    2.5%     25%     50%     75%   97.5% n_eff Rhat
## delta                0.39    0.00  0.14    0.12    0.29    0.39    0.48    0.66  8173    1
## mu                  30.28    0.00  0.39   29.53   30.02   30.28   30.55   31.07  7252    1
## sigma                5.60    0.00  0.28    5.09    5.40    5.58    5.78    6.17  7184    1
## g                    3.59    0.93 67.27    0.09    0.23    0.47    1.13   13.19  5182    1
## mean_delta           0.39    0.00  0.03    0.32    0.37    0.39    0.41    0.44  7823    1
## variance_delta       0.02    0.00  0.00    0.02    0.02    0.02    0.02    0.02  8608    1
## BF_01                0.14    0.00  0.07    0.05    0.09    0.12    0.17    0.31  6952    1
## logPostDensDelta    -2.88    0.01  0.46   -3.79   -3.19   -2.89   -2.58   -1.97  7593    1
## lp__              -655.90    0.02  1.44 -659.60 -656.61 -655.56 -654.83 -654.11  3423    1
## logPriorDensDelta   -0.80    0.00  0.00   -0.80   -0.80   -0.80   -0.80   -0.80     2    1
##
## Samples were drawn using NUTS(diag_e) at Sun Jun 20 21:42:05 2021.
## For each parameter, n_eff is a crude measure of effective sample size,
## and Rhat is the potential scale reduction factor on split chains (at
## convergence, Rhat=1).

## Bayesfactor is
##  	BF_01:0.13800334	BF_10:7.24620143

結果は

$$ \begin{cases} BF_{01} \fallingdotseq 0.138 \\
BF_{10} \fallingdotseq 7.246 \\
\end{cases} $$

です。よって$H_1$を採択し、2グループ間の事故件数には差があると判断されます。

BayesFactorパッケージの場合は、

bf <- ttestBF(x=d$pre, y=d$pro, paired=F)
bf
## Bayes factor analysis
## --------------
## [1] Alt., r=0.707 : 7.149843 ±0%
##
## Against denominator:
##   Null, mu1-mu2 = 0
## ---
## Bayes factor type: BFindepSample, JZS

1 / bf
## Bayes factor analysis
## --------------
## [1] Null, mu1-mu2=0 : 0.1398632 ±0%
##
## Against denominator:
##   Alternative, r = 0.707106781186548, mu =/= 0
## ---
## Bayes factor type: BFindepSample, JZS

$$ \begin{cases} BF_{01} \fallingdotseq 0.140 \\
BF_{10} \fallingdotseq 7.150 \\
\end{cases} $$

ほぼ一致Σ(゚Д゚)スゲェ!

片側検定

## Bayesfactor is
## BF_01_NO:0.06924356	BF_10_NO:14.44177639
## BF_01_PO:25.05332284	BF_10_PO:0.03991487

俺の実装による結果は

$$ \begin{cases} BF_{01PO} \fallingdotseq 25.053 \\
BF_{10PO} \fallingdotseq 0.040 \\
BF_{01NO} \fallingdotseq 0.069 \\
BF_{10NO} \fallingdotseq 14.442 \\
\end{cases} $$

今回の実装で$\delta$は「未対策の交差点における事故件数の平均値」-「対策済みの交差点における事故件数の平均値」の値です。まず$H_{0PO}$が強く採択されることから、「対策済み」グループのほうが「未対策」グループよりも事故件数の平均値が多い、ということはないと判断できます。さらに、$H_{1NO}$が採択されることから、「未対策」グループのほうが「対策済み」グループよりも事故件数の平均値が多いと言えると判断できます。

bf <- ttestBF(x=d$pre, y=d$pro, nullInterval = c(0,Inf) ,paired=F)
bf
## Bayes factor analysis
## --------------
## [1] Alt., r=0.707 0<d<Inf    : 14.25912   ±0%
## [2] Alt., r=0.707 !(0<d<Inf) : 0.04056425 ±0.17%
##
## Against denominator:
##   Null, mu1-mu2 = 0
## ---
## Bayes factor type: BFindepSample, JZS

1/bf
##                  denominator
## numerator         Alt., r=0.707 0<d<Inf Alt., r=0.707 !(0<d<Inf)
##   Null, mu1-mu2=0            0.07013055                 24.65225

パッケージによる結果は

$$ \begin{cases} BF_{01PO} \fallingdotseq 24.652 \\
BF_{10PO} \fallingdotseq 0.041 \\
BF_{01NO} \fallingdotseq 0.070 \\
BF_{10NO} \fallingdotseq 14.259 \\
\end{cases} $$

ほぼ一致…(;´Д`)スッ、スバラスィ …ハァハァ

2つの範囲仮説を検証

対応のある場合と同様、2つの仮説の境界は$\delta_l = -2 / \sigma$、 $\delta_u < -2 / \sigma$とします。

BF_10_Area BF_01_Area
1  0.5996078   1.667757

俺の実装による結果は、

$$ \begin{cases} BF_{01Area} \fallingdotseq 1.668 \\
BF_{10Area} \fallingdotseq 0.600 \\
\end{cases} $$

よって、$H_{0Area}$の方がデータの説明力は高いものの、決定的でないことが分かります。つまりグループ間に$\pm2$以上の事故件数の平均値の差はないといえそうだが、その証拠は十分ではない、ということです。

X <- scale(c(d$pre,d$pro))
attr(X, "scaled:scale")

bf <- ttestBF(x=d$pre, y=d$pro, nullInterval = c(-2,2)/attr(X, "scaled:scale") ,paired=F)

bf[1]/bf[2]

## Bayes factor analysis
## --------------
##   [1] Alt., r=0.707 -0.351290094235453<d<0.351290094235453 : 1.638839 ±0%
##
## Against denominator:
##   Alternative, r = 0.707106781186548, mu =/= 0 !(-0.351290094235453<d<0.351290094235453)
## ---
##   Bayes factor type: BFindepSample, JZS

bf[2]/bf[1]

## Bayes factor analysis
## --------------
##   [1] Alt., r=0.707 !(-0.351290094235453<d<0.351290094235453) : 0.6101882 ±0%
##
## Against denominator:
##   Alternative, r = 0.707106781186548, mu =/= 0 -0.351290094235453<d<0.351290094235453
## ---

パッケージによる結果は、

$$ \begin{cases} BF_{01Area} \fallingdotseq 1.639 \\
BF_{10Area} \fallingdotseq 0.610 \\
\end{cases} $$

最後の検定結果もほぼ同じ、無事有終の美を飾ることができました。

コメントを書く


※ コメントは承認されると表示されます

承認されたコメント一覧