μ's とAqours の人気の差

こんな記事があった。あるアニメショップでキャラの人気投票をしたら、ラブライブにおいてμ's のメンバーのほうが、Aqours のメンバーより総じて上位だったらしい。
というわけで、2グループの人気はどれくらいの差かを考える。
 
2グループ各9人、全部で18人のキャラの得票数V_i\hspace{3}(i=1,\dots,18)がある。あるベースB に各キャラの効果b_i\hspace{3}(i=1,\dots,18)、グループ効果g があり、18 人の所属はG_i={0,1} であるとする。18人のハイパーパラメータ\alpha_i\hspace{3}(i=1,\dots,18)
\alpha_i = gG_i  + b_i +B
投票確率p_i はディリクレ分布
p \sim \textrm{dirichlet}(\alpha)
得票数は多孔分布
V\sim \textrm{multinomial}(\alpha)
でサンプリングされるとする。
 
結果としては\hat{R}=1.5 程度が多く、収束しなかった。また、n_eff が全然なかった。
また、g が何十万とかなって単純にμ's だと何倍人気になる、というのがわかりにくかったので、\alpha の事後分布を各グループについて中央値を取って何倍人気に差があるか、にしている。すると2.5倍くらいμ's とAqours に人気の差があるようだった。


a <- apply(ex$alpha, 1, tapply, dat$group, median)
quantile(a[2,]/a[1,], c(0.025, 0.5, 0.975))
# 人気の差
    2.5%      50%    97.5% 
2.111500 2.469390 3.011622 
code <- "
data{
  #int<lower=1> N_vote;
  int<lower=1> Member;
  int<lower=1> Vote[Member];
  int<lower=0, upper=1> group[Member];
}
parameters{
  real<lower=-2, upper=2> base;
  real b[Member];
  real g;
  simplex[Member] p;
}
transformed parameters{
  vector<lower=0>[Member] alpha;
  for(i in 1:Member){
    #alpha[i] = inv_logit(g*group[i] + b[i] + base);
    alpha[i] = g*group[i] + b[i] + base;
  }
}
model{
  p ~ dirichlet(alpha);
  Vote ~ multinomial(p);
}
"

library(rstan)
rstan_options(auto_write = TRUE)
options(mc.cores = parallel::detectCores())
m <- stan_model(model_code=code)

standata <- list(Member=length(dat$name), Vote=dat$vote, group=c(dat$group)-1)
fit <- sampling(m, standata, warmup=5000, iter=20000)
ex <- extract(fit)

par(mar=c(4.5, 5.5, 2, 2), cex.lab=1.5)
plot(0, type="n", xlim=c(0, max(ex$p)), ylim=c(1, nrow(dat)), xlab="投票率", ylab="", yaxt="n")
abline(h=seq(nrow(dat)), lty=3)
for(i in 1:nrow(dat)) vioplot(ex$p[,i], at=i, add=TRUE, col=c("skyblue", "orange")[c(dat$group)[i]], horizontal=TRUE)
text(par()$usr[1]-0.027, seq(nrow(dat)), dat$name, xpd=TRUE, pos=4)

データ

name vote group
矢澤にこ 384 muse
西木野真姫 384 muse
南ことり 370 muse
東條希 336 muse
綾瀬絵里 283 muse
園田海未 263 muse
小泉花陽 251 muse
星空凛 226 muse
津島善子 190 aqours
黒澤ルビィ 171 aqours
国木田花丸 155 aqours
渡辺曜 141 aqours
高坂穂乃果 128 muse
松浦果南 110 aqours
黒澤ダイヤ 103 aqours
桜内梨子 93 aqours
小原鞠莉 80 aqours
高海千歌 74 aqours

PK戦での各試行に及ぼす影響をrstan でやってみる

MikuHatsune2017-03-07

PK 戦の順序が勝ちやすさに影響するか考えたかったけど、データを集めた時点で先攻が勝つ確率が50% だったので、いろいろな条件のもとでのPK の成功率を考えていた。
stan でやってみる。
 
PK は10人が蹴るまでに終わるとする(154試合1389回)。
各PK での成功率はp である。これが、1人目から10人目までのうちj 番目のキッカーに対して、以下の影響(係数)\beta があるとする。
\beta_1:切片。他の影響が何もないときの基礎成功率。
\beta_2:直前の相手の成功(1)/失敗(0) による影響。直前の相手の成功/失敗はz_{j-1}\in \{0,1\} のベルヌーイ試行とする。
\beta_3:直前の味方の成功(1)/失敗(0) による影響。直前の味方の成功/失敗はz_{j-2}\in \{0,1\} のベルヌーイ試行とする。
\beta_4:PK の回が進むに連れて受ける影響。j にそのまま線形に乗ずる。
\beta_5:そのPK を成功すると勝利、もしくは、失敗すると敗北のフラグに関する影響。そのPK の成功/失敗に関係なく、試合が決まらなければ0、成功した時に勝利するなら1、失敗した時に敗北するなら-1 の値を格納しておく。
というパラメータを用意して、実際の成功率は
p=\textrm{invlogit}(\beta_1+z_{j-1}\beta_2+z_{j-2}\beta_3+j\beta_4+f_j\beta_5)
という変換で[0,1] にしておく。ここでf_jj番目の選手が蹴るときの勝敗フラグ\{-1,0,1\} である。
PK の成功/失敗は、i 試合目のj 番目の選手についてz_{i,j}\in \{0,1\} なので、
z_{i,j}\sim \textrm{bernoulli}(p_j)
とサンプリングされる。
 
R でやるときは、例えば8回目で終わって9,10回目がNA のときを回避するためのidx, 1番目と2番目の選手については、直前の相手(j-1),直前の仲間(j-2) の結果がないので、影響がなくてもいいように0 を無理やりデータにいれてrstan を回している。
 
結果としては、4chain 2000 回ですべて収束した。計算は10秒くらい。

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
b[1]    1.56    0.00 0.16    1.23    1.45    1.56    1.67    1.89  1829    1
b[2]   -0.18    0.00 0.15   -0.47   -0.29   -0.18   -0.08    0.11  2062    1
b[3]    0.10    0.00 0.15   -0.20    0.00    0.10    0.20    0.39  2149    1
b[4]   -0.06    0.00 0.03   -0.12   -0.08   -0.06   -0.05   -0.01  1987    1
b[5]    0.84    0.00 0.16    0.53    0.73    0.83    0.95    1.17  2662    1
lp__ -751.50    0.04 1.60 -755.38 -752.35 -751.15 -750.33 -749.44  1489    1

