I am currently working on a dataset (count data) in which one observation corresponds to one day of monitoring at a site. The overall protocol is to monitor groups of sites along transects. Almost all transects (and by extension almost all sites) were monitored several times. Sometimes, several transects were monitored at on given date.
I would like to be able to predict the abundance of the studied species according to multiple landscape and meteorological metrics. To do so, I tried to construct GLMMs (with a negative binomial distribution) using the glmmTMB function:
Abundance ~ environmental variables + meteorological variables + Julian day (and its quadratic effect) + year (in factor) + (1 | transect / site) + (1 | date)
However, my models suffer from spatial autocorrelation. I tested it with the testSpatialAutocorrelation function from the DHARMa package:
groupLocations = aggregate(countData[, which(colnames(countData) %in% c("x","y"))]
, list(countData$xy), mean)
groupLocations$xy <- unique(countData$xy)
res <- simulateResiduals(myModel)
res2 <- recalculateResiduals(res, countData$xy)
all(unique(res2$group) == groupLocations$xy)
testSpatialAutocorrelation(res2,x=groupLocations$x,y=groupLocations$y)
I would thus like to correct it, ideally using a covariance structure like those presented in this vignette : https://cran.r-project.org/web/packages/glmmTMB/vignettes/covstruct.html
So I tried to add exp(pos + 0 | group)
to my model with (1) countData$pos <- numFactor(countData$x, countData$y)
, (2) with group being a dummy variable, my sites IDs or my date IDs...
Whatever the solution I choose, it doesn't seem to work (R is working indefinitely).
I guess my problem might come from two main issues:
- the factI don't fully understand what the "group" metric sould be,
- the fact that I have several observations at the same location (but at different dates),
For information, latitude (y) and longitude (x) are in Lambert 93 in my dataset but I can transfrom them in WGS84 if needed.
*EDIT
Here is an example of the type of data I use (but not the real ones, which are very heavy) and my code.
Interestingly with this small dataset, exp(pos + 0 |group)
seems to work whether I define group
as a dummy variable or as my site IDs, but with different results. In both cases, the testSpatialAutocorrelation function still detect spatial autocorrelation.
library(DHARMa)
library(glmmTMB)
rm(list=ls())
####I. Data creation####
newData <- data.frame(transect=c(rep("Transect_A",8*8),rep("Transect_B",10*3),rep("Transect_C",12)))
newData$site <- c(rep(paste0("Site_A",c(1:8)),8),rep(paste0("Site_B",c(1:10)),3),paste0("Site_C",c(1:12)))
newData$date <- c(rep("2001-05-03",8),rep("2001-06-25",8),rep("2002-06-04",8),rep("2002-07-15",8)
,rep("2003-04-28",8),rep("2003-05-16",8),rep("2003-06-30",8),rep("2003-07-24",8)
,rep("2001-06-02",10),rep("2002-07-15",10),rep("2003-05-08",10)
,rep("2033-05-08",12))
coordX_TA <- c(524623.6,524379.1,524379.1,524614.5,524877.1,525040.1,525185.0,525112.5)
coordX_TB <- c(527023.3,526769.7,526615.8,526652.0,526941.8,527258.7,527430.8,527566.6,527620.9,527783.9)
coordX_TC <- c(525329.9,525148.8,525230.3,525447.6,525710.2,525945.7,526126.8,526172.0,526090.5,525927.5,525728.3,525683.0)
coordY_TA <- c(6705186,6705041,6704842,6704715,6704806,6704942,6705132,6705358)
coordY_TB <- c(6703728,6703593,6703357,6703094,6702941,6703022,6703194,6703420,6703665,6703864)
coordY_TC <- c(6700966,6700767,6700486,6700369,6700323,6700360,6700559,6700803,6701057,6701220,6701365,6701102)
newData$X <- c(rep(coordX_TA,8),rep(coordX_TB,3),coordX_TC)
newData$Y <- c(rep(coordY_TA,8),rep(coordY_TB,3),coordY_TC)
newData$abundance <- c(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,2,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
,0,0,7,0,0,0,0,0,0,2,0,0,0,0,0,4,2,4,0,1,12,0,0,0,0,0,4,0,0,0,0,0,0,0,2,3,0,0,0,0,0,0,1
,0,0,0,1,0,0,0,0,0,0,1,0,9,3,6,11,4,9,12)
newData$explanatoryVar <- c(rep(c(0.42899904,0.77110546,0.45891875,2.35145233,0.52373614,0.03604363,-0.20136060,3.23943293),8)
,rep(c(0.21227248,1.31673835,3.63072002,1.29581033,3.55550595,1.36627219
,1.56734995,-0.11314501,-0.38866418,-0.18451185),3)
,c(-0.39731842,-0.47077649,-0.46944891,-0.18973198,-0.53603923,-0.38094487,0.16941656,0.60947665
,0.08765610,0.33079765,0.09083049,2.18221584))
####II. Model without correction####
newData$explanatoryVar_scaled <- scale(newData$explanatoryVar)
myModel <- glmmTMB(abundance ~ explanatoryVar + (1|transect/site) + (1|date)
, data = newData, family = "nbinom2",na.action="na.fail")
summary(myModel)
#test Spatial autocorrelation
groupLocations = aggregate(newData[, which(colnames(newData) %in% c("X","Y"))]
, list(newData$site), mean)
groupLocations$site <- unique(newData$site)
res <- simulateResiduals(myModel)
res2 <- recalculateResiduals(res, newData$site)
all(unique(res2$group) == groupLocations$site)
testSpatialAutocorrelation(res2,x=groupLocations$X,y=groupLocations$Y)
####III. Model with correction####
newData$pos <- numFactor(scale(newData$X), scale(newData$Y))
# newData$group <- factor(rep(1, nrow(newData))) #group as a dummy variable
newData$group <- factor(newData$site)
myModelCorrection <- glmmTMB(abundance ~ explanatoryVar + (1|transect/site) + (1|date) + exp(pos + 0 | group)
,data = newData, family = "nbinom2",na.action="na.fail")
summary(myModelCorrection)
#test Spatial autocorrelation
groupLocations = aggregate(newData[, which(colnames(newData) %in% c("X","Y"))]
, list(newData$site), mean)
groupLocations$site <- unique(newData$site)
res <- simulateResiduals(myModelCorrection)
res2 <- recalculateResiduals(res, newData$site)
all(unique(res2$group) == groupLocations$site)
testSpatialAutocorrelation(res2,x=groupLocations$X,y=groupLocations$Y)