library(rprojroot)
root <- has_file(".Bayesian-Workflow-root")$make_fix_file()
library(tidyverse)
library(CircStats)
library(patchwork)
library(lubridate)
library(moveHMM)
library(bayesplot)
library(tidyr)
library(cmdstanr)
options(mc.cores = 4)
# CmdStanR output directory makes Quarto cache to work
dir.create(root("sharks", "stan_output"), showWarnings = FALSE)
options(cmdstanr_output_dir = root("sharks", "stan_output"))Model building with latent variables: Markov models for animal movement
This notebook includes the code for Bayesian Workflow book Chapter 26 Model building with latent variables: Markov models for animal movement.
1 Introduction
To demonstrate how hidden Markov models (HMMs) are used to model animal movement, we use positional data analyzed by Towner et al. (2016). Here we replicate the model building workflow and demonstrate how this would be done in a Bayesian framework.
Load packages
3 Initial values for an N-state HMM:
We create two functions to provide starting values for the mean of the state-dependent distribution for step length. Without specifying starting values, the MCMC may have some trouble initializing at plausible values. There may still be issues with initialization depending on starting values for other parameters but for now we proceed only specifying starting values for the means.
init_fun_mu <- function(no_states, no_chains) {
mu_init <- list()
for (n in 1:no_chains) {
mu_init[[n]] <- list(mu = sort(runif(no_states, min = 0, max = 0.5)))
}
mu_init
}
init_fun_logmu <- function(no_states, no_chains) {
log_mu_init <- list()
for (n in 1:no_chains) {
log_mu_init[[n]] <- list(log_mu = log(sort(runif(no_states, min = 0, max = 0.5))))
}
log_mu_init
}4 2-state HMM
We fit a 2-state HMM to the white shark tracks, assuming that each track is an independent realization of the same 2-state HMM. Note that in the corresponding chapter, we modify either the prior distributions for the state-dependent distribution or specify an ordering on the means for the step length state-dependent distribution. The Stan code in step_turn_hmm.stan can be modified to mimic different behavior and reproduce similar results.
stanHMM_2states <- list(
Nstates = 2,
Tlen = dim(ws_HMM)[1],
track_index = as.numeric(as.factor(ws_HMM$SharksexTrackNo)),
steplength = ws_HMM$steplength,
angle = ws_HMM$turnang,
Ntracks = length(unique(ws_HMM$SharksexTrackNo))
)model_2stateHMM <- cmdstan_model(
root("sharks", "step_turn_hmm.stan")
)
fit_2stateHMM <- model_2stateHMM$sample(
data = stanHMM_2states,
init = init_fun_mu(2,4),
chains = 4
)
fit_2stateHMM$summary(variables = c("mu", "sigma", "mixp",
"xangle", "yangle",
"tpm", "initial_dist",
"lp__"))Plot MCMC marginal distributions for certain parameters:
fit_2stateHMM_draws <- fit_2stateHMM$draws(format = "df",
variables = c("mu", "sigma", "mixp",
"shape", "rate",
"xangle", "yangle",
"kappa", "loc",
"tpm", "initial_dist",
"lp__"))
mcmc_hist_by_chain(fit_2stateHMM_draws, regex_pars = "mu", pars = "lp__")Plot state-dependent distributions for step length and turning angle:
cbPalette <- c("#999999", "#E69F00", "#56B4E9", "#009E73",
"#F0E442", "#0072B2", "#D55E00", "#CC79A7")
eval_gamma_sdd <- function(post_draws, no_samples) {
# setup
xval <- seq(0.001, 1, len = 200)
state1_dens <- matrix(NA, nrow = 200, ncol = no_samples)
state2_dens <- matrix(NA, nrow = 200, ncol = no_samples)
for (j in 1:no_samples) {
state1_dens[,j] <- dgamma(xval,
shape = as.numeric(post_draws[j, "shape[1]"]),
rate = as.numeric(post_draws[j, "rate[1]"]))
state2_dens[,j] <- dgamma(xval,
shape = as.numeric(post_draws[j, "shape[2]"]),
rate = as.numeric(post_draws[j, "rate[2]"]))
}
sdd <- data.frame(xval = rep(xval, times = no_samples),
state1_dens = c(state1_dens),
state2_dens = c(state2_dens),
sample = rep(1:no_samples, each= 200))
sdd
}
eval_vonMises_sdd <- function(post_draws, no_samples) {
# setup
xval <- seq(-pi, pi, len = 200)
state1_dens <- matrix(NA, nrow = 200, ncol = no_samples)
state2_dens <- matrix(NA, nrow = 200, ncol = no_samples)
for (j in 1:no_samples) {
state1_dens[,j] <- dvm(xval,
mu = as.numeric(post_draws[j, "loc[1]"]),
kappa = as.numeric(post_draws[j, "kappa[1]"]))
state2_dens[,j] <- dvm(xval,
mu = as.numeric(post_draws[j, "loc[2]"]),
kappa = as.numeric(post_draws[j, "kappa[2]"]))
}
sdd <- data.frame(xval = rep(xval, times = no_samples),
state1_dens = c(state1_dens),
state2_dens = c(state2_dens),
sample = rep(1:no_samples, each= 200))
sdd
}
sdd_steplength <- eval_gamma_sdd(fit_2stateHMM_draws, no_samples = 1000)
sdd_angle <- eval_vonMises_sdd(fit_2stateHMM_draws, no_samples = 1000)sl_sdd <- ggplot(ws_HMM) +
geom_histogram(aes(steplength, y = after_stat(density)), bins = 100, alpha = 0.2) +
xlim(-0.01, 1) +
geom_line(data = sdd_steplength, aes(xval, 0.5 * state1_dens, group = sample),
color = cbPalette[2], alpha = 0.2) +
geom_line(data = sdd_steplength, aes(xval, 0.5 * state2_dens, group = sample),
color = cbPalette[3], alpha = 0.2) +
geom_line(data = sdd_steplength, aes(xval, 0.5 * state1_dens + 0.5 * state2_dens, group = sample),
color = "darkgrey", alpha = 0.02) +
theme_classic() + xlab("") +
ylab("density") +
annotate("text", x = 0.3, y = 4, label = "state 1", color = cbPalette[2]) +
annotate("text", x = 0.45, y = 1.5, label = "state 2", color = cbPalette[3]) +
ggtitle("step length state-dependent distributions")
angle_sdd <- ggplot(ws_HMM) +
geom_histogram(aes(turnang, y = after_stat(density)), bins = 100, alpha = 0.2) +
xlim(-pi, pi) +
geom_line(data=sdd_angle, aes(xval, 0.5 * state1_dens, group = sample),
color = cbPalette[2], alpha = 0.2) +
geom_line(data = sdd_angle, aes(xval, 0.5 * state2_dens, group = sample),
color = cbPalette[3], alpha = 0.2) +
geom_line(data = sdd_angle, aes(xval, 0.5 * state1_dens + 0.5 * state2_dens, group = sample),
color = "darkgrey", alpha = 0.02) +
theme_classic() + xlab("") +
ylab("density") +
annotate("text", x = -2.5, y = 0.25, label = "state 1", color = cbPalette[2]) +
annotate("text", x = 1.5, y = 0.25, label = "state 2", color = cbPalette[3]) +
ggtitle("turning angle state-dependent distributions")
sl_sdd + angle_sddPlot state-decodings onto track using the forward-backward algorithm for local state-decoding:
state_probs_draws <- fit_2stateHMM$draws(variables =c("state_probs"), format = "draws_matrix")
state_probs_means <- data.frame(state1prob = colMeans(state_probs_draws[1:1000, (4584 + 1):(2 * 4584)]),
state2prob = colMeans(state_probs_draws[1:1000, 1:4584]))
state1_probs_quants <- data.frame(
state1prob025 = apply(state_probs_draws[1:1000, (4584 + 1):(2 * 4584)], 2, quantile, probs=0.025),
state1prob975 = apply(state_probs_draws[1:1000, (4584 + 1):(2 * 4584)], 2, quantile, probs=0.975)
)
state2_probs_quants <- data.frame(
state1prob025 = apply(state_probs_draws[1:1000, 1:(4584)], 2, quantile, probs=0.025),
state1prob975 = apply(state_probs_draws[1:1000, 1:(4584)], 2, quantile, probs=0.975)
)
ws_HMM_rep <- ws_HMM_full[,c("dateTime",
"SharkName",
"SharksexTrackNo",
"steplength",
"turnang",
"year",
"month",
"CDB",
"Lat", "Long")]
ws_HMM_rep$state1prob <- state_probs_means[,1]
ws_HMM_rep$state2prob <- state_probs_means[,2]
ws_HMM_rep$state1prob025 <- state1_probs_quants[,1]
ws_HMM_rep$state1prob975 <- state1_probs_quants[,2]
ws_HMM_rep$state2prob025 <- state2_probs_quants[,1]
ws_HMM_rep$state2prob975 <- state2_probs_quants[,2]ggplot(data = ws_HMM_rep |>
dplyr::filter(SharksexTrackNo ==
c("WSF1 T1", "WSF1 T10")),
aes(Long, Lat)) +
geom_path(aes(group = SharksexTrackNo), alpha = .5, color = "grey") +
geom_point(aes(color = state1prob)) +
labs(color = "State 1 \nProbability") +
scale_color_viridis_c(breaks = c(0, 0.25, 0.5, 0.75, 1)) +
facet_wrap(~SharksexTrackNo) + theme_classic() +
ylab("Latitude") + xlab("Longitude") + coord_map()ggplot(data = ws_HMM_rep |>
dplyr::filter(SharksexTrackNo ==
c("WSF1 T1", "WSF1 T10")),
aes(dateTime, state1prob)) + theme_classic() +
geom_point() + geom_ribbon(aes(ymin = state1prob025,
ymax = state1prob975), alpha = 0.5) +
facet_wrap( ~ SharksexTrackNo, nrow = 2, scales = "free_x") +
ylab("State 1 Probability") + xlab("Time")Plot pseudo-residuals
# plot pseudo residuals
pseudo_residuals <- fit_2stateHMM$draws(variables = "pseudo_residuals",
format = "draws_matrix")
pr_mean <- colMeans(pseudo_residuals[1:1000, ])
pr_025 <- apply(pseudo_residuals[1:1000, ], 2, quantile, probs = 0.025)
pr_975 <- apply(pseudo_residuals[1:1000, ], 2, quantile, probs = 0.975)
pr_missing_index <- which(pr_mean == 0)
df <- data.frame(y = pr_mean[-pr_missing_index])p <- ggplot(df, aes(sample = y))
p + stat_qq() + stat_qq_line() + theme_classic() +
xlab("Theoretical Quantiles") +
ylab("Sample Quantiles") +
ggtitle("Quantile-Quantile Plot")Plot state decoding histograms
Simulate data from posterior predictive distribution of fitted 2-state HMM
init_dist <- fit_2stateHMM$draws("initial_dist", format = "draws_array")
tpm <- fit_2stateHMM$draws("tpm", format = "draws_array")
sdd_sl_shape <- fit_2stateHMM$draws("shape", format = "draws_array")
sdd_sl_rate <- fit_2stateHMM$draws("rate", format = "draws_array")
sdd_sl_zeromass <- fit_2stateHMM$draws("mixp", format = "draws_array")
sdd_angle_loc <- fit_2stateHMM$draws("loc", format = "draws_array")
sdd_angle_conc <- fit_2stateHMM$draws("kappa", format = "draws_array")
post_state_samples <- fit_2stateHMM$draws("state_sequence", format = "draws_array")
state_samples <- matrix(NA, nrow = 4584, ncol = 1000)
state_samples[1, 1] <- sample(x = 2:1, size = 1, prob = init_dist[1,1,])
for (j in 1:1000) {
tpm_iter <- matrix(data = tpm[j, 1, 4:1], nrow = 2)
state_samples[1,j] <- sample(x = 2:1, size = 1, prob = init_dist[j,1,])
for (t in 2:4584) {
if(ws_HMM$SharksexTrackNo[t]==ws_HMM$SharksexTrackNo[t-1]) {
state_samples[t, j] <- sample(x = 2:1,
size = 1,
prob = tpm_iter[state_samples[t - 1, j], ])
} else {
state_samples[t,j] <- sample(x = 2:1, size = 1, prob = init_dist[j, 1, ])
}
}
}
sim_state_counts <- apply(state_samples[1:91, ], 2, table)
sim_state_props <- sim_state_counts/91
post_state_samples_counts <- apply(post_state_samples[, 1, 1:91], 1, table)
post_state_samples_props <- post_state_samples_counts/91
ws_HMM_rep$chum <- ifelse(ws_HMM_full$CDB == "x", yes = 1, no = 0)
ws_HMM_rep$chum[which(is.na(ws_HMM_rep$CDB))] <- 0
chum_ws1_tr1 <- which(ws_HMM_rep$chum[1:91] == 1)
post_state_samples_chumcounts <- apply(post_state_samples[, 1, chum_ws1_tr1], 1, table)
post_state_samples_st2chumprops <- numeric(42)
for (j in 1:1000) {
post_state_samples_st2chumprops[j] <-
max(post_state_samples_chumcounts[[j]])/42
}ggplot(data = data.frame(y = sim_state_props[1, ]), aes(y)) +
geom_histogram(bins = 100, fill = "darkgrey") + theme_classic() + xlim(-0.05, 1) +
annotate("text", label = "Posterior\nPredictive\nSimulations", x = 0.63, y = 200, col = "darkgrey") +
geom_histogram(data = data.frame(x = post_state_samples_props[1,]),
aes(x), bins = 100, fill = "black") +
annotate("text", label="State\nDecodings", x=0.35, y=200, col="black") +
geom_histogram(data = data.frame(p = 1 - post_state_samples_st2chumprops),
aes(p), bins = 100, fill = "blue") +
annotate("text", label = "Chum", x = 0.07, y = 200, col = "blue") +
xlab("Proportion of State 1 Observations") +
ylab("Count")Warning: Removed 2 rows containing missing values or values outside the scale range
(`geom_bar()`).
Removed 2 rows containing missing values or values outside the scale range
(`geom_bar()`).
Removed 2 rows containing missing values or values outside the scale range
(`geom_bar()`).
5 2-state HMM with covariates
Time inhomogeneous HMMs with covariates in transition probability matrix time of day covariates
ws_HMM$tod_cos <- cos((2*pi*(hour(ws_HMM$dateTime)*60 + minute(ws_HMM$dateTime)))/1440)
ws_HMM$tod_sin <- sin((2*pi*(hour(ws_HMM$dateTime)*60 + minute(ws_HMM$dateTime)))/1440)chum covariate
ws_HMM$chum <- ifelse(ws_HMM$CDB == "x", yes = 1, no = 0)
ws_HMM$chum[which(is.na(ws_HMM$CDB))] <- 0sex covariate
ws_HMM$sex_char <- substring(ws_HMM$SharksexTrackNo, 3, 3)
ws_HMM$sex <- ifelse(ws_HMM$sex_char == "F", yes = 0, no = 1)
HMM_covar <- matrix(data = c(rep(1, dim(ws_HMM)[1]),
ws_HMM$chum,
ws_HMM$sex,
ws_HMM$tod_cos,
ws_HMM$tod_sin), nrow = dim(ws_HMM)[1], ncol = 5)
stanHMM_2states_covariates <- list(Nstates = 2,
Tlen = dim(ws_HMM)[1],
track_index = as.numeric(as.factor(ws_HMM$SharksexTrackNo)),
steplength = ws_HMM$steplength,
angle = ws_HMM$turnang,
nCovs = 4,
covs = HMM_covar)model_2stateHMM_covariates <- cmdstan_model(
root("sharks", "step_turn_hmm_covariates.stan")
)
fit_2stateHMM_covariates <- model_2stateHMM_covariates$sample(
data = stanHMM_2states_covariates,
init = init_fun_mu(2, 4),
chains = 4
)6 2-state HMM covariates in transition probability matrix and individual varying effects
HMM with 2 state, covariates in tpm and non-centered parametrization for varying effects
stanHMM_2states_tpmcov_crencp <- list(
Nstates = 2,
Tlen = dim(ws_HMM)[1],
track_index = as.numeric(as.factor(ws_HMM$SharksexTrackNo)),
steplength = ws_HMM$steplength,
angle = ws_HMM$turnang,
nCovs = 4,
covs = HMM_covar[, -1],
Nsharks = length(unique(ws_HMM$SharksexTrackNo)),
shark_index = as.numeric(as.factor(ws_HMM$SharksexTrackNo))
)model_2stateHMM_tpmcov_crencp <- cmdstan_model(
root("sharks", "step_turn_hmm_covariates_cre_ncp.stan")
)
fit_2stateHMM_tpmcov_crencp <- model_2stateHMM_tpmcov_crencp$sample(
data = stanHMM_2states_tpmcov_crencp,
init = init_fun_mu(2, 1),
chains = 1
)Plot entries of transition probability matrix with covariates and individual varying effects:
beta <- fit_2stateHMM_tpmcov_crencp$draws("beta", format = "draws_array")
randeff <- fit_2stateHMM_tpmcov_crencp$draws("randeff_tpm", format = "draws_array")
mu_tpm <- fit_2stateHMM_tpmcov_crencp$draws("mu_tpm", format = "draws_array")
# intercept is female, no chum
DM_female_nochum <- cbind(
1,
rep(0, 721),
rep(0, 721),
cos(2*pi*480:1200/1440),
sin(2*pi*480:1200/1440)
)
# intercept is female, chum
DM_female_chum <- cbind(
1,
rep(1, 721),
rep(0, 721),
cos(2*pi*480:1200/1440),
sin(2*pi*480:1200/1440)
)
DM_male_nochum <- cbind(
1,
rep(0, 721),
rep(1, 721),
cos(2*pi*480:1200/1440),
sin(2*pi*480:1200/1440)
)
beta_array <- array(data = NA, dim = c(2, 2, length(480:1200), 100))
grid_tod <- 480:1200
track_factor <- as.factor(ws_HMM$SharksexTrackNo)
track_numeric <- as.numeric(track_factor)par(mfrow = c(2, 2))
for (i in 1:2) {
for (j in 1:2) {
beta_mat <- rbind(c(mu_tpm[1,1,1],
beta[1,1,c(1, 3, 5, 7)]),
c(mu_tpm[1,1,2],
beta[1,1,c(2, 4, 6, 8)]))
tpm <- moveHMM:::trMatrix_rcpp(nbStates = 2,
beta = t(beta_mat),
covs = DM_male_nochum)
plot(grid_tod, tpm[i, j, ], type = "l",
ylim = c(0, 1), col = "grey",
lwd = 0.5, xlab = "minute of the day",
ylab = paste0("Pr(", i, " -> ",j, ")"))
for (k in 2:100) {
beta_mat <- rbind(c(mu_tpm[k,1,1],
beta[k,1,c(1, 3, 5, 7)]),
c(mu_tpm[k,1,2],
beta[k,1,c(2, 4, 6, 8)]))
tpm <- moveHMM:::trMatrix_rcpp(nbStates = 2,
beta = t(beta_mat),
covs = DM_male_nochum)
lines(grid_tod, tpm[i, j, ], col = "grey", lwd = .1)
}
}
}
beta_mat <- rbind(c(mu_tpm[1, 1, 1],
beta[1, 1, c(1, 3, 5, 7)]),
c(mu_tpm[1, 1, 2],
beta[1, 1, c(2, 4, 6, 8)]))
tpm <- moveHMM:::trMatrix_rcpp(nbStates = 2,
beta = t(beta_mat),
covs = DM_male_nochum)
male_chum_tpm <- data.frame(omega11 = tpm[1, 1, ],
omega12 = tpm[1, 2, ],
omega21 = tpm[2, 1, ],
omega22 = tpm[2, 2, ],
dateTime = 480:1200,
draw = 1)
for (k in 2:500) {
beta_mat <- rbind(c(mu_tpm[k, 1, 1],
beta[k, 1, c(1, 3, 5, 7)]),
c(mu_tpm[k, 1, 2],
beta[k, 1, c(2, 4, 6, 8)]))
tpm <- moveHMM:::trMatrix_rcpp(nbStates = 2,
beta = t(beta_mat),
covs = DM_male_nochum)
male_chum_tpm <- rbind(male_chum_tpm,
data.frame(omega11 = tpm[1, 1, ],
omega12 = tpm[1, 2, ],
omega21 = tpm[2, 1, ],
omega22 = tpm[2, 2, ],
dateTime = 480:1200,
draw = k))
}
colnames(male_chum_tpm)[1] <- "omega[11](t)"
colnames(male_chum_tpm)[2] <- "omega[12](t)"
colnames(male_chum_tpm)[3] <- "omega[21](t)"
colnames(male_chum_tpm)[4] <- "omega[22](t)"
male_chum_tpm_long <- male_chum_tpm |>
pivot_longer(!c(dateTime, draw), names_to = "omega")
p1 <- ggplot(male_chum_tpm_long, aes(dateTime, value)) +
geom_line(aes(group = draw), alpha = 0.1, col = "darkgrey") +
facet_wrap(~omega, nrow = 2, ncol = 2, labeller = label_parsed) +
theme_classic() + ylab("Probability") +
xlab("Time") +
scale_x_continuous(breaks = c(540, 720, 900, 1080),
labels = c("09:00", "12:00", "15:00", "18:00")) +
ggtitle("Male white shark, no chum")
par(mfrow = c(2, 2))
for (i in 1:2) {
for (j in 1:2) {
beta_mat <- rbind(c(mu_tpm[1, 1, 1],
beta[1, 1, c(1, 3, 5, 7)]),
c(mu_tpm[1, 1, 1],
beta[1, 1, c(2, 4, 6, 8)]))
tpm <- moveHMM:::trMatrix_rcpp(nbStates = 2,
beta = t(beta_mat),
covs = DM_female_nochum)
plot(grid_tod, tpm[i, j, ], type = "l",
ylim = c(0, 1), col = "grey",
lwd = 0.5, xlab = "minute of the day",
ylab = paste0("Pr(", i, " -> ",j, ")"))
for (k in 2:100) {
beta_mat <- rbind(c(mu_tpm[k, 1, 1],
beta[k, 1, c(1, 3, 5, 7)]),
c(mu_tpm[k, 1, 2],
beta[k, 1, c(2, 4, 6, 8)]))
tpm <- moveHMM:::trMatrix_rcpp(nbStates = 2,
beta = t(beta_mat),
covs = DM_female_nochum)
lines(grid_tod, tpm[i, j, ], col = "grey", lwd = .1)
}
}
}
beta_mat <- rbind(c(mu_tpm[1, 1, 1],
beta[1, 1, c(1, 3, 5, 7)]),
c(mu_tpm[1, 1, 2],
beta[1, 1, c(2, 4, 6, 8)]))
tpm <- moveHMM:::trMatrix_rcpp(nbStates = 2,
beta = t(beta_mat),
covs = DM_male_nochum)
female_chum_tpm <- data.frame(
omega11 = tpm[1, 1, ],
omega12 = tpm[1, 2, ],
omega21 = tpm[2, 1, ],
omega22 = tpm[2, 2, ],
dateTime = 480:1200,
draw = 1
)
for (k in 2:500) {
beta_mat <- rbind(c(mu_tpm[1, 1, 1],
beta[k, 1, c(1, 3, 5, 7)]),
c(mu_tpm[1, 1, 2],
beta[k, 1, c(2, 4, 6, 8)]))
tpm <- moveHMM:::trMatrix_rcpp(nbStates = 2,
beta = t(beta_mat),
covs = DM_female_nochum)
female_chum_tpm <- rbind(
female_chum_tpm,
data.frame(omega11 = tpm[1, 1, ],
omega12 = tpm[1, 2, ],
omega21 = tpm[2, 1, ],
omega22 = tpm[2, 2, ],
dateTime = 480:1200,
draw = k)
)
}
colnames(female_chum_tpm)[1] <- "omega[11](t)"
colnames(female_chum_tpm)[2] <- "omega[12](t)"
colnames(female_chum_tpm)[3] <- "omega[21](t)"
colnames(female_chum_tpm)[4] <- "omega[22](t)"
female_chum_tpm_long <- female_chum_tpm |>
pivot_longer(!c(dateTime, draw), names_to = "omega")
p2 <- ggplot(female_chum_tpm_long, aes(dateTime, value)) +
geom_line(aes(group = draw), alpha = 0.1, col = "darkgrey") +
facet_wrap(~omega, nrow = 2, ncol = 2, labeller = label_parsed) +
theme_classic() + ylab("Probability") +
xlab("Time") +
scale_x_continuous(breaks = c(540, 720, 900, 1080),
labels = c("09:00", "12:00", "15:00", "18:00")) +
ggtitle("Female white shark, no chum")
wsf1_t1 <- which(ws_HMM$SharksexTrackNo == "WSF1 T1")
wsf1_t1_covar <- HMM_covar[wsf1_t1,]
wsf1_dateTime <- 60*hour(ws_HMM$dateTime[seq_along(wsf1_t1)]) +
minute(ws_HMM$dateTime[seq_along(wsf1_t1)])
dim(HMM_covar)[1] 4584 5
beta_mat <- rbind(c(randeff[1, 1, 1],
beta[1, 1, c(1, 3, 5, 7)]),
c(randeff[1, 1, 2],
beta[1, 1, c(2, 4, 6, 8)]))
tpm <- moveHMM:::trMatrix_rcpp(nbStates = 2,
beta = t(beta_mat),
covs = wsf1_t1_covar)
wsf1_chum_tpm <- data.frame(
omega11 = tpm[1, 1, ],
omega12 = tpm[1, 2, ],
omega21 = tpm[2, 1, ],
omega22 = tpm[2, 2, ],
dateTime = 60*hour(ws_HMM$dateTime[seq_along(wsf1_t1)]) +
minute(ws_HMM$dateTime[seq_along(wsf1_t1)]),
draw = 1
)
for (k in 2:500) {
beta_mat <- rbind(c(mu_tpm[k, 1, 1],
beta[k, 1, c(1, 3, 5, 7)]),
c(mu_tpm[k, 1, 2],
beta[k, 1, c(2, 4, 6, 8)]))
tpm <- moveHMM:::trMatrix_rcpp(nbStates = 2,
beta = t(beta_mat),
covs = wsf1_t1_covar)
wsf1_chum_tpm <- rbind(
wsf1_chum_tpm,
data.frame(
omega11 = tpm[1, 1, ],
omega12 = tpm[1, 2, ],
omega21 = tpm[2, 1, ],
omega22 = tpm[2, 2, ],
dateTime = 60*hour(ws_HMM$dateTime[seq_along(wsf1_t1)]) +
minute(ws_HMM$dateTime[seq_along(wsf1_t1)]),
draw = k
)
)
}
colnames(wsf1_chum_tpm)[1] <- "omega[11](t)"
colnames(wsf1_chum_tpm)[2] <- "omega[12](t)"
colnames(wsf1_chum_tpm)[3] <- "omega[21](t)"
colnames(wsf1_chum_tpm)[4] <- "omega[22](t)"
wsf1_chum <- which(HMM_covar[wsf1_t1, 2] ==1)
wsf1_chum_tpm_long <- wsf1_chum_tpm |>
pivot_longer(!c(dateTime, draw), names_to = "omega")
p3 <- ggplot(wsf1_chum_tpm_long, aes(dateTime, value)) +
geom_line(aes(group = draw),
alpha = 0.1, col = "darkgrey") +
geom_vline(xintercept = wsf1_dateTime[wsf1_chum],
linetype = 1, col = "lightgrey", alpha = 0.15) +
facet_wrap(~omega, nrow = 2, ncol = 2, labeller = label_parsed) +
theme_classic() + ylab("Probability") +
xlab("Time") +
scale_x_continuous(breaks = c(540, 720, 900, 1080),
labels = c("09:00", "12:00", "15:00", "18:00")) +
ggtitle("White shark female 1, track 1")(p1 + p2)/p3References
Licenses
- Code © 2025, Vianey Leos Barajas, licensed under BSD-3.
- Text © 2025, Vianey Leos Barajas, licensed under CC-BY-NC 4.0.