この結果はinv.logit する前の線形パラメータなので、inv.logit して考える。

             [,1]        [,2]        [,3]         [,4]      [,5]
  2.5%  0.7739358 -0.11624136 -0.04874818 -0.029273129 0.1283631
  50%   0.8267488 -0.04609405  0.02457289 -0.016001107 0.1960369
  97.5% 0.8683126  0.02859455  0.09669435 -0.002499696 0.2622942

基礎成功率は83% だった。
95% 信用区間的に有意なのはPK の順番と勝敗フラグのときだった。PK の順番は効果量が小さいが信用区間が狭く、PK の順番が進むごとに1.6% 成功率が下がる。
勝敗フラグがあるとき、20% 近く確率の変動がある(勝ちフラグは1、負けフラグは-1 なのでそのまま考えてよい)。
直前の相手/仲間の成功は有意ではないとは言え、仲間が成功していたら正の効果(2.5%)、相手が成功していたら負の効果(-4.6%)はある様子。

 
こういう効果を踏まえていろいろ計算すると、全体的な成功率は76% 程度になる。
各回での成功率をプロットすると、PK の順番係数が有意に負なので、全体的にPK の回が進むと、成功率は下がる。
8回目以降で裾が広く、山がふたつあるように見える。10回目に至っては成功率が上がっている。これは勝敗フラグによりふたつの状況が混在しているためにこのようになっている。

そこで勝敗フラグによってPK を分類してそれぞれ成功率をプロットすると、勝ちフラグ時には86%、負けフラグ時には51% の成功率だった。

 
フットボールネーション、いまPK 戦をしていて、3話くらい前にPK 線の確率の話をしていた気がするが、休載が多くて忘れた。

モデル

data{
  int<lower=0> N; # No. game
  int<lower=0, upper=1> PK[12, N]; # result of kick
  int<lower=-1, upper=1> Flag[12, N]; # Win or lose flag
  int<lower=0, upper=12> idx[N];
}
parameters{
  real b[5];
}
model{
  {
    real p;
    p <- inv_logit(b[1] + PK[j-1, i]*b[2] + PK[j-2, i]*b[3] + (j-2)*b[4] + Flag[j, i]*b[5]);
    for(i in 1:N){
      for(j in 3:idx[i]){
        PK[j, i] ~ bernoulli(p);
      }
    }
  }
}
library(rstan)
library(vioplot)
library(gtools)
dat <- readLines("pk.txt")
s <- do.call(rbind, mapply(strsplit, dat, " ", USE.NAMES=FALSE))
s1 <- lapply(mapply(strsplit, s[,3], "", USE.NAMES=FALSE), as.numeric)

# 勝敗が決まりうる番での確率
# すべて書き出す
tide <- function(vec){
  n <- 6:length(vec)
  flag <- rep(0, length(vec))
  for(i in n){
    p1 <- vec[ which(odd(seq(i-1))) ]
    p2 <- vec[ which(even(seq(i-1))) ]
    dif <- sum(p1) - sum(p2)
    if(i == 6){
      if(dif == 3){
        flag[i] <- -1
      } else if(dif == -2){
        flag[i] <- 1
      }
    } else if(i == 7) {
      if(dif == 2){
        flag[i] <- 1
      } else if (dif == -2) {
        flag[i] <- -1
      } 
    } else if (i == 8) {
      if (dif == 2){
        flag[i] <- -1
      } else if (dif == -1) {
        flag[i] <- 1
      }
    } else if (i == 9) {
       if (dif == 1){
         flag[i] <- 1
       } else if (dif == -1){
         flag[i] <- -1
       }
    } else if (i == 10){
      if (dif == 1) {
        flag[i] <- -1
      } else if (dif == 0){
        flag[i] <- 1
      }
    } else {
      if(i%%2 == 0){
        flag[i] <- ifelse(dif==0, 1, -1)
      }
    }
  }
  return(flag)
}

wlflag <- mapply(tide, s1)
a1 <- s1[ sapply(s1, length) < 11 ]
wl1 <- wlflag[ sapply(s1, length) < 11 ]

vec10 <- function(vec) c(vec, rep(NA, 10-length(vec)))
a2 <- rbind(0, 0, mapply(vec10, a1))
wl2 <- rbind(0, 0, mapply(vec10, wl1))
idx <- colSums(!is.na(a2))
a2[is.na(a2)] <- 0
wl2[is.na(wl2)] <- 0

m <- stan_model(model_code=stanmodel)
standata <- list(N=ncol(a2), PK=a2, Flag=wl2, idx=idx)
fit <- sampling(m, standata)
ex <- extract(fit)

# rstan 内でできなかったので計算後に再計算する
p <- matrix(NA, 12, ncol(a2))
for(i in 1:ncol(a2)){
  b <- ex$b[i,]
  for(j in 3:idx[i]){
    p[j, i] <- inv.logit(b[1] + a2[j-1, i]*b[2] + a2[j-2, i]*b[3] + (j-2)*b[4] + wl2[j, i]*b[5])
  }
}
p <- t(tail(p, -2))

plot(0,  0, type="n", xlim=c(1, ncol(p))+c(-1, 1)*0.5, ylim=c(0, 1), xlab="PK 戦の蹴る回", ylab="成功率")
for(i in 1:ncol(p)) vioplot(na.omit(p[,i]), at=i, add=TRUE, col=7)

w <- tapply(c(p), c(t(tail(wl2, -2))), c)
w <- w[-2]
plot(0, 0, type="n", xlim=c(0, 1), ylim=c(1, 2)+c(-1, 1)*0.5, yaxt="n", xlab="成功確率", ylab="")
for(i in seq(d)) vioplot(w[[i]], at=i, add=TRUE, horizontal=TRUE, col=c("blue", "red")[i])
text(par()$usr[1], 1:2, c("負けフラグ", "勝ちフラグ"), pos=4, xpd=TRUE)

r <- sweep(inv.logit(ex$b), 2, c(0, inv.logit(rep(0, 4))), "-")
bcoef <- c("基礎成功率", "直前の相手の成功", "直前の仲間の成功", "PK の順番", "勝敗フラグ時")
par(mar=c(4.5, 10, 2, 2))
plot(0, 0, type="n", ylim=c(1, ncol(r))+c(-1, 1)*0.5, xlim=range(r), ylab="", xlab="確率", yaxt="n")
abline(v=0, lty=3)
for(i in 1:ncol(r)) vioplot(r[,i], at=i, add=TRUE, col=7, horizontal=TRUE, frame=FALSE)
axis(2, 1:ncol(r), labels=NA)
text(par()$usr[1]-0.45, 1:ncol(r), bcoef, xpd=TRUE, pos=4)

