Let’s start by looking at the density of step and turn distributions. As we know the true parameters for these, we can add them to the plots.
# restore NAs
datos$step[datos$step < 0] <- NA
datos$angle[datos$angle < (-pi)] <- NA
# unpack posterior draws
shape.pos <- rstan::extract(fit, pars = "shape")$shape
scale.pos <- rstan::extract(fit, pars = "scale")$scale
mu.pos <- rstan::extract(fit, pars = "mu")$mu
rho.pos <- rstan::extract(fit, pars = "rho")$rho
# indices of posterior draws to plot (thinned for visualisation purposes)
ind <- seq(1, nrow(shape.pos), by = 5)
# plot step length densities
stepgrid <- seq(min(datos$step, na.rm = TRUE),
max(datos$step, na.rm = TRUE), length = 100)
plot(NA, xlim = c(0, 10), ylim = c(0, 1.1),
xlab = "step length", ylab = "density")
for (i in ind) {
lines(stepgrid, dweibull(stepgrid, shape = shape.pos[i, 1], scale = scale.pos[i, 1]),
lwd = 0.2, col = adjustcolor(pal[1], alpha.f = 0.1))
lines(stepgrid, dweibull(stepgrid, shape = shape.pos[i, 2], scale = scale.pos[i, 2]),
lwd = 0.2, col = adjustcolor(pal[2], alpha.f = 0.1))
}
lines(stepgrid, dweibull(stepgrid, shape = b[1], scale = a[1]), lwd = 2)
lines(stepgrid, dweibull(stepgrid, shape = b[2], scale = a[2]), lwd = 2)
# plot turning angle densities
anglegrid <- seq(-pi, pi, length = 100)
plot(NA, xlim = c(-pi, pi), ylim = c(0, 1.6),
xlab = "turning angle", ylab = "density")
for (i in ind[-1]) {
lines(anglegrid, dwrpcauchy(anglegrid, mu = mu.pos[i, 1], rho = rho.pos[i, 1]),
lwd = 0.2, col = adjustcolor(pal[1], alpha.f = 0.1))
lines(anglegrid, dwrpcauchy(anglegrid, mu = mu.pos[i, 2], rho = rho.pos[i, 2]),
lwd = 0.2, col = adjustcolor(pal[2], alpha.f = 0.1))
}
lines(anglegrid, dwrpcauchy(anglegrid, mu=m[1], rho=rho[1]), lwd = 2)
lines(anglegrid, dwrpcauchy(anglegrid, mu=m[2], rho=rho[2]), lwd = 2)
Now a posterior predictive check on the autocorrelation in step lengths
psam <- rstan::extract(fit, pars = c("mu", "rho", "scale", "shape", "viterbi"))
steps <- datos$step
n.sims <- nrow(psam$mu)
ppsteps <- matrix(NA,n.sims,T)
#ppturns <- matrix(NA,n.sims,T)
p2 <- psam$viterbi
nobs <- ncol(p2)
scale <- psam$scale
shape <- psam$shape
mu <- psam$mu
rho <- psam$rho
for(i in 1:n.sims){
z <- p2[i,]
ppsteps[i,] = rweibull(T,shape=shape[i,z],scale=scale[i,z])
# ppturns[i,] = rwrpcauchy(T, location = (mu[i,z]), rho = rho[i,z])
}
#Autocorrelation
nlags <- 61
oac = acf(steps[2:(T-1)],lag.max=(nlags-1),plot=FALSE) # ACF observada
ppac = matrix(NA,n.sims,nlags)
for(i in 1:n.sims){
ppac[i,] = acf(ppsteps[i,],lag.max=(nlags-1),plot=FALSE)$acf
}
library(coda)
hpd <- HPDinterval(as.mcmc(ppac), prob = 0.9)
dat <- data.frame(y = 1:61, acf = as.numeric(oac$acf),
lb = hpd[, 1], ub = hpd[, 2])
ggplot(dat, aes(y, acf)) +
geom_ribbon(aes(x = y, ymin = lb, ymax = ub), fill = "grey70", alpha = 0.5) +
geom_point(col = "black", size = 1) +
geom_line() +
coord_cartesian(xlim = c(2, 60), ylim = c(-0.2, 0.7)) +
xlab("Lag") + ylab("ACF") +
ggtitle("Observed Autocorrelation
with 90% CI for ACF of Predicted Quantities")
library (raster)
set.seed(12)
side <- 300
E <- raster(nrows=side, ncols=side, xmn=0, xmx=side, ymn=0,ymx=side)
E[] <- runif(side*side, -8, 16)
E <- focal(E, w=matrix(1, 7, 7), mean)
E <- focal(E, w=matrix(1, 7, 7), mean)
E.m = cellStats(E, "mean")
E.sd = cellStats(E, "sd")
E.scale <- (E - E.m) / E.sd
plot(E.scale)
T <- 200
a <- c(1, 5)
b <- c(1, 2)
m <- c(pi, 0)
rho <- c(0.5, 0.85)
p <- 0.2
b0 <- c(-2, -0.5)
b1 <- c(0, 2)
MU <- matrix(0, T, 2)
z <- numeric(T)
env <- numeric(T)
phi <- runif(1, 0, 2 * pi)
z[1] <- rbinom(1, size = 1, prob = p) + 1
MU[1, ] <- side/2
set.seed(12)
for(t in 2:T){
env[t-1] <- raster::extract(E.scale, matrix(MU[t-1,],1,2))
if(!is.na(env[t-1])){
p <- plogis(b0[z[t-1]] + b1[z[t-1]] * env[t-1])
}
z[t] <- rbinom(1, size = 1, prob = p) + 1
tmp <- rwrpcauchy(1, location = m[z[t]], rho = rho[z[t]])
if (tmp > pi)
tmp <- tmp - 2 * pi
if (tmp < -pi)
tmp <- tmp + 2 * pi
phi <- phi + tmp
s <- rweibull(1, shape = b[z[t]], scale = a[z[t]])
move <- s * c(Re(exp((0+1i) * phi)), Im(exp((0+1i) * phi)))
MU[t, ] <- MU[t-1, ] + move
}
op <- par(cex.lab = 1.2 , font.lab = 1, cex.axis = 1, bty = "n", las = 1)
plot(E.scale)
#lines(MU, type = "l", asp = 1, xlab="", ylab="", lwd=1) #, xaxt='n', yaxt='n')
for(i in 2:T){
lines(MU[(i-1):i,], col=pal[z[i]], lwd=2)
}
plot time series for states
op <- par(cex.lab = 1.2 , font.lab = 2, cex.axis = 1, bty = "n", las = 1, mar=c(5,4,8,1))
plot(z-1, type = "s", xlab = "time", ylab = "z", yaxt="n")
axis(side=2, at=c(0,1))
Fit the model
datos <- momentuHMM::prepData(data.frame(MU),type="UTM",coordNames=c("X1","X2"))
# remove NAs
datos$step[is.na(datos$step)] <- -100
datos$angle[is.na(datos$angle)] <- -100
datos$ID <- as.numeric(datos$ID)
stan.data <- list(T=nrow(datos),
N=2,
ID=datos$ID,
steps=datos$step,
turns=datos$angle,
nCovs=1,
covs=cbind(matrix(1,nrow=nrow(datos)),env),
lb = 0.8)
library(rstan)
rstan_options(auto_write = TRUE)
options(mc.cores = parallel::detectCores())
fit <- stan(file="Stan_code/sw.stan",
data=stan.data,
iter=1000,
chains=3,
seed = 1234,
control=list(adapt_delta=0.95)
)
Before anything, we check that the chains have converged (r-hat < 1.1) and look at the posteriors for the steps and turns
plot(fit, pars = list("mu", "rho", "shape", "scale", "beta"),
show_density = TRUE, ci_level = 0.5, fill_color = "gray")
The classsified trajectory
psam <- rstan::extract(fit, pars = c("mu", "rho", "scale", "shape", "viterbi"))
states <- colMeans(psam$viterbi)
ggplot(datos, aes(x,y,group=ID,col=states)) +
geom_point(size=0.5) +
geom_path(size=0.5) +
coord_equal()
Compare true states to estimated ones
op <- par(cex.lab = 1.2 , font.lab = 1, cex.axis = 1, bty = "n", las = 1)
plot(jitter(z[-1]), states[-200], col = gray(0.5,0.5), pch=16, xlab = "jittered z", ylab = "mean posterior state" )
Leos-Barajas, V., and T. Michelot. 2018. An introduction to animal movement modeling with hidden markov models using stan for bayesian inference.