# データの読み込み
bs <- read_csv("baseball.csv")
## Parsed with column specification:
## cols(
## .default = col_integer(),
## Name = col_character(),
## team = col_character(),
## salary = col_double(),
## position = col_character(),
## bloodType = col_character(),
## throw.by = col_character(),
## batting.by = col_character(),
## birth.place = col_character(),
## birth.day = col_date(format = ""),
## 背番号 = col_character(),
## 打率 = col_double(),
## 出塁率 = col_double(),
## 長打率 = col_double(),
## OPS = col_double(),
## RC27 = col_double(),
## XR27 = col_double(),
## 防御率 = col_double(),
## 勝率 = col_double(),
## 投球回 = col_double(),
## WHIP = col_double()
## # ... with 2 more columns
## )
## See spec(...) for full column specifications.
# 身長と体重のデータだけ読み込む
df <- bs %>% dplyr::select("weight", "height") %>% na.omit
# 最尤推定
summary(lm(height ~ weight, data = df))
##
## Call:
## lm(formula = height ~ weight, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -13.9016 -2.8402 -0.0261 2.6653 16.4306
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 148.57283 1.33809 111.03 <2e-16 ***
## weight 0.38267 0.01586 24.13 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 4.37 on 916 degrees of freedom
## Multiple R-squared: 0.3886, Adjusted R-squared: 0.388
## F-statistic: 582.3 on 1 and 916 DF, p-value: < 2.2e-16
# データはリスト型で渡す
dataset <- list(L = NROW(df), X = df$weight, Y = df$height)
model.reg <- stan_model("regression.stan")
## data{
## int<lower=0> L;
## real X[L];
## real Y[L];
## }
##
## parameters{
## real beta_0;
## real beta_1;
## real<lower=0> sigma;
## }
##
## model{
## for(l in 1:L){
## Y[l] ~ normal(beta_0 + beta_1 * X[l], sigma);
## }
## }
##
## generated quantities{
## real log_lik[L];
## real pred[L];
## for(l in 1:L){
## log_lik[l] = normal_lpdf(Y[l]|beta_0 + beta_1 * X[l], sigma);
## pred[l] = normal_rng(beta_0 + beta_1 * X[l], sigma);
## }
## }
fit.reg <- sampling(model.reg, dataset)
# 係数の出力
print(fit.reg, pars = c("beta_0", "beta_1", "sigma"))
## Inference for Stan model: regression.
## 4 chains, each with iter=2000; warmup=1000; thin=1;
## post-warmup draws per chain=1000, total post-warmup draws=4000.
##
## mean se_mean sd 2.5% 25% 50% 75% 97.5% n_eff Rhat
## beta_0 148.50 0.04 1.29 145.88 147.65 148.50 149.35 151.02 1345 1
## beta_1 0.38 0.00 0.02 0.35 0.37 0.38 0.39 0.41 1350 1
## sigma 4.37 0.00 0.10 4.17 4.30 4.37 4.44 4.58 1507 1
##
## Samples were drawn using NUTS(diag_e) at Mon Sep 3 19:33:19 2018.
## 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).
# プロット
plot(fit.reg,pars=c("sigma"),show_density=T)
## ci_level: 0.8 (80% intervals)
## outer_level: 0.95 (95% intervals)
bayesplot::mcmc_dens(as.array(fit.reg), pars = c("beta_0", "beta_1", "sigma"))
g1 <- rstan::extract(fit.reg, pars = "pred") %>% data.frame %>%
tidyr::gather(key,val) %>%
ggplot(aes(x=val))+geom_histogram(binwidth=1)+xlim(140,220)
g2 <- ggplot(df,aes(x=height))+geom_histogram(binwidth = 1)+xlim(140,220)
# gridExtraパッケージは複数のggplotオブジェクトを並べて表示してくれます
gridExtra::grid.arrange(g1, g2, ncol = 1)
# 予測値を抜き出します
rstan::extract(fit.reg, pars = "pred") %>% data.frame %>%
# データを縦長にし,変数ごとにグループ化
tidyr::gather(key,val,factor_key=TRUE) %>% group_by(key) %>%
# 平均を推定値とします
summarize(EAP = mean(val)) -> pred
# 実測値と予測値の相関係数の二乗
(cor(df$height, pred$EAP))^2
## [1] 0.389353
# 事後対数尤度(これだけでは比較できませんが)
rstan::extract(fit.reg, pars = "log_lik")$log_lik %>% loo()
## Warning: Relative effective sample sizes ('r_eff' argument) not specified.
## For models fit with MCMC, the reported PSIS effective sample sizes and
## MCSE estimates will be over-optimistic.
##
## Computed from 4000 by 918 log-likelihood matrix
##
## Estimate SE
## elpd_loo -2658.8 24.6
## p_loo 3.6 0.4
## looic 5317.5 49.1
## ------
## Monte Carlo SE of elpd_loo is 0.0.
##
## All Pareto k estimates are good (k < 0.5).
## See help('pareto-k-diagnostic') for details.
セ・パ両リーグの違いをデータから予測してみます。従属変数が0/1のデータになるので,ロジスティック回帰分析を使います。
# 投手ならば1,野手ならば0という変数を作成
bs %>% mutate(pos2 = ifelse(position=="投手",1,0)) %>%
# 新変数と身長のデータだけ取り出す
dplyr::select(pos2,height) %>% na.omit ->df2
# 身長とポジション(二値)の関係
ggplot(df2,aes(x=height,y=pos2))+geom_point()
# 最尤推定
glm(pos2 ~ height, data=df2, family=binomial(link="logit")) %>% summary
##
## Call:
## glm(formula = pos2 ~ height, family = binomial(link = "logit"),
## data = df2)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -2.0454 -1.1364 0.6643 1.1006 1.7613
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -16.83525 2.39691 -7.024 2.16e-12 ***
## height 0.09351 0.01327 7.045 1.86e-12 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 1272.0 on 917 degrees of freedom
## Residual deviance: 1216.6 on 916 degrees of freedom
## AIC: 1220.6
##
## Number of Fisher Scoring iterations: 4
これをベイジアンモデリングで実行するには次のようにします。
model.logistic <- stan_model("logistic.stan")
## data{
## int<lower=0> N; // sample size
## real X[N]; // predictor
## int<lower=0,upper=1> Y[N]; // predicted value(should be 0,1)
## }
##
## parameters{
## real beta0; // baseline
## real beta1; // coefficient
## }
##
## model{
## real prob[N]; // transformed parameter
## //likelihood
## for(n in 1:N){
## prob[n] = inv_logit(beta0 + beta1 * X[n]);
## Y[n] ~ bernoulli(prob[n]);
## }
## //prior
## beta0 ~ normal(0,100);
## beta1 ~ normal(0,100);
## }
実行します。
# データはリスト型で渡す
dataset <- list(N = NROW(df2), X = df2$height, Y = df2$pos2)
# MCMC!
fit.logistic <- sampling(model.logistic,dataset)
# 結果の出力
fit.logistic
## Inference for Stan model: logistic.
## 4 chains, each with iter=2000; warmup=1000; thin=1;
## post-warmup draws per chain=1000, total post-warmup draws=4000.
##
## mean se_mean sd 2.5% 25% 50% 75% 97.5% n_eff
## beta0 -16.89 0.11 2.32 -21.74 -18.37 -16.88 -15.31 -12.31 436
## beta1 0.09 0.00 0.01 0.07 0.09 0.09 0.10 0.12 437
## lp__ -609.25 0.04 0.95 -611.80 -609.63 -608.96 -608.57 -608.32 738
## Rhat
## beta0 1.00
## beta1 1.00
## lp__ 1.01
##
## Samples were drawn using NUTS(diag_e) at Mon Sep 3 19:34:21 2018.
## 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).
stanには専用の関数があって,これを利用した方が高速に結果を出すことができます。
## data{
## int<lower=0> N; // sample size
## real X[N]; // predictor
## int<lower=0,upper=1> Y[N]; // predicted value(should be 0,1)
## }
##
## parameters{
## real beta0; // baseline
## real beta1; // coefficient
## }
##
## model{
## //likelihood
## for(n in 1:N){
## Y[n] ~ bernoulli_logit(beta0 + beta1 * X[n]);
## }
## //prior
## beta0 ~ normal(0,100);
## beta1 ~ normal(0,100);
## }
## data{
## int<lower=0> L; // sample size
## real X[L]; // predictor
## int<lower=0> Y[L]; // predicted
## }
##
## parameters{
## real beta0;
## real beta1;
## }
##
## transformed parameters{
## real<lower=0> theta[L];
## for(l in 1:L)
## theta[l] = exp(beta0 + beta1*X[l]);
## }
##
## model{
## for(l in 1:L)
## Y[l] ~ poisson(theta[l]);
##
## //prior
## beta0 ~ normal(0,100);
## beta1 ~ normal(0,100);
## }
専用の関数バージョン
## data{
## int<lower=0> L; // sample size
## real X[L]; // predictor
## int<lower=0> Y[L]; // predicted
## }
##
## parameters{
## real beta0;
## real beta1;
## }
##
##
## model{
## for(l in 1:L)
## Y[l] ~ poisson_log(beta0 + beta1*X[l]);
##
## //prior
## beta0 ~ normal(0,100);
## beta1 ~ normal(0,100);
## }
# 野球データから野手だけフィルター
df3 <- bs %>% filter(position!="投手") %>%
# 変数を選択,欠損値の削除
dplyr::select("打席数","本塁打") %>% na.omit %>%
# 日本語変数を英語名に
rename(AT.Bat="打席数",HR="本塁打") %>%
# 単位を整えておきます
mutate(AT.Bat = AT.Bat/10)
# ホームランのヒストグラム
ggplot(df3,aes(x=HR))+geom_histogram(binwidth =3)
# ホームランと打席の散布図
ggplot(df3,aes(x=AT.Bat,y=HR))+geom_point()
# 事後予測分布を作るモデルに書き換え
model.pois <- stan_model("poisson3.stan")
# データ作成
data.pois <- list(L=NROW(df3),X=df3$AT.Bat,Y=df3$HR)
# MCMC
fit.pois <- sampling(model.pois,data.pois)
## Warning in .local(object, ...): some chains had errors; consider specifying
## chains = 1 to debug
## here are whatever error messages were returned
## [[1]]
## Stan model 'poisson3' does not contain samples.
# 表示
print(fit.pois,pars=c("beta0","beta1"))
## Inference for Stan model: poisson3.
## 3 chains, each with iter=2000; warmup=1000; thin=1;
## post-warmup draws per chain=1000, total post-warmup draws=3000.
##
## mean se_mean sd 2.5% 25% 50% 75% 97.5% n_eff Rhat
## beta0 -0.22 0 0.07 -0.35 -0.26 -0.22 -0.17 -0.08 736 1
## beta1 0.06 0 0.00 0.06 0.06 0.06 0.07 0.07 805 1
##
## Samples were drawn using NUTS(diag_e) at Mon Sep 3 19:34:26 2018.
## 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).
先ほどの例,事後予測分布をつかって予測区間をみてみます。
rstan::extract(fit.pois, pars = "pred") %>% data.frame %>%
# 変数を縦長にし,ケースごとにグループ化
tidyr::gather(key,val,factor_key=T) %>% group_by(key) %>% print %>%
# 統計量の計算
summarise(EAP=mean(val),
U95=quantile(val,prob=0.975),
L95=quantile(val,prob=0.025)) %>% ungroup %>%
# 元データの予測値と合わせる
mutate(AT=df3$AT.Bat,HR=df3$HR) %>% print %>%
# key列を除外
dplyr::select(-key) %>%
# 作図用に改めて縦長に作り直し
tidyr::gather(type,val,-AT,-HR,factor_key=T) %>%
# 作図
ggplot(aes(x=AT,y=val,color=type)) + geom_point() +
geom_point(aes(x=AT,y=HR),color="Black")
## # A tibble: 906,000 x 2
## # Groups: key [302]
## key val
## <fct> <dbl>
## 1 pred.1 1
## 2 pred.1 2
## 3 pred.1 3
## 4 pred.1 0
## 5 pred.1 3
## 6 pred.1 1
## 7 pred.1 1
## 8 pred.1 1
## 9 pred.1 0
## 10 pred.1 0
## # ... with 905,990 more rows
## # A tibble: 302 x 6
## key EAP U95 L95 AT HR
## <fct> <dbl> <dbl> <dbl> <dbl> <int>
## 1 pred.1 0.871 3 0 1 0
## 2 pred.2 25.0 35 16 52.9 9
## 3 pred.3 0.826 3 0 0.4 0
## 4 pred.4 3.33 7 0 21.8 4
## 5 pred.5 1.13 4 0 5.1 0
## 6 pred.6 11.6 19 5 41 29
## 7 pred.7 0.859 3 0 0.8 0
## 8 pred.8 1.52 4 0 9.7 3
## 9 pred.9 5.02 10 1 28.3 12
## 10 pred.10 1.44 4 0 8.7 1
## # ... with 292 more rows
結構外れている点がありますね。 これは同じ打席数であっても,HRバッターかどうかによって変わる個人差が大きいようです。 そこで,「基本的には打席数はホームラン数を予測する」けど「部分的には個人差が生じる」をモデル化して行きたいと思います。
全体的には \[ Y_i \sim Poisson(\lambda_i)\] ただし \[ \log(\lambda_i) = \beta_0 + \beta_1 X_i\] あるいは \[ \lambda_i = exp(\beta_0 + \beta_1 X_i) \]
ですが,個人差\(\tau_i\)を付け加えて \[ \log(\lambda_i) = \beta_0 + \beta_1 X_i + \tau_i\] とします。ただしこの個人差は人によってプラスマイナスはありますが,平均はゼロの正規分布に従うとします。 \[ \tau_i \sim Normal(0,\sigma^2) \]
回帰分析のロジックと同じで,平均部分が0なのですから, \[ \log(\lambda_i) \sim Normal(\beta_0 + \beta_1 X_i,\sigma^2) \]
これで個人変動\(\sigma^2\)を想定したモデルになります。
model.pois2 <- stan_model("poisson4.stan")
fit.pois2 <- sampling(model.pois2,data.pois)
# 係数の確認
print(fit.pois2,pars=c("beta0","beta1","sig"))
## Inference for Stan model: poisson4.
## 4 chains, each with iter=2000; warmup=1000; thin=1;
## post-warmup draws per chain=1000, total post-warmup draws=4000.
##
## mean se_mean sd 2.5% 25% 50% 75% 97.5% n_eff Rhat
## beta0 -0.99 0 0.14 -1.28 -1.08 -0.99 -0.90 -0.73 853 1
## beta1 0.08 0 0.00 0.07 0.08 0.08 0.08 0.09 1278 1
## sig 0.87 0 0.07 0.75 0.82 0.87 0.92 1.02 823 1
##
## Samples were drawn using NUTS(diag_e) at Mon Sep 3 19:34:44 2018.
## 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).
rstan::extract(fit.pois2, pars = "pred") %>% data.frame %>%
# 変数を縦長にし,ケースごとにグループ化
tidyr::gather(key,val,factor_key=T) %>% group_by(key) %>% print %>%
# 統計量の計算
summarise(EAP=mean(val),
U95=quantile(val,prob=0.975),
L95=quantile(val,prob=0.025)) %>% ungroup %>%
# 元データの予測値と合わせる
mutate(AT=df3$AT.Bat,HR=df3$HR) %>% print %>%
# key列を除外
dplyr::select(-key) %>%
# 作図用に改めて縦長に作り直し
tidyr::gather(type,val,-AT,-HR,factor_key=T) %>%
# 作図
ggplot(aes(x=AT,y=val,color=type)) + geom_point() +
geom_point(aes(x=AT,y=HR),color="Black")
## # A tibble: 1,208,000 x 2
## # Groups: key [302]
## key val
## <fct> <dbl>
## 1 pred.1 0
## 2 pred.1 2
## 3 pred.1 0
## 4 pred.1 0
## 5 pred.1 1
## 6 pred.1 0
## 7 pred.1 0
## 8 pred.1 1
## 9 pred.1 2
## 10 pred.1 1
## # ... with 1,207,990 more rows
## # A tibble: 302 x 6
## key EAP U95 L95 AT HR
## <fct> <dbl> <dbl> <dbl> <dbl> <int>
## 1 pred.1 0.401 2 0 1 0
## 2 pred.2 10.4 20 3 52.9 9
## 3 pred.3 0.385 2 0 0.4 0
## 4 pred.4 3.6 9 0 21.8 4
## 5 pred.5 0.503 3 0 5.1 0
## 6 pred.6 27.7 43 15 41 29
## 7 pred.7 0.395 2 0 0.8 0
## 8 pred.8 2.02 6 0 9.7 3
## 9 pred.9 10.7 21 3 28.3 12
## 10 pred.10 0.944 4 0 8.7 1
## # ... with 292 more rows
今のはポアソン回帰のパラメータ\(\lambda\)に事前分布ならぬ別の分布が入っている(確率分布の入れ子)ので,階層線形モデルと呼ばれるモデルになっています。
チームごとの階層性を考えてみましょう。次のグラフを見てください。
# 野手のデータだけフィルタリング
bs %>% filter(position != "投手") %>%
# 年俸と本塁打数,チーム情報変数をセレクト
select("salary", "本塁打", "team") %>%
# 欠損値を除外
na.omit() %>%
# 描画
ggplot(aes(x = 本塁打, y = salary, color = team)) +
geom_point() + geom_smooth(method = "lm", se = F)
チームごとに傾きが異なりそうですので,チームごとの傾きが違うことをモデリングして行きたいと思います。
# データセットの作成。まずは野手のみフィルタリング
df <- bs %>% filter(position != "投手") %>%
# 変数を選んで欠損地を除外
select("salary","本塁打", "team") %>% na.omit() %>%
# 縦軸の数字が桁違いに大きいので,桁を整えます
mutate(salary = salary/1000)
チームによって傾きは違う,というのを考慮するモデルを書きます。
普通の回帰分析は \[ Y_i = \beta_0 + \beta_1 X_i\] ですが,チーム(添字j)をつけて \[ Y_{ij} = \beta_0 + \beta_{1j} X_{ij} \] とします。
切片(\(\beta_0\))には\(j\)がついていないので,各チーム共通です。
# データはリスト型で渡す
data.HLM <- list(L=NROW(df),X=df$本塁打, Y=df$salary,
G=12, Idx=as.numeric(as.factor(df$team)))
model.HLM.0 <- stan_model('HLM0.stan')
## data{
## int<lower=0> L; // data length
## int<lower=0> G; //number of maximim groups
## real X[L];
## real Y[L];
## int Idx[L]; // index of team
## }
##
## parameters{
## real beta_0;
## real beta_1[G];
## real<lower=0> sigma[G];
## }
##
## model{
## for(l in 1:L){
## Y[l] ~ normal(beta_0 + beta_1[Idx[l]] * X[l], sigma[Idx[l]]);
## }
## beta_0 ~ normal(0,100);
## beta_1 ~ normal(0,100);
## sigma ~ cauchy(0,5);
## }
##
## generated quantities{
## real pred[L];
## for(l in 1:L){
## pred[l] = normal_rng(beta_0 + beta_1[Idx[l]]* X[l], sigma[Idx[l]]);
## }
## }
fit.HLM.0 <- sampling(model.HLM.0,data.HLM)
print(fit.HLM.0,pars=c("beta_0","beta_1"))
## Inference for Stan model: HLM0.
## 4 chains, each with iter=2000; warmup=1000; thin=1;
## post-warmup draws per chain=1000, total post-warmup draws=4000.
##
## mean se_mean sd 2.5% 25% 50% 75% 97.5% n_eff Rhat
## beta_0 2.47 0.01 0.37 1.74 2.22 2.47 2.72 3.17 4000 1
## beta_1[1] 0.46 0.00 0.10 0.26 0.40 0.46 0.53 0.67 4000 1
## beta_1[2] 0.63 0.00 0.11 0.41 0.56 0.63 0.71 0.86 4000 1
## beta_1[3] 0.65 0.00 0.14 0.38 0.56 0.65 0.75 0.94 4000 1
## beta_1[4] 0.63 0.00 0.16 0.30 0.52 0.63 0.73 0.94 4000 1
## beta_1[5] 0.77 0.00 0.12 0.53 0.69 0.77 0.84 0.99 4000 1
## beta_1[6] 1.13 0.00 0.26 0.64 0.95 1.12 1.30 1.64 4000 1
## beta_1[7] 0.56 0.00 0.21 0.15 0.43 0.56 0.70 0.99 4000 1
## beta_1[8] 0.22 0.00 0.15 -0.08 0.12 0.21 0.32 0.52 4000 1
## beta_1[9] 0.71 0.00 0.25 0.23 0.55 0.71 0.87 1.21 4000 1
## beta_1[10] 1.35 0.00 0.17 1.02 1.24 1.35 1.47 1.68 4000 1
## beta_1[11] 0.96 0.00 0.13 0.69 0.88 0.96 1.05 1.22 4000 1
## beta_1[12] 2.11 0.01 0.39 1.36 1.85 2.12 2.37 2.88 4000 1
##
## Samples were drawn using NUTS(diag_e) at Mon Sep 3 19:34:55 2018.
## 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).
# MCMCオブジェクトから予測値を取り出してデータフレームへ
rstan::extract(fit.HLM.0,pars='pred') %>% data.frame() %>%
# データを縦型に
tidyr::gather(key,val,factor_key=T) %>%
# 変数ごとにグルーピング
group_by(key) %>%
# 平均値を推定値とします
summarize(EAP=mean(val)) %>% ungroup %>%
# 元データの情報をくっつけて
mutate(salary=df$salary,team=df$team,HR=df$本塁打) %>%
# 必要な変数だけにして
select("EAP","HR","salary","team") %>%
# 描画
ggplot(aes(x=HR,y=EAP,color=team))+geom_point()+
geom_point(aes(x=HR,y=salary),color="Black")+
facet_wrap(~team)
回帰係数が12もあるのでわかりにくいかもしれません。そこで,回帰係数\(\beta_{1j}\)が分布することを考えます。すなわち, \[\beta_{1j} \sim Normal(\beta_{1m},\tau^2)\] のように,一箇所に情報を集約してやるのです。これもパラメータ\(\beta_{1j}\)に更に上位階層のパラメータ(\(\beta_{1m},\tau\))がついた階層モデルになります。
model.HLM <- stan_model('HLM.stan')
## data{
## int<lower=0> L;
## int<lower=0> G; //グループ数
## real X[L];
## real Y[L];
## int Idx[L];
## }
##
## parameters{
## real beta_0;
## real beta_1[G];
## real mu_beta1;
## real<lower=0> sigma[G];
## real<lower=0> tau;
## }
##
## model{
## for(l in 1:L){
## Y[l] ~ normal(beta_0 + beta_1[Idx[l]] * X[l], sigma[Idx[l]]);
## }
##
## beta_1 ~ normal(mu_beta1,tau);
## beta_0 ~ normal(0,100);
## sigma ~ cauchy(0,5);
## mu_beta1 ~ normal(0,100);
## tau ~ cauchy(0,5);
## }
##
## generated quantities{
## real pred[L];
## for(l in 1:L){
## pred[l] = normal_rng(beta_0 + beta_1[Idx[l]]* X[l], sigma[Idx[l]]);
## }
## }
fit.HLM <- sampling(model.HLM,data.HLM)
print(fit.HLM,pars=c("beta_0","mu_beta1","tau"))
## Inference for Stan model: HLM.
## 4 chains, each with iter=2000; warmup=1000; thin=1;
## post-warmup draws per chain=1000, total post-warmup draws=4000.
##
## mean se_mean sd 2.5% 25% 50% 75% 97.5% n_eff Rhat
## beta_0 2.47 0.01 0.37 1.72 2.22 2.48 2.72 3.19 4000 1
## mu_beta1 0.79 0.00 0.14 0.54 0.70 0.78 0.87 1.09 2741 1
## tau 0.39 0.00 0.14 0.18 0.29 0.37 0.46 0.71 2251 1
##
## Samples were drawn using NUTS(diag_e) at Mon Sep 3 19:35:04 2018.
## 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).
このようにすることで得られるメリットは三つあります。
一方留意すべき点として,次の二点があります。
上位階層のパラメータであったとしても,ベイジアンモデリングの文脈では事後分布という同時確率空間の中にあることを思い出してください。
二点めのことについては,渡邊先生のサイトに詳しいので,参照してください。
個人iが刺激(項目)jに反応したデータ\(Y_{ij}\)は,正答を1,誤答を0としたダミーデータが入っているとします。この時,個人の正答率\(p_i\)も検証したいですが,問題が難問なのか簡単だったのか,という難易度にも夜と思われます。そこで難易度を\(q_j\)とし,
\[ Y_{ij} \sim Bernoulli(\theta_{ij})\] \[ \theta_{ij} = p_i \times q_j \] を考えたモデルを書くことができます。
model.irt0 <- stan_model("IRT0.stan")
## data{
## int<lower=0> N; //number of persons
## int<lower=0> M; //number of questions
## int K[N,M];
## }
##
## parameters{
## real<lower=0,upper=1> p[N];
## real<lower=0,upper=1> q[M];
## }
##
## transformed parameters{
## real<lower=0,upper=1> theta[N,M];
## for(n in 1:N){
## for(m in 1:M){
## theta[n,m] = p[n] * q[m];
## }
## }
## }
##
## model{
## for(n in 1:N){
## for(m in 1:M){
## K[n,m] ~ bernoulli(theta[n,m]);
## }
## }
## p ~ beta(1,1);
## q ~ beta(1,1);
## }
このように,刺激の特性と個人の特性を区別して推定できるようになります。実験刺激の個々の特性から独立した個人の性質を測定したい場合などに応用できるでしょう。
この考え方は項目反応理論(Item Response Theory)とよばれるテスト理論の一部を,ベイズモデルとして書いたものです(よく用いられるラッシュモデルは異なる定式化をしますが)。多段階の反応モデルも提案されており,リッカーと尺度の分析などにも応用できるでしょう。
ベイズモデルを使うと,このように「個人でネストしている」「変数でネストしている」というような状況も表現できますし,それぞれの上位階層を想定することも可能です。まさに,ベイジアンモデリングの限界は研究者の想像力だけなのです。