EM アルゴリズムとベイズ

MikuHatsune2017-03-02

EM アルゴリズムベイズという話が出てきたので、やってみる。
題材はこちら
状況としては、ABOの血液型で、どんな血液型を持っているかは観測できるが、その血液型population を生み出したアレル頻度は一体どのようなものだろうか、これを推定したい、ということ。
EM アルゴリズムベイズも、観測できない潜在パラメータ\theta を、手持ちのデータからなんとかして推定しようという試み。
 
EM では適当にABO のアレル頻度P_a, P_b, P_o として、実際にABO 血液型を観測した人数に当てはめて、アレルの期待値を出して、それを更新して人数に当てはめてアレルの期待値を出して、…を繰り返す。
(ここで、ABO 血液型がどのように生じるかという遺伝学、分子生物学的な知識は既にあるものとし、ボンベイ型やシスAB 型みたいなレアなものは考えないとする。)
すると、リンクのような、A:B:AB:O=160:80:40:120のときに、アレル頻度をEM で求めると

dat <- c(A=160, B=80, AB=40, O=120)
N <- sum(dat)*2

# 初期値
pa <- 0.2
pb <- 0.2
po <- 1-pa-pb

ABO <- c(pa, pb, po)

niter <- 10
for(i in 1:niter){
  # E-step
  pA <- c(pa*(pa+po) / (pa*(pa+2*po)), 0, pa*pb/(2*pa*pb), 0)
  nA <- sum(pA * dat)

  pB <- c(0, pb*(pb+po)/(pb*(pb+2*po)), pa*pb/(2*pa*pb), 0)
  nB <- sum(pB * dat)

  nO <- sum(dat) - nA - nB
  # M-step
  pabo <- c(nA, nB, nO)/sum(dat)
  
  pa <- pabo[1]
  pb <- pabo[2]
  po <- pabo[3]

  ABO <- rbind(ABO, pabo)
}

matplot(ABO, type="l", lwd=3, lty=1, ylim=c(0, 1), xlab="iteration")
legend("topright", legend=c("A", "B", "O"), lty=1, col=1:3, lwd=3)

アルゴリズムの停止は、更新値がどれだけ更新されなくなったかという収束限界値をいれてもいいし、更新回数でもよい。
この場合ではほんの数回でほぼ収束する。

 
ベイズ的にやってみる。rstan でやるならば、アレルABO は足して1になるのでsimplex からサンプリングされ(dirichlet)、実際のABO の血液型は、そのアレル頻度から理論的(HWE)に推定されるA/B/AB/O 血液型の確率から非負の整数値がサンプリングされる(multinomial)とする。このとき、A/B/AB/O 血液型は足して1になる。というのは、普通に考えてそうだし、A/B/AB/O を生み出すABO アレルがそもそも足して1 なので計算上も勝手にそうなる。

library(rstan)
stanmodel <- '
data{
  int<lower=0> N[4];
}
parameters{
  simplex[3] pABO;
}
transformed parameters{
  simplex[4] p;
  p[1] <- pABO[1]*pABO[1] + 2*pABO[1]*pABO[3]; 
  p[2] <- pABO[2]^2 + 2*pABO[2]*pABO[3];
  p[3] <- 2*pABO[1]*pABO[2];
  p[4] <- pABO[3]^2;
}
model{
  N ~ multinomial(p);
}
'
m <- stan_model(model_code=stanmodel)
fit <- sampling(m, list(N=dat))
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
pABO[1]    0.29    0.00 0.02    0.26    0.28    0.29    0.30    0.33  3005    1
pABO[2]    0.16    0.00 0.01    0.14    0.15    0.16    0.17    0.19  2609    1
pABO[3]    0.54    0.00 0.02    0.50    0.53    0.54    0.56    0.58  2525    1
p[1]       0.40    0.00 0.02    0.36    0.39    0.40    0.42    0.45  3058    1
p[2]       0.20    0.00 0.02    0.17    0.19    0.20    0.22    0.24  2830    1
p[3]       0.10    0.00 0.01    0.08    0.09    0.10    0.10    0.11  2462    1
p[4]       0.30    0.00 0.02    0.25    0.28    0.30    0.31    0.34  2522    1
lp__    -516.70    0.03 1.06 -519.64 -517.09 -516.35 -515.96 -515.70  1139    1

実行結果は、EM の場合とほぼ一緒である。
実際にサンプリングされたABO アレル頻度の散布図である。上の推定結果からもわかるように、非常に狭い範囲で推定されている。図でもx+y+z=1 の平面上の極狭い部分にしか点が存在しない。
モデルとしてはかなりシンプルなので推定が楽だし、ベイズを使うとEM では初期値を(A,B,O)=(0.2,0.2,0.6) と選んだのがなぜそれを選んだの?(先行研究から選ぶもんだが) という疑問が拭えないので、ベイズで無情報分布でうまくいったのでいいことにする。
もちろん、EM でも初期値をディリクレ分布から適当に選びまくって推定して、どんな初期値でも同じような結果に収束する(今回はするだろうけど)ことを確認してもいいし、逆にベイズのほうでも、事前に先行研究から範囲が分かっていれば、ABO アレルのsimplex サンプリングに偏ったdirichlet 分布から発生されてもよいはず。

声優統計の統計C91版

MikuHatsune2016-12-30

声優統計第九号は、新刊の第九号と既刊の5-8 がまとまった論文集2 ともに完売しました。

 
そして、本当に最後らしいです。
告知から最後、と言っていて、自分も原稿中に「最後の…」とか書いてて、コピー本印刷を見に行った時に完成サンプルをみても他執筆陣が「最後の…」とか書いてたので、壮大なドッキリが待ち構えていると思っていたのですが、ここまでくると本当に最後のようです。
最後に怒涛の自己引用をしたので、インパクトファクターは少し持ち直して、0.18 となりました。

 
まんがタイムきららフェスタ2016 の観客動員力の推定というものをrstan でやりました。条件式からしてスッカスカだったので推定結果は本当にこんなんでいいの? 感が満載です。
 
