library(nimble)
## nimble version 1.1.0 is loaded.
## For more information on NIMBLE and a User Manual,
## please visit https://R-nimble.org.
##
## Note for advanced users who have written their own MCMC samplers:
## As of version 0.13.0, NIMBLE's protocol for handling posterior
## predictive nodes has changed in a way that could affect user-defined
## samplers in some situations. Please see Section 15.5.1 of the User Manual.
##
## 次のパッケージを付け加えます: 'nimble'
## 以下のオブジェクトは 'package:stats' からマスクされています:
##
## simulate
## 以下のオブジェクトは 'package:base' からマスクされています:
##
## declare
library(readr)
library(dplyr)
##
## 次のパッケージを付け加えます: 'dplyr'
## 以下のオブジェクトは 'package:stats' からマスクされています:
##
## filter, lag
## 以下のオブジェクトは 'package:base' からマスクされています:
##
## intersect, setdiff, setequal, union
library(tidyr)
library(stringr)
library(ggplot2)
library(posterior)
## This is posterior version 1.5.0
##
## 次のパッケージを付け加えます: 'posterior'
## 以下のオブジェクトは 'package:stats' からマスクされています:
##
## mad, sd, var
## 以下のオブジェクトは 'package:base' からマスクされています:
##
## %in%, match
library(bayesplot)
## This is bayesplot version 1.11.1
## - Online documentation and vignettes at mc-stan.org/bayesplot
## - bayesplot theme set to bayesplot::theme_default()
## * Does _not_ affect other ggplot2 plots
## * See ?bayesplot_theme_set for details on theme setting
##
## 次のパッケージを付け加えます: 'bayesplot'
## 以下のオブジェクトは 'package:posterior' からマスクされています:
##
## rhat
set.seed(123)
前の記事にひきつづき、松浦健太郎さんの“Bayesian Statistical Modeling with Stan, R, and Python”の11.6節にある、多変量正規分布の状態空間モデルをNIMBLEに移植してみました。
準備
ライブラリの読み込みと乱数の設定です。
データ
データを読み込んで整形します。サンプルコードと同様に、途中のデータを一部欠測とします。
<- "https://raw.githubusercontent.com/MatsuuraKentaro/Bayesian_Statistical_Modeling_with_Stan_R_and_Python/master/chap11/input/data-eg2.csv"
data_url <- read_csv(data_url)
d ## Rows: 60 Columns: 3
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (3): Time, Weight, Bodyfat
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
<- nrow(d)
T <- c(1:20, 31:40, 51:60)
t_obs2t <- length(t_obs2t)
T_obs <- 21:30
t_bf2t <- length(t_bf2t)
T_bf <- d[t_obs2t, c("Weight", "Bodyfat")] / 10
Y_obs <- d$Bodyfat[t_bf2t] / 10 Y_bf
モデル
NIMBLEのモデルです。NIMBLEでは独自の拡張として、Stanのモデルと同様に、LKJ相関分布のコレスキー因子を多変量正規分布のパラメータとして与えることができます。
相関行列と標準偏差ベクトルの掛け算をするuppertri_mult_diag
関数は、マニュアルにあるものをそのままつかっています。
<- nimbleFunction(
uppertri_mult_diag run = function(mat = double(2), vec = double(1)) {
returnType(double(2))
<- length(vec)
p <- matrix(nrow = p, ncol = p, init = FALSE)
out for(i in 1:p)
<- mat[ , i] * vec[i]
out[ , i] return(out)
})
<- nimbleCode({
code # observation model
for (t in 1:T_obs) {
1:2] <- mu[t_obs2t[t], 1:2]
mu_obs[t, 1:2] ~ dmnorm(mu_obs[t, 1:2],
Y_obs[t, cholesky = cov_chol_y[1:2, 1:2],
prec_param = 0)
}for (t in 1:T_bf) {
~ dnorm(mu[t_bf2t[t], 2], sd = s_y_vec[2])
Y_bf[t]
}
# system model
for (t in 2:T) {
1:2] ~ dmnorm(mu[t - 1, 1:2],
mu[t, cholesky = cov_chol_mu[1:2, 1:2],
prec_param = 0)
}1, 1] ~ dnorm(0, sd = 10)
mu[1, 2] ~ dnorm(0, sd = 10)
mu[
# priors
1:2, 1:2] ~ dlkj_corr_cholesky(Nu, 2)
corr_chol_mu[1:2, 1:2] ~ dlkj_corr_cholesky(Nu, 2)
corr_chol_y[1:2, 1:2] <- uppertri_mult_diag(corr_chol_mu[1:2, 1:2],
cov_chol_mu[1:2])
s_mu_vec[1:2, 1:2] <- uppertri_mult_diag(corr_chol_y[1:2, 1:2],
cov_chol_y[1:2])
s_y_vec[for (i in 1:2) {
~ dt(0, sigma = 0.05, df = 6)
s_mu_vec[i] ~ dt(0, sigma = 0.1, df = 6)
s_y_vec[i]
} })
実行
実行します。その前に、パラメータの初期値を生成するinits
関数を定義しています。
繰り返し回数16万回で、そのうちの前半分の8万回をバーンインとしています。さらに20回ごとの間引きをつけていますが、これでも収束はなかなか難しいです。後でも見ますが、とくに標準偏差パラメータの収束が悪く、乱数によっては、大きな値で安定してしまう連鎖ができたりしてしまいます。
<- function() {
inits list(mu = matrix(runif(T * 2, -2, 2), T, 2),
s_mu_vec = runif(2, 0, 1),
s_y_vec = runif(2, 0, 1),
corr_chol_mu = matrix(runif(4, 0, 1), 2, 2),
corr_chol_y = matrix(runif(4, 0, 1), 2, 2))
}
<- nimbleMCMC(code,
samp constants = list(T = T, T_obs = T_obs, T_bf = T_bf,
t_obs2t = t_obs2t, t_bf2t = t_bf2t),
data = list(Y_obs = Y_obs, Y_bf = Y_bf, Nu = 2),
inits = inits, setSeed = 11:13,
monitors = c("mu", "s_mu_vec", "s_y_vec"),
nchains = 3,
niter = 160000, nburnin = 80000, thin = 20,
samplesAsCodaMCMC = TRUE) |>
as_draws()
## Defining model
## Building model
## Setting data and initial values
## Running calculate on model
## [Note] Any error reports that follow may simply reflect missing values in model variables.
## Checking model sizes and dimensions
## Checking model calculations
## Compiling
## [Note] This may take a minute.
## [Note] Use 'showCompilerOutput = TRUE' to see C++ compilation details.
## Warning: Unknown or uninitialised column: `new`.
## Unknown or uninitialised column: `new`.
## Unknown or uninitialised column: `new`.
## running chain 1...
## |-------------|-------------|-------------|-------------|
## |-------------------------------------------------------|
## running chain 2...
## |-------------|-------------|-------------|-------------|
## |-------------------------------------------------------|
## running chain 3...
## |-------------|-------------|-------------|-------------|
## |-------------------------------------------------------|
結果
R-hatを確認します。大きい順に並べ替えて表示します。
|>
samp summarise_draws() |>
::arrange(desc(rhat)) |>
dplyr::select(variable, rhat)
dplyr## # A tibble: 124 × 2
## variable rhat
## <chr> <dbl>
## 1 s_mu_vec[1] 1.25
## 2 s_y_vec[1] 1.07
## 3 s_mu_vec[2] 1.01
## 4 mu[35, 1] 1.01
## 5 mu[21, 2] 1.01
## 6 s_y_vec[2] 1.01
## 7 mu[27, 1] 1.01
## 8 mu[9, 1] 1.01
## 9 mu[26, 1] 1.01
## 10 mu[17, 1] 1.01
## # ℹ 114 more rows
s_mu_vec[1]
のR-hat値が1.1を超えてしまっています。
標準偏差パラメータのs_mu_vec
とs_y_vec
の軌跡を確認してみます。
mcmc_trace(samp, pars = c("s_mu_vec[1]", "s_mu_vec[2]",
"s_y_vec[1]", "s_y_vec[2]"))
やはりs_mu_vec[1]
の混ざり方がいまひとつです。Stanの結果と比較しても大きな値となっています。
グラフ表示
収束はいまひとつでしたが、状態mu
の推定値をみてみます。
<- samp |>
summ summarise_draws(~quantile(.x, probs = c(0.025, 0.25, 0.5,
0.75, 0.975)))
<- c("Weight", "Bodyfat")
levels <- summ |>
mu ::filter(str_detect(variable, "^mu")) |>
dplyr::mutate_at(-1, ~ .x * 10) |>
dplyr::bind_cols(data.frame(Time = c(1:T, 1:T),
dplyrItem = rep(levels, each = T))) |>
::mutate(Item = factor(Item, levels = levels))
dplyr
ggplot(mu, aes(x = Time)) +
geom_ribbon(aes(ymin = `2.5%`, ymax = `97.5%`),
fill = "gray80") +
geom_ribbon(aes(ymin = `25%`, ymax = `75%`),
fill = "gray50") +
geom_line(aes(y = `50%`)) +
labs(x = "Time (Day)", y = "Y") +
facet_wrap(~Item, nrow = 2, scales = "free") +
theme_bw()
mu
の推定値はだいたいStanの結果と同様となっていました。