次のような問題でした:
$x = (1,2,3,4,5,6)$ のとき $y = (\mathrm{ND},\mathrm{ND},3,5,4,6)$ になった。ただしNDは測定値が2未満を意味する。このデータを1次式 $y \sim ax + b$ でフィットしたい。
これにさらに真値が負でない($ax_i + b \geq 0$)という制約を付けて解いています。本書のRコードでこの問題に関する部分は
xdata = c(1, 2, 3, 4, 5, 6) # データ ydata = c(NA, NA, 3, 5, 4, 6) # データ iy = is.na(ydata) # NA jy = !iy # NA以外 ny = sum(jy) # NA以外の個数 logpost = function(a, b, t) { # 事後分布の対数 if (any(a*xdata+b < 0)) return(-Inf) sum(pnorm(2, a*xdata[iy]+b, exp(t/2), log.p=TRUE)) - 0.5 * (ny*t + sum((a*xdata[jy]+b-ydata[jy])^2)/exp(t)) }
です。事前分布は $a$,$b$,$t = \log \sigma^2$ について一様としています。このあとに通常のメトロポリスのアルゴリズムが続きます。
つまり,NDでなければ通常の正規分布ですが,ND($< 2$)であれば正規分布の密度関数を $-\infty$ から $2$ まで積分した(累積)分布関数 cumulative distribution function が尤度になります。
Stanには正規分布の(累積)分布関数の対数 $\mathrm{normal\_lcdf}(x | \mu, \sigma)$ および 1 から(累積)分布関数を引いた値の対数 $\mathrm{normal\_lccdf}(x | \mu, \sigma)$ が備わっていますので,これを使えば簡単です:
// ex77.stan data { int n; real x[n]; real y[n]; } parameters { real a; real b; real logsigma; } model { for (i in 1:n) { if (a * x[i] + b < 0) { target += negative_infinity(); } else if (y[i] >= 2) { y[i] ~ normal(a*x[i]+b, exp(logsigma)); } else { target += normal_lcdf(2 | a*x[i]+b, exp(logsigma)); } } }
Rコードは次のようになります。StanにはRの NA
のようなものがないので 0 で代用しています。
xdata = c(1, 2, 3, 4, 5, 6) # データ ydata = c(NA, NA, 3, 5, 4, 6) # データ data = list(n=length(xdata), x=xdata, y=ifelse(is.na(ydata), 0, ydata)) fit = stan("ex77.stan", data=data, iter=26000, warmup=1000)
図7.9を描くには次のようにします。
e = extract(fit) plot(xdata, ydata, ylim=c(0,6), pch=16, xlab="x", ylab="y") abline(mean(e$b), mean(e$a)) points(1:2, c(2,2), pch=1) arrows(1,2, 1,0, length=0.1, angle=20) arrows(2,2, 2,0, length=0.1, angle=20) abline(lm(ydata ~ xdata), lty=2)