出演パターン

 
推定結果

 
出演情報

anime	bd	茅野愛衣	M・A・O	水瀬いのり	高橋李衣	小澤亜季	種田梨紗	内山夕実	田中真奈美	東山奈央	西明日香	和久井優	金澤まい	今村彩夏	戸田めぐみ	山口愛	高田憂希	竹尾歩美	花守ゆみり	白石晴香	安野希世乃	山村響	吉岡茉祐	長縄まりあ	前川涼子	大久保瑠美	内田真礼
がっこうぐらし	2426	1	1	1	1	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0
きんいろモザイク	6582	0	0	0	0	0	1	1	1	1	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0
三者三葉	2269	0	1	0	0	0	0	0	0	0	1	1	1	1	0	0	0	0	0	0	0	0	0	0	0	0	0
NEWGAME	6452	1	0	0	0	0	0	0	0	0	0	0	0	0	1	1	1	1	0	0	0	0	0	0	0	0	0
あんハピ	1197	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	1	1	1	1	0	0	0	0
ステラのまほう	1000	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	1	0	0
ご注文はうさぎですか?	11038	1	0	1	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1
ゆゆ式	3082	1	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0
# model01.stan
data{
  int<lower=0> N;
  int<lower=0> M;
  int<lower=0, upper=1> castN[N, M];
  int<lower=0> BD[M];
}
parameters{
  real<lower=0> theta[N];
  #real<lower=0> s[M];
}
transformed parameters{
  real<lower=0> alpha[M];
  for(j in 1:M)
    alpha[j]  <- 0;

  for(i in 1:N)
    for(j in 1:M)
      alpha[j] <- alpha[j] + theta[i]*castN[i, j];
}


model{
  BD ~ poisson(alpha);
}
library(rstan)
library(vioplot)
d <- read.delim("kirara.txt", stringsAsFactors=FALSE, row=1, check.names=FALSE)
bd <- d$bd
dat <- subset(d, select=-bd)
festa <- 5000

dat <- rbind.data.frame(dat, "きららフェスタ"=rep(1, ncol(dat)))
bd <- c(bd, festa)

bvec <- rep(1, ncol(dat))
const.dir <- rep("==", nrow(dat))
solveLP(bvec, bd, dat, maximum=TRUE, const.dir)



N <- ncol(dat)
M <- nrow(dat)
castN <- unlist(dat[1,])
castN <- t(as.matrix(dat))
BD <- bd
stan_data <- list(N=N, M=M, castN=castN, BD=BD)


model <- stan_model("model01.stan")
fit <- sampling(model, stan_data, chain=3, warmup=1000, iter=2000)
ex <- extract(fit)
theta <- apply(ex$theta, 2, median)
names(theta) <- colnames(dat)
alpha <- apply(ex$alpha, 2, median)

rowSums(sweep(dat, 2, theta, "*"))




d <- t(dat)
colcex <- replace(rep(1, ncol(d)), 7, 0.8)

# 出演パターン
par(mar=c(6, 8, 1.5, 3))
image(seq(nrow(d)), seq(ncol(d)), d, xaxt="n", yaxt="n", col=c(0, 1), xlab="", ylab="")
text(par()$usr[1]-8.5, seq(ncol(d)), colnames(d), xpd=TRUE, pos=4, cex=colcex)
text(par()$usr[2], seq(ncol(d)+1), c(bd, "売上"), xpd=TRUE, pos=4)
tate <-  mapply(function(z) paste0(strsplit(z, "")[[1]], collapse="\n"), rownames(d))
ad <- nchar(rownames(d))-min(nchar(rownames(d)))
ady <- 0.2
text(seq(nrow(d)), par()$usr[3]-0.3-ad*ady, tate, xpd=TRUE, pos=1)
abline(v=head(seq(nrow(d)), -1)+0.5, h=head(seq(ncol(d)), -1)+0.5, lty=3)

# 推定結果の可視化
par(mar=c(5, 5.2, 2, 3.2), cex.lab=1.5)
plot(0, xlim=c(0, max(ex$theta)), ylim=c(1, ncol(dat)), type="n", yaxt="n", xlab="観客動員数の推定値", ylab="")
abline(h=seq(ncol(dat)), lty=3, col=grey(0.2))

