粒子フィルタ(パーティクルフィルタ、モンテカルロフィルタ)による状態の推定です。
以降の数式や導出は全てこちらの論文からから引用しています。
- Genshiro Kitagawa(1998),A Self-Organizing State-Space Model,Journal of the American Statistical Association, Vol. 93, No. 443 (Sep., 1998), pp. 1203-1215 (以降、Kitagawa(1998))
また、Rコードはこちらの資料を参照引用しています。
始めに Kitagawa(1998), p.1206 のサンプルデータ
ここで、
library(tidyr)
library(ggplot2)
<- 20240610
seed set.seed(seed)
<- rep(0, 400)
t 101:200] <- 1.5
t[201:300] <- -1
t[<- length(t)
T <- rnorm(T, mean = t, sd = 1)
y <- data.frame(T = seq(T), t, y) %>% gather(key = "key", value = "value", colnames(.)[-1])
tidydf ggplot() +
geom_line(data = tidydf, aes(x = T, y = value, col = key, linewidth = key)) +
theme_minimal() +
scale_linewidth_manual(values = c(1.5, 0.6)) +
theme(legend.title = element_blank())
参考として LOESS近似曲線 を確認します。1つ目はスパンをデフォルトの 0.75 とします。
<- scales::hue_pal()(2)
linecol <- data.frame(T = seq(T), y) %>% gather(key = "key", value = "value", colnames(.)[-1])
tidydf ggplot(data = tidydf, aes(x = T, y = value, col = key)) +
geom_line(linewidth = 0.6) +
theme_minimal() +
theme(legend.title = element_blank()) +
geom_smooth(method = "loess", col = linecol[1], span = 0.75) +
scale_color_manual(values = linecol[2]) +
labs(title = "LOESS(span = 0.75)")
`geom_smooth()` using formula = 'y ~ x'
loess(span = 0.25) とした場合は、
ggplot(data = tidydf, aes(x = T, y = value, col = key)) +
geom_line(linewidth = 0.6) +
theme_minimal() +
theme(legend.title = element_blank()) +
geom_smooth(method = "loess", col = linecol[1], span = 0.25) +
scale_color_manual(values = linecol[2]) +
labs(title = "LOESS(span = 0.25)")
`geom_smooth()` using formula = 'y ~ x'
「システムノイズにコーシー分布を用いると,状態の時間変化について前時刻とほぼ同じであるが稀にジャンプが 生じる状況を表すことになる」
出典: https://www.cirje.e.u-tokyo.ac.jp/research/reports/R-15-3.pdf#page=322
状態とパラメータの同時推定のため、2次の状態ベクトルを考えます。
分散パラメータ
よって、状態空間モデル
- 状態
- 観測
初期状態
なお、粒子の数は Kitagawa(1998), p.1206 では201個とされていますが 北川(2019) では1万個とされていますので本ページでは1万個とします。
<- 10000 m
それでは、初期状態を作成します。なお以降
set.seed(seed)
<- rnorm(m, mean = 0, sd = 4)
xf <- runif(m, min = -8, max = -2)
log10tau2 <- runif(m, min = sd(y) * 0.5, max = sd(y) * 2) w
フィルタ分布
<- array(
pfresult data = NA,
dim = c(m, T, 3),
dimnames = list(
NULL,
NULL,
c("xf", "log10tau2", "w")
) )
以降、予測、尤度の算出、リサンプリング を繰り返します。
set.seed(seed)
for (ii in seq(T)) {
<- rcauchy(n = m, location = 0, scale = sqrt(10^log10tau2))
v <- xf + v
xp <- dnorm(y[ii] - xp, mean = 0, sd = w) %>%
alpha
{/ sum(.)
.
}<- sample(seq(m), prob = alpha, replace = T)
rs <- xp[rs]
xf <- log10tau2[rs]
log10tau2 <- w[rs]
w "xf"] <- xf
pfresult[, ii, "log10tau2"] <- log10tau2
pfresult[, ii, "w"] <- w
pfresult[, ii, }
最初は t(状態) と推定値 xf_mean を比較します。
<- apply(X = pfresult[, , "xf"], MARGIN = 2, FUN = function(x) mean(x))
xf_mean <- data.frame(T = seq(T), t, xf_mean) %>% gather(key = "key", value = "value", colnames(.)[-1])
tidydf ggplot() +
geom_line(data = tidydf, aes(x = T, y = value, col = key, linewidth = key)) +
theme_minimal() +
scale_linewidth_manual(values = c(1.5, 0.7)) +
theme(legend.title = element_blank())
続いて t(状態) と xf の 95%信用区間 を比較します。
<- apply(X = pfresult[, , "xf"], MARGIN = 2, FUN = function(x) quantile(x = x, probs = c(0.025, 0.975)))
xf_quantile <- data.frame(T = seq(T), t) %>% gather(key = "key", value = "value", colnames(.)[-1])
tidydf <- t(xf_quantile) %>% data.frame(T = seq(T), ., check.names = F)
tidydf_ribbon ggplot() +
geom_line(data = tidydf, aes(x = T, y = value, col = key, linewidth = key)) +
geom_ribbon(aes(x = tidydf_ribbon$T, ymin = tidydf_ribbon$`2.5%`, ymax = tidydf_ribbon$`97.5%`), alpha = 0.2) +
theme_minimal() +
scale_linewidth_manual(values = 1.5) +
theme(legend.title = element_blank())
同時に推定したパラメータ
<- apply(X = pfresult[, , "log10tau2"], MARGIN = 2, FUN = function(x) mean(x))
log10tau2_mean <- apply(X = pfresult[, , "log10tau2"], MARGIN = 2, FUN = function(x) quantile(x, probs = c(0.025, 0.975)))
log10tau2_quantile <- data.frame(T = seq(T), log10tau2_mean) %>% gather(key = "key", value = "value", colnames(.)[-1])
tidydf <- t(log10tau2_quantile) %>% data.frame(T = seq(T), ., check.names = F)
tidydf_ribbon ggplot() +
geom_line(data = tidydf, aes(x = T, y = value, col = key, linewidth = key)) +
theme_minimal() +
geom_ribbon(aes(x = tidydf_ribbon$T, ymin = tidydf_ribbon$`2.5%`, ymax = tidydf_ribbon$`97.5%`), alpha = 0.2) +
theme_minimal() +
scale_linewidth_manual(values = 1.5) +
theme(legend.title = element_blank())
同じく同時に推定したパラメータ
<- apply(X = pfresult[, , "w"], MARGIN = 2, FUN = function(x) mean(x))
w_mean <- apply(X = pfresult[, , "w"], MARGIN = 2, FUN = function(x) quantile(x, probs = c(0.025, 0.975)))
w_quantile <- data.frame(T = seq(T), w_mean) %>% gather(key = "key", value = "value", colnames(.)[-1])
tidydf <- t(w_quantile) %>% data.frame(T = seq(T), ., check.names = F)
tidydf_ribbon ggplot() +
geom_line(data = tidydf, aes(x = T, y = value, col = key, linewidth = key)) +
theme_minimal() +
geom_ribbon(aes(x = tidydf_ribbon$T, ymin = tidydf_ribbon$`2.5%`, ymax = tidydf_ribbon$`97.5%`), alpha = 0.2) +
theme_minimal() +
scale_linewidth_manual(values = 1.5) +
theme(legend.title = element_blank())
以上です。