Skip to content

Commit 82e54cf

Browse files
Multispecies / multiresponse vignette
I talked to Lewis last week about this. Currently includes examples of: - main effects by species - spatial fields by species, with and without shared variance - spatiotemporal fields by species
1 parent f63bd84 commit 82e54cf

File tree

1 file changed

+247
-0
lines changed

1 file changed

+247
-0
lines changed

vignettes/web_only/multispecies.Rmd

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
---
2+
title: "Fitting multispecies models with sdmTMB"
3+
date: "`r Sys.Date()`"
4+
output: rmarkdown::html_vignette
5+
vignette: >
6+
%\VignetteIndexEntry{Fitting multispecies models with sdmTMB}
7+
%\VignetteEngine{knitr::rmarkdown}
8+
%\VignetteEncoding{UTF-8}
9+
---
10+
11+
**If the code in this vignette has not been evaluated, a rendered version is available on the [documentation site](https://pbs-assess.github.io/sdmTMB/index.html) under 'Articles'.**
12+
13+
```{r setup, include = FALSE, cache=FALSE}
14+
dplyr_installed <- require("dplyr", quietly = TRUE)
15+
ggplot_installed <- require("ggplot2", quietly = TRUE)
16+
pkgs <- dplyr_installed && ggplot_installed
17+
EVAL <- identical(Sys.getenv("NOT_CRAN"), "true") && pkgs
18+
knitr::opts_chunk$set(
19+
collapse = TRUE,
20+
warning=FALSE,
21+
message=FALSE,
22+
comment = "#>",
23+
fig.width = 7,
24+
fig.asp = 0.618,
25+
eval = EVAL,
26+
purl = EVAL
27+
)
28+
```
29+
30+
```{r packages, message=FALSE, warning=TRUE}
31+
library(ggplot2)
32+
library(dplyr)
33+
library(sdmTMB)
34+
```
35+
36+
For some applications, we might be interested in fitting a model that includes multiple responses (such as 2+ species). The most important step in fitting these models is understanding which parameters are shared, and which parameters are species specific.
37+
38+
Below, we illustrate a series of models that may be used to answer different questions. We'll start by simulating a 2-species dataset. Each species is allowed to have unique spatial ranges and variances, as well as different year effects.
39+
40+
```{r sim_dat}
41+
set.seed(123)
42+
43+
# make fake predictor(s) (a1) and sampling locations:
44+
predictor_dat <- data.frame(
45+
X = runif(500), Y = runif(500),
46+
year = rep(1:5, each = 100)
47+
)
48+
predictor_dat$fyear <- as.factor(predictor_dat$year)
49+
mesh <- make_mesh(predictor_dat, xy_cols = c("X", "Y"), cutoff = 0.1)
50+
51+
sim_dat_A <- sdmTMB_simulate(
52+
formula = ~ 0 + fyear,
53+
data = predictor_dat,
54+
time = "year",
55+
mesh = mesh,
56+
range = 0.5,
57+
family = tweedie(),
58+
seed = 42,
59+
sigma_O = 0.2,
60+
phi = 0.1,
61+
sigma_E = 0.1,
62+
B = runif(5, min = 5, max = 8) # 5 random year effects
63+
)
64+
sim_dat_A$species <- "A"
65+
66+
sim_dat_B <- sdmTMB_simulate(
67+
formula = ~ 0 + fyear,
68+
data = predictor_dat,
69+
time = "year",
70+
mesh = mesh,
71+
range = 0.2,
72+
family = gaussian(),
73+
seed = 42,
74+
sigma_O = 0.3,
75+
phi = 0.1,
76+
sigma_E = 0.1,
77+
B = runif(5, min = 5, max = 8) # 5 random year effects
78+
)
79+
sim_dat_B$species <- "B"
80+
81+
sim_dat <- rbind(sim_dat_A, sim_dat_B)
82+
sim_dat$fyear <- as.factor(sim_dat$year)
83+
```
84+
85+
We'll start by making the mesh across the full dataset
86+
87+
```{r mesh_fig, fig.asp=1}
88+
spde <- make_mesh(sim_dat, c("X", "Y"), cutoff = 0.1)
89+
plot(spde)
90+
```
91+
92+
### Model 1: species specific intercepts
93+
94+
As a first model, we can include species specific year effects. This can be done in a couple ways -- one option would be to estimate the `species*year` interaction, letting the year effects for each species be independent. All other parameters (range, spatial field, spatial variance, spatiotemporal fields, spatiotemporal variances) are shared.
95+
96+
```{r}
97+
fit <- sdmTMB(
98+
formula = observed ~ fyear*species,
99+
data = sim_dat,
100+
time = "year",
101+
spatiotemporal = "iid",
102+
mesh = spde, family = gaussian())
103+
```
104+
105+
This model doesn't appear to converge, and the results from `sanity(fit)` indicate problems with the spatial and spatiotemporal variances. This is likely a sign that our assumptions in the estimation model are wrong for this particular dataset (not surprising, as our simulated data has different parameters by species).
106+
107+
Our simulated dataset only has 5 years of data -- if we had a longer time series, an alternative formulation for our year effects could be to fit smooth terms, like in a GAM with `mgcv`.
108+
109+
```{r eval=FALSE}
110+
fit <- sdmTMB(
111+
formula = observed ~ s(year,by="species"),
112+
data = sim_dat,
113+
time = "year",
114+
spatiotemporal = "iid",
115+
mesh = spde, family = gaussian())
116+
```
117+
118+
### Model 2: species specific fields
119+
120+
We may be interested in fitting a model that lets the spatial patterning differ by species. These kinds of models can be expressed using spatially varying coefficients. Note that we use `spatial = off` because this represents a global spatial intercept -- turning this off is equivalent to using a `-1` of `0` in a main formula including a factor.
121+
122+
```{r}
123+
fit <- sdmTMB(
124+
observed ~ 0 + fyear, # shared year effects
125+
data = sim_dat,
126+
mesh = spde,
127+
family = gaussian(),
128+
spatial = "off",
129+
time = "year",
130+
spatiotemporal = "iid",
131+
spatial_varying = ~ 0 + as.factor(species)
132+
)
133+
```
134+
135+
This model also appears to be problematic. If we look at the estimated parameters, you'll notice that there are two row entries for `sigma_Z`. This means that our model is trying to estimate separate species specific variance terms for the species specific spatial fields.
136+
137+
```{r}
138+
fit$sd_report
139+
```
140+
141+
If we wanted to estimate species specific spatial fields with a single shared variance (meaning the magnitude of the peaks and valleys in the field was similar), we could do that by specifying a custom map argument and passing it into `sdmTMBcontrol()`
142+
143+
```{r}
144+
map_list = list(ln_tau_Z = factor(c(1, 1)))
145+
146+
fit <- sdmTMB(
147+
observed ~ 0 + fyear, # shared year effects
148+
data = sim_dat,
149+
mesh = spde,
150+
family = gaussian(),
151+
spatial = "off",
152+
time = "year",
153+
spatiotemporal = "iid",
154+
spatial_varying = ~ 0 + as.factor(species),
155+
control = sdmTMBcontrol(map = map_list)
156+
)
157+
```
158+
159+
### Model 3: species specific spatiotemporal fields
160+
161+
As a last example, we can set up a model that also includes spatiotemporal fields unique to each species. In all of the examples above, spatiotemporal fields are included -- but shared among species.
162+
163+
One approach to including spatiotemporal fields is creating a new variable combining species and year. We can then include this spatiotemporal variation by changing the `time` argument to be `time = "species_year"`
164+
165+
```{r}
166+
# This needs to be a factor
167+
sim_dat$species_year <- as.factor(paste(sim_dat$species, sim_dat$year))
168+
169+
map_list = list(ln_tau_Z = factor(c(1, 1)))
170+
171+
fit <- sdmTMB(
172+
observed ~ 0 + fyear, # shared year effects
173+
data = sim_dat,
174+
mesh = spde,
175+
family = gaussian(),
176+
spatial = "off",
177+
time = "species_year",
178+
spatiotemporal = "iid",
179+
spatial_varying = ~ 0 + as.factor(species),
180+
control = sdmTMBcontrol(map = map_list)
181+
)
182+
183+
```
184+
185+
This is getting a little better -- there is a gradient warning, but the Hessian appears to be ok.
186+
187+
### Putting it all together
188+
189+
Our spatiotemporal model above came close to passing all sanity() checks, and could be modified to also include the species-specific year effects that we started with
190+
191+
```{r}
192+
fit <- sdmTMB(
193+
observed ~ 0 + fyear*species, # shared year effects
194+
data = sim_dat,
195+
mesh = spde,
196+
family = gaussian(),
197+
spatial = "off",
198+
time = "species_year",
199+
spatiotemporal = "iid",
200+
spatial_varying = ~ 0 + as.factor(species),
201+
control = sdmTMBcontrol(map = map_list)
202+
)
203+
```
204+
205+
These examples illustrate a number of ways that species - specific effects can be included in `sdmTMB` models, and can be extended to other types of groupings. A brief summary of these can be summarized as:
206+
207+
```{r echo=FALSE}
208+
desc <- data.frame("Form" = c("Main effects", "Spatial effects", "Spatial effects w/shared variance", "Spatiotemporal effects"), "Implementation" = c("Year by species interactions, or smooths", "Spatially varying coefficients", "Spatially varying coefficients + map argument", "Species-year factor as time variable"))
209+
knitr::kable(desc)
210+
```
211+
212+
### Extensions
213+
214+
Including spatiotemporal effects like species-year combinations in the `time` argument is relatively straightforward, but these models could get more complicated if we wanted to also include other grouping factors (such as cohort effects for each species). There may be some identifiability issues that would have to be resolved, but even if the `time` argument is being used, it's possible to add other factors through the `spatial_varying` formula. As an example, if our dataframe included a cohort variable for each species, we could first create a new combination
215+
216+
```{r eval=FALSE}
217+
sim_dat$species_cohort <- as.factor(paste(sim_dat$species, sim_dat$cohort))
218+
```
219+
220+
If there were ~ 10 levels of `species_cohort`, and we were including our original spatial effects in `spatial_varying`, we could create a new map list that would indicate we wanted all species-cohort fields to have the same spatial variance,
221+
222+
```{r eval=FALSE}
223+
sim_dat$species_year <- as.factor(paste(sim_dat$species, sim_dat$year))
224+
225+
# The first 2 elements correspond to species, remaining 10 to species-cohort
226+
map_list = list(ln_tau_Z = factor(c(1, 1, rep(2, 10))))
227+
228+
fit <- sdmTMB(
229+
observed ~ 0 + fyear*species,
230+
data = sim_dat,
231+
mesh = spde,
232+
family = gaussian(),
233+
spatial = "off",
234+
time = "species_year",
235+
spatiotemporal = "iid",
236+
spatial_varying = ~ 0 + as.factor(species) + species_cohort,
237+
control = sdmTMBcontrol(map = map_list)
238+
)
239+
```
240+
241+
242+
243+
244+
245+
246+
247+

0 commit comments

Comments
 (0)