動的市場反応モデル
こちらを参考にKFASで遷移式のaを求める場合のコード。
自分用のメモなのでオリジナリティは皆無。万が一他の方が辿り着いたら上記を参照ください。
まずは関数部分(変更なし)。
# ------ common part ----
require(ggplot2)
require(dplyr)
require(tidyr)
require(KFAS) # 1.2.9
# calculate the reference price
calc_RP <- function(RP, p, a){
for( i in 2:length(RP)){
RP[i] <- a * RP[i-1] + (1-a) * p[i-1]
}
return(RP)
}
# calculate Z
calc_Z <- function(RP, p){
z_1 <- 0
z_2 <- 0
v_wk <- p - RP
n <- length(RP)
for (i in 1:n){
if (v_wk[i] >= 0) {
z_1[i] <- v_wk[i] / 100
} else {
z_1[i] <- 0
}
if (v_wk[i] < 0) {
z_2[i] <- - v_wk[i] / 100
} else {
z_2[i] <- 0
}
}
return(list(z1 = z_1, z2 = z_2))
}
# read the data
#ID_W:週ID
#end:エンド陳列の有無
#logPI:点数PIの対数
#AP:売価
#RP095:参照価格(繰越パラメータ0.95:RP_t=0.95*RP_{t-1}+(1-0.95)*AP_{t-1})
df <- read.csv("test.csv")
ggplot(gather(df, key = series, value = value, - aID_W)) +
geom_line(aes(x = aID_W, y = value, group = series), color = "grey") +
facet_wrap(~ series, scales = "free") + labs(x = "week", y = "") +
scale_color_discrete(guide = F) + expand_limits(y = 0) + theme_bw()
続いてKFASでAIC最小化。
source("common.R", encoding = "utf-8")
# ----- KFAS ------
res <- data.frame()
for(a in seq(from = .1, to = .95, by = .05)){
RP <- calc_RP(RP = df$RP095, p = df$AP, a = a)
z <- calc_Z(RP = RP, p = df$AP)
z1 <- z$z1
z2 <- z$z2
z1E <- z$z1 * df$end
z2E <- z$z2 * df$end
# model settings
# params[1] = obs. noise variance
# params[2] = local trend noise variance
# params[3:6] = noises variance of alpha_1, beta_1, alpha_2, beta_2
# params[7:8] = AR coefs
# params[9] = AR(2) noise variance
model3KFAS <- SSModel(logPI ~ SSMtrend(1, Q = NA) +
SSMarima(ar = c(.1, .1), Q = 0) +
SSMregression(~ z1 + z1E + z2 + z2E, Q = diag(rep(NA, 4))),
H=NA, data = df, distribution = "gaussian")
# estimate unknown parameters
update.model <- function(pars, model){
model$H[] <- exp(pars[1])
diag(model$Q[, , 1])[1:6] <- exp(pars[2:7])
model$T[6:7, 6, 1] <- tanh(pars[8:9])
return(model)
}
# estimate structural params
e <- try({res.mle.kfas <- fitSSM(model = model3KFAS, updatefn = update.model,
inits = numeric(9), maxit = 1000000, method = "L-BFGS-B", hessian = T)
})
if (class(e) == "try-error") {
}
else {
res <- bind_rows(res,
data.frame(
a = a,
conv = res.mle.kfas$optim.out$convergence, loglike = - res.mle.kfas$optim.out$value,
AIC = 2 * res.mle.kfas$optim.out$value + 2 * 10,
matrix(res.mle.kfas$optim.out$par, nrow = 1))
)
}
}
res[,5:10] <- exp(res[,5:10])
res[,12:13] <- tanh(res[,12:13])
res[,11] <- exp(res[,11])
result <- filter(res, conv == 0) %>% arrange(desc(loglike))
a <- result$a[1]
RP <- calc_RP(RP = df$RP095, p = df$AP, a = a)
z <- calc_Z(RP = RP, p = df$AP)
# create the design matrix
x<-cbind(z$z1, z$z2, z$z1 * df$end, z$z2 * df$end)
parameters <- result[1,5:ncol(result)] %>% as.numeric
print(paste("AIC=", round(2 * result$loglike[1] + 2 * (ncol(result) - 4), 2)))
mod4 <- SSModel(logPI ~ SSMtrend(1, Q = parameters[6]) +
SSMarima(ar = c(parameters[8], parameters[9]), Q = parameters[7]) +
SSMregression(~ z1 + z1E + z2 + z2E, Q = diag(parameters[2:5])),
H = parameters[1], data = df, distribution = "gaussian")
coef(KFS(mod4))
# smoothed time-varying parameters
ggplot(data.frame(coef(KFS(mod4))) %>%
mutate(t=1:n()) %>%
gather(key=series, value=value, -t)) +
geom_line(aes(x=t, y=value, group=series), color="grey") +
facet_wrap(~series, scales = "free") +
labs(x="week", y="") + theme_bw()
# ---- without AR(2) ----
model2 <- SSModel(logPI ~ SSMtrend(1, Q = NA) +
SSMregression(~ z1 + z1E + z2 + z2E, Q=diag(rep(NA, 4))),
data=df, H = NA)
fit.model2 <- fitSSM(model2, inits=numeric(6), method="L-BFGS-B")
# convergence check
fit.model2$optim.out$convergence
# log likelihood
-fit.model2$optim.out$value
# smoothed time-varying parameterss
ggplot(data.frame(coef(KFS(fit.model2$model))) %>% mutate(t=1:n()) %>% gather(key=series, value=value, -t)) +
geom_line(aes(x=t, y=value, group=series), color="grey") +
facet_wrap(~series, scales = "free") +
labs(x="week", y="") +
theme_bw()