text(par()$usr[2], seq(ncol(dat)+1), c(round(apply(ex$theta, 2, median)), "最頻値"), pos=4, xpd=TRUE)
text(par()$usr[1]-3500, seq(ncol(dat)), colnames(dat), pos=4, xpd=TRUE)
eval(parse(text=paste0("vioplot(", paste0(paste0("ex$theta[,", seq(ncol(dat)), "]"), collapse=","), ",horizontal=TRUE,names=rownames(d),col=grey(0.8),border=NA,add=TRUE,colMed='black')")))
title	author	publish	C84	C85	C86	C87	C88	C89	C90	C91
日本声優統計学会発足のご挨拶 -声優と科学の融合を目指して	@MagnesiumRibbon	83	0	0	0	1	1	1	0	0
Wikipediaの声優PVデータ特性とブレイク判定手法	@kkobayashi	83	2	0	1	0	1	0	0	0
キャスティング情報のbag-of-声優モデルを用いた音響監督推定問題	@MagnesiumRibbon	83	1	1	1	0	0	1	0	0
声優統計入門	@R_Linux	83	0	0	0	0	0	0	0	0
ブログを用いた女性声優の結婚時期予測問題	@y_benjo	83	0	0	0	1	0	1	0	1
アニメの内容及びキャスティングを用いないDVD売上予測問題	@y_benjo	83	1	0	1	0	0	0	0	1
序文 「日本声優統計学会に寄せて」	@toddler2009	84	0	0	0	0	0	0	0	0
トピックモデルを用いたニコニコ動画コメントデータの声優トピック流行推移解析	@Med_KU	84	0	0	0	1	0	0	0	0
声優活動における「元アイドル」の影響予測	@kkobayashi	84	0	0	0	0	1	0	0	0
声優ブログの「ご報告」エントリ自動検出システムの検討	@MagnesiumRibbon	84	0	0	0	1	0	0	0	0
続・声優統計入門 貧乳と巨乳の狭間で	@R_Linux	84	0	0	0	0	0	0	0	0
音声による既婚声優の判別問題	@y_benjo	84	0	1	0	0	1	1	1	1
Labeled LDAを用いた声優のニコニコ動画における特徴的コメントの抽出	@y_benjo	84	0	0	0	1	0	0	0	0
序文: 声優統計における言語情報と非言語情報 -- 『声優統計』第三号に寄せて --	@langstat	85	0	0	0	0	0	0	0	0
声優も「箱で推せ!」 -- 声優ファンにおける推し声優コミュニティの検出 --	@kkobayashi	85	0	0	0	0	0	0	0	0
ソーシャルな声優イベント参加履歴に基づく声優ファン行動の定量化分析	@MagnesiumRibbon	85	0	0	0	2	0	0	0	1
アニメ,声優,二次創作における百合ネットワークの考察	@Med_KU	85	0	0	0	0	0	0	0	1
複数の声優によるセリフの音響的類似性の考察:不愉快です	@Med_KU	85	0	0	0	0	0	0	0	0
続・続・声優統計入門 -- 初めてのテキストマイニング --	@R_Linux	85	0	0	0	0	0	0	0	0
今会いに行ける声優: ブログに登場する位置情報単語を用いた声優の出現位置予測	@y_benjo	85	0	0	1	1	0	0	0	0
声優の結婚時期予測2013: 2012年予測の精度,変化	@y_benjo	85	0	0	0	0	0	1	0	0
Twitterからみる声優ファンのネットワーク構造	@ysks3n	85	0	0	0	0	0	0	0	0
序文: 声優と統計とシンギュラリティ -- 声優統計の目指す未来 --	@MagnesiumRibbon	86	0	0	0	0	0	0	0	0
種田梨沙が出演すると百合アニメか?: Propensity score matching による検討	@Med_KU	86	0	0	0	0	0	0	1	0
声優ファンが今推したいアイドル	@kkobayashi	86	0	0	0	0	0	0	0	0
声優固有のアニメ顔は存在するか: Deep Learning を用いたアニメ画像キャスティング一致問題	@y_benjo	86	0	0	0	0	0	0	0	0
声優統計未解決問題	@y_benjo	86	0	0	0	0	1	0	0	0
田村ゆかりは永遠の 17 歳なのか? - CV から見た声年齢の推移 -	@harapon	87	0	0	0	0	0	0	0	0
現役女子高生声優とその周辺事情	@kkobayashi	87	0	0	0	0	0	0	0	0
晴れ声優もしくは雨声優に対する統計学的考察	@MagnesiumRibbon	87	0	0	0	0	0	0	1	0
Twitterの投稿時間分布から見る声優の生態	@Med_KU	87	0	0	0	0	0	0	0	0
パンツを求めて	@R_Linux	87	0	0	0	0	0	0	0	0
主役力 : キャストの表記順に着目したプレイヤーレーティング	@y_benjo	87	0	0	0	0	0	0	0	0
声優の食事内容の検討 - 外食声優を求めて -	@dichika	87	0	0	0	0	0	0	0	0
序文 : 人工声優は東京ドーム公演の夢を見るか?	@hitoshi_ni	88	0	0	0	0	0	0	0	0
イベント出演状況から予想するネクストブレイク声優	@kkobayashi	88	0	0	0	0	0	1	0	0
同一セリフからの声優と心情の同時推定問題 -- 声優統計標準ベンチマークの提案	@MagnesiumRibbon	88	0	0	0	0	0	0	0	1
ダメ絶対音感:レベル・ネオは早見沙織? 日笠陽子?	@Med_KU	88	0	0	0	0	0	0	0	1
青田買いの神話 : 青田買いを考慮した製品普及モデルにもとづく声優分析	@y_benjo	88	0	0	0	0	0	0	0	0
脇役識別問題	:-)	88	0	0	0	0	0	0	0	0
結婚したら声優は仕事が減るのか?	@Med_KU	89	0	0	0	0	0	0	0	0
日本声優統計学会 投稿&査読ガイド	@MagnesiumRibbon	89	0	0	0	0	0	0	0	0
「他界」の科学 : 限界効用逓減と代替財を考慮した声優イベント参加モデル	@y_benjo	89	0	0	0	0	0	0	0	0
Wikipediaとラジオでの楽曲選択に基づく黒沢ともよさんの音楽嗜好推定	@wakuteka	89	0	0	0	0	0	0	0	1
Bluemix × Watson × 声優	@kkobayashi	89	0	0	0	0	0	0	0	0
なれる!声優〜Deep Learning を利用した声質変換〜	@asteerism	90	0	0	0	0	0	0	0	0
声優しりとり	@Med_KU	90	0	0	0	0	0	0	0	0
複数声優歌唱楽曲における歌唱パート特定問題 ~声優統計的ハイレゾのススメ~	@MagnesiumRibbon	90	0	0	0	0	0	0	0	0
「他界」の科学 (2) : 個別の感染症モデルにもとづくイベント参加予測	@y_benjo	90	0	0	0	0	0	0	0	0
会いにいける賃貸住宅を求めて	@wakuteka	90	0	0	0	0	0	0	0	1
声優統計特別研究員	@Med_KU	91	0	0	0	0	0	0	0	0
まんがタイムきららフェスタ2016	@Med_KU	91	0	0	0	0	0	0	0	0
声優力	@Med_KU	91	0	0	0	0	0	0	0	1
前書き	@R_Linux	91	0	0	0	0	0	0	0	0
seiyu2vec	@MagnesiumRibbon	91	0	0	0	0	0	0	0	0
二次配布可能な音素バランス文と声優統計音声コーパスの構築	@y_benjo	91	0	0	0	0	0	0	0	0
テレビアニメにおける新人声優とその傾向について	@kkobayashi	91	0	0	0	0	0	0	0	0
黒沢ともよさんの音楽嗜好に基づくロックフェス推薦システムに関する取り組み	@wakuteka	91	0	0	0	0	0	0	0	0

声優統計第九号 声優力

MikuHatsune2016-12-25

この記事は
R Advent Calendar 2016
Stan Advent Calendar 2016
ごちうさ Advent Calendar 2016
まんがタイムきらら Advent Calendar 2016
の25日目の担当記事です。
 
