日付単位とかでデータを取ることが多いこのご時世、等間隔の状態空間モデルを使うことが多いと思います。しかし、ふと不等間隔の状態空間モデルってどうやるんだろーとつぶやいたところ、ご指導いただきました。いつも大変感謝です。
.@berobero11 細かく等間隔に切って欠測扱いにするのが基本.欠測で速度のおちないブロックサンプラーが有用になる.非線形常微分方程式でデータのない部分を「解いてしまう」方法は逐次モンテカルロ限定かな? ほとんど観測がないならカーネル回帰に直す方法もありますが端が近似になる
— baibai (@ibaibabaibai) 2015, 2月 19
.@berobero11 間違ってもカルマンフィルタで補間してから,別の状態空間モデルをカルマンフィルタであてはめたりしないように.
— baibai (@ibaibabaibai) 2015, 2月 19
@berobero11 まずはざっくりと.それで計算時間が無駄だなあ,と思ったところで他の手法(伊庭も全ては知らない)のチェックをしたらどうでしょう.
— baibai (@ibaibabaibai) 2015, 2月 19
@berobero11 種々のツールで欠測をキレイにコードすつ方法も考えられると有用ですね.
— baibai (@ibaibabaibai) 2015, 2月 19
.@berobero11 空間(とくに3次元)だとスプライン関数なんかを利用する方法もあるようですが,ノードを動かすと面倒なんで,カーネル回帰にしたほうが筋が良いような気も.
— baibai (@ibaibabaibai) 2015, 2月 19
.@berobero11 せっかくなので2次階差に対応するカーネルは何か,とか見ておくといいですね. http://t.co/zL144ARsTu 3ページから4ページ dlmやcarの結果と比較してみたら 無限系に対するカーネルだから端ではちょっとずれるはず
— baibai (@ibaibabaibai) 2015, 2月 19
.@berobero11 分散パラメータをCV(GCV?)で決めるのとベイズの比較も面白いです.これはdlmやcarでも可能.面白いことに,結果の「裾」がCV系のほうが長い,つまり多数回載せるノイズを変えて模擬データでやるとCVのほうがやや不安定という指摘が昔ありました.
— baibai (@ibaibabaibai) 2015, 2月 19
.@berobero11 前にも書いたが,これとかも見ておくと視野がひろがるかも http://t.co/bmxEtbXkI6 機械学習寄りのWilliams本http://t.co/wGeUBIhQtY とは相補的
— baibai (@ibaibabaibai) 2015, 2月 19
1階階差のモデルのカーネルはラプラス方程式のグリーン関数,2階階差だけのモデルのカーネルは重調和方程式のグリーン関数になる http://t.co/yvgY6mI43b 場の理論でいうプロパゲータ.
— baibai (@ibaibabaibai) 2015, 2月 19
1階や2階の階差のモデルはなぜ高次元では使えないのか.それは無限系のカーネル(グリーン関数)を計算してみるとわかるが,意外にみんな知らないらし い.フーリエ解析の簡単な応用.こういう基本的なことがぱっとわかるのが本当の数理だと思うんだけど,みんな用語や呪文覚えるが好きだからなあ.
— baibai (@ibaibabaibai) 2015, 2月 19
今回は不等間隔の時系列データに対し、ざっくり小数点第1位で等間隔のメッシュを作って、データのない所は欠損値として扱う、通常の状態空間モデルに落とす方法でやってみました。Stanコードは以下になります。
data { int<lower=1> N; int<lower=1> N_obs; int<lower=1, upper=N> Idx_obs[N_obs]; real Y_obs[N_obs]; } parameters { real r[N]; real<lower=0> s_r; real<lower=0> s_y; } model { for (n in 3:N) r[n] ~ normal(2*r[n-1] - r[n-2], s_r); for(i in 1:N_obs) Y_obs[i] ~ normal(r[Idx_obs[i]], s_y); }
- 9行目:背後のrはすべてのメッシュで存在します。
- 16行目:今回は2階差分のCAR modelを使っています。
- 3~5行目:あとのRコードの方で詳しく見ますが、観測したところのデータとメッシュのインデックスを渡します。
- 17~18行目:観測したデータのところの尤度を計算しています。
キックするRコードは以下になります。
library(rstan) d <- read.delim("input/GPbook-Fig2_05.data.txt", sep="\t") X_obs <- d$X X_mesh <- -7.0 + 0:140 * 0.1 N <- length(X_mesh) N_obs <- nrow(d) X_mod <- round(X_obs, 1) Idx_obs <- as.integer(round((X_mod + 7.0)/0.1)) Y_obs <- d$Y data <- list(N=N, N_obs=N_obs, Idx_obs=Idx_obs, Y_obs=Y_obs) stan_fit <- stan(file="model/model.stan", chains=0) fit <- stan(fit=stan_fit, data=data, iter=1000, warmup=400, thin=1, seed=123, chains=3)
- 5,8,9行目:ここで
X_obs
を小数点第1位のデータX_mod
に落として、そのインデックス(X_mod
がメッシュで左から何番目の位置か)を計算しています。なお計算時間はSurface Pro 3(core i5)で2次のCAR modelが1chainあたり20秒ぐらいでした。意外と早い。
結果は以下になります。想定よりうまくいっているのではないかと思います。
白抜き点はデータ、青線はrのMCMCサンプルの中央値、薄い灰帯は同じく90%信用区間、濃い灰帯は同じく50%信用区間です。
こちらは、灰帯だけ変えて、1800個のMCMCサンプルを全てプロットしたものです。
最後に上の図を描いたRコードを載せておきます。
library(rstan) library(ggplot2) library(reshape2) ...(Stanで計算)... d_obs <- data.frame(X=X_obs, Y=Y_obs) la <- extract(fit) N_mcmc <- length(la$lp__) qua <- apply(la$r, 2, quantile, prob=c(0.05, 0.25, 0.5, 0.75, 0.95)) d_qua <- data.frame(X=X_mesh, t(qua)) colnames(d_qua) <- c('X', 'p5', 'p25', 'p50', 'p75', 'p95') p <- ggplot() p <- p + geom_ribbon(data=d_qua, aes(x=X, y=p50, ymax=p95, ymin=p5), alpha=1/4) p <- p + geom_ribbon(data=d_qua, aes(x=X, y=p50, ymax=p75, ymin=p25), alpha=2/4) p <- p + geom_line(data=d_qua, aes(x=X, y=p50), color='blue') p <- p + geom_point(data=d_obs, aes(x=X, y=Y), shape=as.factor(1), size=2) p <- p + coord_cartesian(ylim=c(-3.5, 3.5)) p <- p + xlab('X') + ylab('Y') p <- p + theme(legend.position='none') ggsave(file='output/r_est_quantile.png', plot=p, dpi=300, width=4, height=3) d_mcmc <- data.frame(mcmc_sample=1:N_mcmc, la$r) colnames(d_mcmc) <- c('mcmc_sample', X_mesh) d_melt <- melt(d_mcmc, id='mcmc_sample', variable.name='X', value.name='Y') d_melt$X <- as.numeric(as.character(d_melt$X)) p <- ggplot() p <- p + geom_line(data=d_melt, aes(x=X, y=Y, group=mcmc_sample), color='darkorange3', alpha=1/100) p <- p + geom_line(data=d_qua, aes(x=X, y=p50), color='blue') p <- p + geom_point(data=d_obs, aes(x=X, y=Y), shape=as.factor(1), size=2) p <- p + coord_cartesian(ylim=c(-3.5, 3.5)) p <- p + xlab('X') + ylab('Y') p <- p + theme(legend.position='none') ggsave(file='output/r_est_MCMC.png', plot=p, dpi=300, width=4 , height=3)