I am currently trying to fit a HGAM to model differences in daily activity patterns of fish in two treatments. Data were collected with high-resolution telemetry, and I currently have estimates of total distance swum (m) per hour (1-24) for 30 fish (~15 of each treatment) over the course of ~35 experimental days for each of three lakes (lakes A-C).These data have a clear hierarchical structure, which I am trying to account for with random effects (please let me know if my syntax is wrong as I am used to modelling in brms
/lme4
). I am creating a separate model for each lake to reduce model complexity. The model structure is as follows:
mod <- gam(as.numeric(total_distance) ~
Treatment + exp_day_z + weight_z +
s(hour, by = Treatment, k = 22, bs = "cc") + # this is my interaction of interest
s(individual_ID, bs = 're') + # individual-level random intercept
s(individual_ID, exp_day_z, bs = 're') + # individual-level random slope for experimental day (centred)
s(individual_ID, hour, bs = 're'), # individual-level random slope for hour
data = filter(perch, lake == 'A'),
method = "REML",
knots=list(hour=c(0.5, 24.5)),
family = Gamma(link = 'log'))
However, there still seems to be substantial autocorrelation in the residuals.
After reading Eric Pedersen's HGAM paper, watching several of his and Gavin Simpson's great tutorials, and reading a few older posts, I decided to try an autoregressive term in the model to account for this, as follows:
perch <- perch %>%
arrange(individual_ID, exp_day, hour) %>%
mutate(start = if_else(is.na(lag(individual_ID)) | individual_ID != lag(individual_ID), TRUE, FALSE))
mod2 <- bam(as.numeric(total_distance) ~
Treatment + exp_day_z + weight_z +
s(hour, by = Treatment, k = 22, bs = "cc") +
s(individual_ID, bs = 're') +
s(individual_ID, exp_day_z, bs = 're') +
s(individual_ID, hour, bs = 're'),
data = filter(perch, lake == 'A'),
method = "fREML",
rho = 0.6,
AR.start = start,
discrete = TRUE,
knots=list(hour=c(0.5, 24.5)),
family = Gamma(link = 'log'))
This seemed to improve the residual autocorrelation (note: this acf plot was generated with check_resid(mod2)
where grey bars represent standard residuals and black bars denote standardized AR1 corrected residuals).
This model also seemed to produce sensible predictions with slightly wider CI's than my previous model (note: contrasts were estimated using the comparisons
function from the marginaleffects
package).
I am still a little unsure of a few things:
- Should I be including random effects and an autoregresive term in the same model?
- Is my random effects structure formatted correctly, where hour and experimental day are individual-level random slopes?
- What is the rationale for choosing specific rho values when using the
bam()
function? - Finally, somewhat unrelated, is there a way to get lake- and treatment-specific smooths from one overall model, rather than model the three lakes separately (I initially tried to do this with
by = Interaction(Treatment, lake)
but this took ages to run, but perhaps this is the only way)?
Any help would be greatly appreciated!
Thanks