C91 で声優統計ネタとして声優力を推定します。声優力とはなんぞや、という話ですが、ある声優がアニメに出演するとき、主役だったりメインヒロインだったりすると、上位にキャストされると思います。そのキャストされるのがどれだけ上位か、というのをデータから得て、声優力の推定をします。
例えば総勢x 人の声優が出演するとき、そのy 番目に名前があったとしたら、p=\frac{x-y+1}{x+1}として声優力を[0,1] のデータにします。1 に近いと上位にいて、0に近いと末尾に名前があることに相当します。
これは[0,1] にしたかったので適当な変換になります。[0,1] にすると、pベータ分布からサンプリングされるだろうと勝手に仮定できます。ベータ分布を決めているパラメータは、shape parameter 1 と2 の\alpha, \beta であり、この分布の平均値は\frac{\alpha}{\alpha+\beta} となります。サンプリングは
p \sim \textrm{beta}(\alpha, \beta)
となります。
これを2008年から2016年まで、1年を3ヶ月ずつ冬春夏秋クールの時系列に分けて推定する。
 
ごちうさ声優陣である佐倉綾音水瀬いのり種田梨沙佐藤聡美内田真礼茅野愛衣村川梨衣徳井青空早見沙織の声優力をみてみましょう。
村川梨衣以外は、全期間で割と高い声優力を有しているようなクラスターに属した。村川梨衣は2013年以降に声優力が高いクラスター、つまり結構若手なクラスターに属した。

 
村川梨衣以外のごちゃっとしたクラスターを拡大して検討してみる。
左にあるクラスタリングを適当に6個に分けた。
 
下から、ピンク茶色クラスターは、2008年から2016年までぎっちりと高い声優力を保って出演を続けている声優たちである。ピンクのほうは2016年直近もよく出ているが、茶色は2016年に近い年代で白(出演なし)が目立つところで差が出ている。
ピンクのクラスターには戸松遥豊崎愛生井上麻里奈悠木碧釘宮理恵伊藤静沢城みゆき福園美里花澤香菜らが属した。誰が聞いてもわかるし納得の面々だと思う。個人的には井上麻里奈伊藤静がいるのが超うれしい。
茶色には早見沙織が属した。はやみんは2016年もよく出ているが、どちらかというと2008年ころの出演がないの茶色に属したようである。
 
濃い緑クラスターは、2011年から2012年ころから出演しはじめた声優が属している。水瀬いのり種田梨沙らが属した。黄色の点線は種田梨沙のデビューということで引いている。
声優統計本誌では、種田梨沙に近くて種田梨沙の声優力を模倣してくれるであろう声優をピックアップし、2017年に推して行こうという話が書かれている。
 
青緑クラスターは、2008年から出演しているが、直近ではかなり白が目立つクラスターとなった。ただし、2016年直近は出演情報がデータベースにきちんと登録されていない可能性があるため、「本当にチョイ役だけど載っていない」とかだと空白になってしまう。
 
クラスターは、2010年から2011年ころから出演しており、直近では黒色が濃い(声優力が高い)クラスターとなった。ここでは内田真礼徳井青空佐倉綾音茅野愛衣など、若手超実力派といっても過言ではない声優が属した。茅野愛衣がここにくるのは予想外だったが、思い返してみると茅野愛衣が自分の中で爆発したのは2011年のあの花のめんま役だったので、意外とまだ若手〜中堅といってもいいのだろう。ただしオレの中での存在感はやばい。
 
クラスターは、2008年から継続的に出演しているが、2012年以前は色が濃く、2012年以降は色が薄いもしくは白抜けが目立つ感じのクラスターとなった。佐藤聡美はここに属した。思い返せば2009年のけいおん!からいるので、かなり古参である。

 
実際に時系列でプロットしてみるとほとんどが0.5 周辺でうろうろしているだけのような気がして、本当に声優力が[0,1]でいい感じに推定されているのかはちょっとよくわからない。ベータ分布は無情報事前分布だと平均が0.5 から始まるので0.5 周辺から抜け出さないのか、そもそもモデルがアレなのか…
グラフ内の線の色はキャラのイメージカラー、凡例内の声優の色はクラスター色と対応している。

 
一応収束したモデル

# model01.stan
data{
  int<lower=0> n_time; # No. of anime kr
  int<lower=0> n_kr[n_time]; # No. of cast in kr
  int<lower=0> n_cast; # length of casted
  real<lower=0, upper=1> p[n_cast];
}

parameters{
  real<lower=0> alpha[n_time]; # poisson paremeter lambda
  real<lower=0> beta[n_time];  # negative binomial div
  real<lower=0> s_a; # diff model normal sigma
  real<lower=0> s_b; # diff model normal sigma
}

model{
  for(i in 3:n_time){
    alpha[i] ~ lognormal(2*alpha[i-1]-alpha[i-2], s_a);
    beta[i] ~ lognormal(2*beta[i-1]-beta[i-2], s_b);
  }
  for(i in 1:n_time){
    if (n_kr[i] > 0) {
      for(j in 1:n_kr[i]){
        p[sum(n_kr[1:(i-1)])+j] ~ beta(alpha[i], beta[i]);
      }
    }
  }
}
# Python での前処理
import os
import re
import progressbar
wd = "/shoboi/" # しょぼいカレンダーのhtml がいっぱい入っているディレクトリ

files = os.listdir(wd)
cast = '<div class="title">キャスト</div>'
cvre = re.compile('="nofollow">.{1,}?</a>')
widgets = ["progress:", Percentage(), Bar()]
pbar = ProgressBar(maxval=len(files), widgets=widgets).start()
table = '</table>'
w0 = open("cast.txt", "w")
w0.write("\t".join(["title", "start", "cv"]) + "\n")
res = []
for f in range(len(files)):
  #pbar.update(pbar.currval + 1)
  g = open(wd + files[f], "rU")
  res0 = []
  res1 = "NA"
  cvlist =[]
  flag = 0
  for tmp in g:
    if cast in tmp:
      flag = 1
    if table in tmp:
      flag = 2
    if 'しょぼいカレンダー</title>' in tmp:
      res0 += [ re.compile(".+</title>").findall(tmp)[0][9:-38] ]
    if '<tr><th>放送期間</th><td>' in tmp:
      day = re.compile("\d{4}-\d{1,}").findall(tmp)
      if len(day) > 0:
        res1 = day[0]
      else:
        res1 = "NA"
    if flag == 1:
      r = cvre.findall(tmp)
      if len(r) > 0:
        cvlist += [ r[0][12:-4].rstrip() ]
    if flag == 2:
      pass
  res = [ " ".join(res0), res1, ",".join(cvlist) ]
  w0.write("\t".join(res) + "\n")
  print f

w0.close()
dat <- read.delim("cast.txt", stringsAsFactors=FALSE)
cv <- mapply(strsplit, dat$cv, ",")
names(cv) <- NULL
cvs <- unique(unlist(cv))
as.Date(paste(dat$start, "01", sep="-"))

d <- as.Date(paste0(dat$start, "-01"))
kr <- seq.Date(min(d, na.rm=TRUE), max(d, na.rm=TRUE), by="quarter") # anime kr

res <- vector("list", length(cvs))
names(res) <- cvs
pb <- txtProgressBar(min=1, max=length(cvs), style=3)
for(i in seq(cvs)){
  setTxtProgressBar(pb, i)
  j <- which(mapply(function(z) cvs[i] %in% z, cv)) # 声優がアニメに含まれるかどうか
  rank0 <- mapply(function(z) match(cvs[i], z), cv[j])
  all0  <- sapply(cv[j], length)
  top0  <- all0 - rank0 + 1
  mat <- cbind(rank=rank0, all=all0, top=top0)
  y <- split(as.data.frame(mat), cut(d, kr)[j])
  res[[i]] <- y
}

hoge <- mapply(function(z) sum(sapply(z, nrow)), res)
res0 <- res[ hoge > 4 ]
fidx <- read.csv("sex.csv", stringsAsFactors=FALSE)$sex

res0 <- res0[fidx == "女性" & !is.na(fidx)]
n_kr <- mapply(function(z) sapply(z, nrow), res0) # 各クールでの単純な出演数
data.frame(name=names(res0), sex=prof$sex[match(names(res0), prof$name)])

bar <- do.call(rbind, res0[[i]])
y <- sapply(res0[[i]], nrow)
kr_idx <- rep(match(names(y)[y>0], as.character(kr)), y[y>0])

n_kr <- sapply(res0[[i]], nrow)
kr0 <- head(tail(kr, length(kr)-rle(n_kr>0)$length[1]), -1)
n_kr0 <- n_kr[as.character(kr0)]

# run rstan
rstan_options(auto_write = TRUE)
options(mc.cores = parallel::detectCores())

model <- stan_model("model01.stan")
stan_data <- list(n_time=length(n_kr0), n_kr=n_kr0, n_cast=nrow(bar), N=bar$top)

fit <- sampling(model, data=stan_data, chains=3, warmup=30, iter=30, seed=1234)

bm0 <- ex$alpha/(ex$alpha + ex$beta)
bm <- apply(bm0, 2, median)
mv <- tapply(stan_data$p, rep(seq(stan_data$n_kr), stan_data$n_kr), median)
mv <- replace(rep(0, length(n_kr0)), as.numeric(names(mv)), mv)

alpha <- 0.05
cols <- c("black", "blue")
ci <- apply(bm0, 2, quantile, c(alpha/2, 1-alpha/2))
xy <- cbind(bm, mv)
par(mar=c(4, 4.5, 2, 2), cex.lab=1.5, cex.axis=1.5)
matplot(xy, type="n", ylim=c(0, 1), xlab="", ylab="Parameter", las=1, lty=c(1,3), xaxt="n")
polygon(c(1:ncol(ci), ncol(ci):1), c(ci[1,], rev(ci[2,])), col=grey(0.9), border=NA)
for(k in 1:ncol(xy)) lines(1:nrow(xy), xy[,k], type="o", pch=15, lwd=3, col=cols[k], lty=c(1,3)[k])

ymd <- do.call(rbind, strsplit(names(n_kr0), "-"))
colnames(ymd) <- c("y", "m", "d")
ymd <- as.data.frame(ymd, stringsAsFactors=TRUE)
season <- c("冬", "春", "夏", "秋")
xd <- 0.0
yd <- 0.03
for(i in seq(levels(ymd$y))){
  tmp <- which(c(ymd$y) == i)
  x0 <- tmp[1]
  x1 <- tail(tmp, 1)
  xs <- seq(length(n_kr0))
  segments(xs[x0]+xd, par()$usr[3]-yd, xs[x1]-xd, lwd=2, xpd=TRUE)
  text(mean(xs[tmp]), par()$usr[3]-yd, levels(ymd$y)[i], xpd=TRUE, pos=1, cex=1.2)
  text(xs[tmp], par()$usr[3]-yd*3.2, season[c(ymd$m)[tmp]], xpd=TRUE, pos=1, cex=1.2)
}
abline(v=cumsum(head(rle(c(ymd$y))$lengths, -1))+0.5, lty=3)
legend("bottomright", legend=c("Estimate", "Observation"), lty=c(1,3), lwd=3, col=cols, bg="white", cex=1.5)


# ごちうさ解析
gochiusa <- c("佐倉綾音", "水瀬いのり", "種田梨沙", "佐藤聡美", "内田真礼", "茅野愛衣", "村川梨衣", "徳井青空", "早見沙織")
gochiusa_col <- c("plum1", "lightskyblue", "blueviolet", "aquamarine3", "gold2", "orange", "red1", "blue1", grey(0.3))

# 人気声優 advent 用
h1 <- cut(h$rowDendrogram, h=6000)$lower[[1]]
h1 <- color_branches(h1, k=6)
h1 <- set(h1, "branches_lwd", 5)
# 木の色を強引に取り出す
h1attr <- rapply(h1, attributes)
clust_col <- h1attr[names(h1attr) == "edgePar.col"]
names(clust_col) <- h1attr[grep("label", names(h1attr))]

idx <- colnames(m0)[unlist(h1)]
layout(matrix(1:2, nc=2), widths=c(2, 10))
par(mar=c(0.1, 0.5, 0, 0.3))
plot(h1, horiz=TRUE, leaflab="none", yaxt="n", xaxs="i")
par(mar=c(5, 0.1, 4.5, 6))
image(seq(nrow(h$carpet)), seq(ncol(h$carpet[, idx])), h$carpet[,idx], col=cols, xaxt="n", yaxt="n", xlab="", ylab="")
cvy <- c(-0.6, -2, 2, 0.6, 0, 0)*10
tip.color <- replace(rep("black", length(idx)), match(gochiusa, idx), "blue")
text(par()$usr[2], seq(idx), idx, xpd=TRUE, pos=4, col=tip.color)
abline(v=which(rownames(h$carpet) == "2012-07-01")-0.5, lty=3, lwd=3, col="yellow")

ymd <- do.call(rbind, strsplit(rownames(m0), "-"))
colnames(ymd) <- c("y", "m", "d")
ymd <- as.data.frame(ymd, stringsAsFactors=TRUE)
season <- c("冬", "春", "夏", "秋")
xd <- 0.0
yd <- 2
for(i in seq(levels(ymd$y))){
  tmp <- which(c(ymd$y) == i)
  x0 <- tmp[1]
  x1 <- tail(tmp, 1)
  xs <- seq(nrow(h$carpet))
  segments(xs[x0]+xd, par()$usr[3]-yd, xs[x1]-xd, lwd=2, xpd=TRUE)
  text(mean(xs[tmp]), par()$usr[3]-yd, levels(ymd$y)[i], xpd=TRUE, pos=1)
  text(xs[tmp], par()$usr[3]-yd*0, season[c(ymd$m)[tmp]], xpd=TRUE, pos=1)
}

# ごちうさ声優の声優力
G <- m0[, gochiusa]
G[G == 0] <- NA
par(mar=c(4, 4.5, 2, 2), cex.lab=1.5, cex.axis=1.5)
matplot(G, type="o", ylim=c(0, 1), xlab="", ylab="Parameter", col=gochiusa_col, las=1, lty=1, lwd=3, pch=15, xaxt="n")

ymd <- do.call(rbind, strsplit(names(n_kr0), "-"))
colnames(ymd) <- c("y", "m", "d")
ymd <- as.data.frame(ymd, stringsAsFactors=TRUE)
season <- c("冬", "春", "夏", "秋")
xd <- 0.0
yd <- 0.03
for(i in seq(levels(ymd$y))){
  tmp <- which(c(ymd$y) == i)
  x0 <- tmp[1]
  x1 <- tail(tmp, 1)
  xs <- seq(length(n_kr0))
  segments(xs[x0]+xd, par()$usr[3]-yd, xs[x1]-xd, lwd=2, xpd=TRUE)
  text(mean(xs[tmp]), par()$usr[3]-yd, levels(ymd$y)[i], xpd=TRUE, pos=1, cex=1.2)
  text(xs[tmp], par()$usr[3]-yd*2.2, season[c(ymd$m)[tmp]], xpd=TRUE, pos=1, cex=1.2)
}
abline(v=cumsum(head(rle(c(ymd$y))$lengths, -1))+0.5, lty=3)
legend("bottomright", legend=gochiusa, col=gochiusa_col, text.col=clust_col[gochiusa], lwd=3, lty=1, pch=15, merge=TRUE, cex=1.2, bg="white")

Biclustering

読んだ。
Bioinformatics. 2016 Oct 6.
Nucleic Acids Res. 2009 Aug;37(15):e101.
Biclustering をするQUBIC という手法をR で実装しました。クッソ速いです、とのこと。
 
そもそもbiclustering とはなにかというと、ヒートマップクラスタリングをするときに行もしくは列でクラスタリングをするが、行と列の一部を使ったsubcluster というものができて、それが生物学的に意味があるのではないかという感じのやり方。
ベイズ的にbiclustering をするbaybi パッケージもあるらしい。
cran にないっぽいのでこちら を参考に

install.packages("baybi", repos="http://R-Forge.R-project.org")

3次元で上からクラスタリングを描いている上のリンクがどうやったらできるのかはよくわからない。


急性リンパ芽球性白血病ですごいきれいなbiclustering になっている論文(読めてない) Blood 2010 115:1214-1225
総説 っぽいもの。

StanとRでベイズ統計モデリング

読んだ。

COI:謹 呈。激甘書評。
 
rstan の神が丹精込めて書きあげた、至高の一冊。
「StanとRでベイズ統計モデリング」松浦健太郎 という本を書きました - StatModeling Memorandum
rstan、統計モデリングをするものでこれを読んでない人は本当読んだほうがよい。
 
かつて、岩波DS vol.1 をいただいてからというものの、rstan をがんばって声優統計を書いてみたりしたが、この本は筆者の経験による、rstan を使ったり統計モデリングをしたりする際の、細かな注意点と実際のやり方があますとこなく説明されている。
その意味では、数式的に証明がどうとか、理論的にどうとか、そういう観点からの説明は少ない。そういう点は別の本で補強すればよいが、数式でゴリゴリ書いてあるのはいいとして、では実際にどうすればいいの? という点に答えている統計書は少ない(と思う)。その点では統計モデリングを実際にこう考えながらやっている、という本書は、実務のうえではよい。また、筆者は生物化学系の実験データが出るようなところでデータ分析に携わっていたようなので、(生物系の)現場から出るデータを実際に解析する苦労とか、理想と現実の妥協点の探り方だとか、そういう考え方が非常に生物医学系のデータを解析する人と親和性が高いと思う。
実務のうえではよいが、近年はR やPython などで勝手に統計、機械学習が実装されているので、「とりあえずコピペしたらこうなった」というコピペマティシャンが続出する恐れがあるのは、唯一の本書の欠点かもしれない。
 
本書の内容としては、統計モデリングでの長所、例えば自分が感心したのは、打ち切りデータの解析のところで、<25 というデータを得たときにどうするか。観測値が小さいことを言いたいのであれば、25 にそろえてしまってきびしめに検定することが古典的なやり方では可能だが、統計モデリングではこれをlp__ でモデル化できるだとか、外れ値としての様子が濃厚なデータを外れ値が許容されうる分布、例えばコーシーやt分布でモデル化するところが非常によかった。
回帰分析の悩みどころでは、rstan に限らず一般的な統計解析で悩む点が赤裸々に述べられており、そう思う。
統計モデリングの視点から確率分布の紹介では、実際に統計モデリングで考えらえれる分布の採用思考過程が書かれており、初学者にわかりやすい。収束しない場合の対処法は、rstan の経験が豊富な筆者の知恵が詰まっている。
 
統計には少し詳しい素人()なので、「これ書いてあることの8割くらいわかるわ」と思いながらも2割くらいの新しい発見があったのでサイコーである。
個人的にちょっと思ったことを言うと、rstan での練習をかねて単/重回帰などをrstan でやっているのだが、今後統計モデリングが流行ってこういうこともrstan などで解析されるようになるかというとオーバーキルな感じもするし、古典的仮説検定がまともに(少なくとも自分の属する生物医学界隈で)理解されているかというとそうでもないのにベイズ的なアレが広まるかというとそうでもないので、お勉強はしておいて来るときに備えて牙を研いでおこうぜ的なアレ。
 
謹呈されてなくても100冊くらいは買ってた(100冊はさすがにウソだけど