-
-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathREADME.Rmd
382 lines (321 loc) · 18.6 KB
/
README.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
---
output: github_document
---
<!-- README.md is generated from README.Rmd. Please edit that file -->
```{r, echo = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "man/figures/README-",
dev = "png",
dpi = 150,
fig.height = 6,
fig.width = 9,
out.width = "100%"
)
```
<img src="man/figures/mvgam_logo.png" width = 120 alt="mvgam R package logo"/>[<img src="https://raw.githubusercontent.com/stan-dev/logos/master/logo_tm.png" align="right" width=120 alt="Stan Logo"/>](https://mc-stan.org/)
# mvgam
> **M**ulti**V**ariate (Dynamic) **G**eneralized **A**dditive **M**odels
[](https://github.com/nicholasjclark/mvgam/actions/)
[](https://app.codecov.io/gh/nicholasjclark/mvgam)
[](https://nicholasjclark.github.io/mvgam/)
[](https://doi.org/10.1111/2041-210X.13974)
[](https://cran.r-project.org/package=mvgam)
[](https://cran.r-project.org/package=mvgam)
The goal of the `mvgam` 📦 is to fit Bayesian Dynamic Generalized Additive Models (DGAMs) that can include highly flexible nonlinear predictor effects for both process and observation components. The package does this by relying on functionalities from the impressive [`brms`](https://paulbuerkner.com/brms/){target="_blank"} and [`mgcv`](https://cran.r-project.org/package=mgcv){target="_blank"} packages. Parameters are estimated using the probabilistic programming language [`Stan`](https://mc-stan.org/), giving users access to the most advanced Bayesian inference algorithms available. This allows `mvgam` to fit a very wide range of models, including:
* [Multivariate State-Space Time Series Models](https://nicholasjclark.github.io/mvgam/articles/trend_formulas.html){target="_blank"}
* [Hierarchical N-mixture Models](https://nicholasjclark.github.io/mvgam/articles/nmixtures.html){target="_blank"}
* [Hierarchical Generalized Additive Models](https://www.youtube.com/watch?v=2POK_FVwCHk){target="_blank"}
* [Joint Species Distribution Models](https://nicholasjclark.github.io/mvgam/reference/jsdgam.html){target="_blank"}
## Installation
Install the stable package version from `CRAN` using: `install.packages('mvgam')`, or install the latest development version from `GitHub` using:
`devtools::install_github("nicholasjclark/mvgam")`. You will also need a working version of `Stan` installed (along with either `rstan` and/or `cmdstanr`). Please refer to installation links for `Stan` with `rstan` [here](https://mc-stan.org/users/interfaces/rstan){target="_blank"}, or for `Stan` with `cmdstandr` [here](https://mc-stan.org/cmdstanr/){target="_blank"}.
## Cheatsheet
[](https://github.com/nicholasjclark/mvgam/raw/master/misc/mvgam_cheatsheet.pdf)
## A simple example
We can explore the package’s primary functions using one of it's built-in datasets. Use `plot_mvgam_series()` to inspect features for the four time series from [the Portal Project](https://portal.weecology.org/){target="_blank"}, which represent captures of four desert rodent species over time (see `?portal_data` for more details)
```{r include = FALSE}
library(mvgam)
library(ggplot2)
theme_set(theme_bw())
```
```{r echo = FALSE}
library(mvgam)
```
```{r, fig.alt = "Visualizing the multivariate time series in mvgam", warning=FALSE}
data(portal_data)
plot_mvgam_series(
data = portal_data,
y = 'captures',
series = 'all'
)
plot_mvgam_series(
data = portal_data,
y = 'captures',
series = 1
)
plot_mvgam_series(
data = portal_data,
y = 'captures',
series = 4
)
```
These plots show that the time series are count-responses, with missing data, seasonality and temporal autocorrelation all present. These features make time series analysis and forecasting very difficult if using conventional software and models. But `mvgam` shines in these tasks.
For most forecasting exercises, we'll want to split the data into training and testing folds
```{r}
data_train <- portal_data %>%
dplyr::filter(time <= 60)
data_test <- portal_data %>%
dplyr::filter(time > 60 &
time <= 65)
```
Formulate an `mvgam` model; this model fits a State-Space GAM in which each species has its own intercept, linear association with `ndvi_ma12` and potentially nonlinear association with `mintemp`. These effects are estimated jointly with a full time series model for the temporal dynamics (in this case an Vector Autoregressive process). We assume the outcome follows a Poisson distribution and will condition the model in `Stan` using MCMC sampling with the `Cmdstan` interface:
```{r, include=FALSE}
mod <- mvgam(
# Observation model is empty as we don't have any
# covariates that impact observation error
formula = captures ~ 0,
# Process model contains varying intercepts,
# varying slopes of ndvi_ma12 and varying smooths
# of mintemp for each series.
# Temporal dynamics are modelled with a Vector
# Autoregression (VAR(1))
trend_formula = ~
trend +
s(trend, bs = 're', by = ndvi_ma12) +
s(mintemp, bs = 'bs', by = trend) - 1,
trend_model = VAR(cor = TRUE),
# Obvservations are conditionally Poisson
family = poisson(),
priors = c(prior(normal(0, 2),
class = b),
prior(exponential(2.5),
class = sigma)),
# Condition on the training data
data = data_train,
control = list(adapt_delta = 0.99),
burnin = 1500
)
```
```{r, eval=FALSE}
mod <- mvgam(
# Observation model is empty as we don't have any
# covariates that impact observation error
formula = captures ~ 0,
# Process model contains varying intercepts,
# varying slopes of ndvi_ma12 and varying smooths
# of mintemp for each series.
# Temporal dynamics are modelled with a Vector
# Autoregression (VAR(1))
trend_formula = ~
trend +
s(trend, bs = 're', by = ndvi_ma12) +
s(mintemp, bs = 'bs', by = trend) - 1,
trend_model = VAR(cor = TRUE),
# Obvservations are conditionally Poisson
family = poisson(),
# Condition on the training data
data = data_train,
backend = 'cmdstanr'
)
```
Using `print()` will return a quick summary of the object:
```{r}
mod
```
Split Rhat and effective sample size diagnostics show good convergence of the model estimates
```{r, fig.alt = "Rhats of parameters estimated with Stan in mvgam", warning=FALSE}
mcmc_plot(mod,
type = 'rhat_hist')
```
```{r, fig.alt = "Effective sample sizes of parameters estimated with Stan in mvgam", warning=FALSE}
mcmc_plot(mod,
type = 'neff_hist')
```
Use `conditional_effects()` for a quick visualisation of the main terms in model formulae
```{r, fig.alt = "Plotting GAM effects in mvgam and R"}
conditional_effects(mod,
type = 'link')
```
If you have the `gratia` package installed, it can also be used to plot partial effects of smooths
```{r, fig.alt = "Plotting GAM smooth functions in mvgam using gratia", message=FALSE}
require(gratia)
draw(mod,
trend_effects = TRUE)
```
Or design more targeted plots using `plot_predictions()` from the `marginaleffects` package
```{r, fig.alt = "Using marginaleffects and mvgam to plot GAM smooth functions in R"}
plot_predictions(
mod,
condition = c('ndvi_ma12',
'series',
'series'),
type = 'link'
)
```
```{r, fig.alt = "Using marginaleffects and mvgam to plot GAM smooth functions in R"}
plot_predictions(
mod,
condition = c('mintemp',
'series',
'series'),
type = 'link'
)
```
We can also view the model's posterior predictions for the entire series (testing and training). These forecasts can be scored using a range of proper scoring rules. See `?score.mvgam_forecast` for more details
```{r, fig.alt = "Plotting forecast distributions using mvgam in R", warning=FALSE}
fcs <- forecast(mod,
newdata = data_test)
plot(fcs, series = 1) +
plot(fcs, series = 2) +
plot(fcs, series = 3) +
plot(fcs, series = 4)
```
For Vector Autoregressions fit in `mvgam`, we can inspect [impulse response functions and forecast error variance decompositions](https://ecogambler.netlify.app/blog/vector-autoregressions/#impulse-response-functions){target="_blank"}. The `irf()` function runs an Impulse Response Function (IRF) simulation whereby a positive “shock” is generated for a target process at time `t = 0`. All else remaining stable, it then monitors how each of the remaining processes in the latent VAR would be expected to respond over the forecast horizon `h`. The function computes impulse responses for all processes in the object and returns them in an array that can be plotted using the S3 `plot()` function. Here we will use the generalized IRF, which makes no assumptions about the order in which the series appear in the VAR process, and inspect how each process is expected to respond to a sudden, positive pulse from the other processes over a horizon of 12 timepoints.
```{r, fig.alt = "Impulse response functions computed using mvgam in R"}
irfs <- irf(mod,
h = 12,
orthogonal = FALSE)
plot(irfs,
series = 1)
plot(irfs,
series = 3)
```
Using the same logic as above, we can inspect forecast error variance decompositions (FEVDs) for each process using the `fevd()` function. This type of analysis asks how orthogonal shocks to all process in the system contribute to the variance of forecast uncertainty for a focal process over increasing horizons. In other words, the proportion of the forecast variance of each latent time series can be attributed to the effects of the other series in the VAR process. FEVDs are useful because some shocks may not be expected to cause variations in the short-term but may cause longer-term fluctuations
```{r, fig.alt = "Forecast error variance decompositions computed using mvgam in R"}
fevds <- fevd(mod,
h = 12)
plot(fevds)
```
This plot shows that the variance of forecast uncertainty for each process is initially dominated by contributions from that same process (i.e. self-dependent effects) but that effects from other processes become more important over increasing forecast horizons. Given what we saw from the IRF plots above, these long-term contributions from interactions among the processes makes sense.
Plotting randomized quantile residuals over `time` for each series can give useful information about what might be missing from the model. We can use the highly versatile `pp_check()` function to plot these:
```{r, warning=FALSE}
pp_check(
mod,
type = 'resid_ribbon_grouped',
group = 'series',
x = 'time',
ndraws = 200
)
```
When describing the model, it can be helpful to use the `how_to_cite()` function to generate a scaffold for describing the model and sampling details in scientific communications
```{r}
description <- how_to_cite(mod)
```
```{r, eval = FALSE}
description
```
```{r, echo=FALSE}
cat("Methods text skeleton\n")
cat(insight::format_message(description$methods_text))
```
```{r echo=FALSE}
cat("\nPrimary references\n")
for (i in seq_along(description$citations)) {
cat(insight::format_message(description$citations[[i]]))
cat('\n')
}
cat("\nOther useful references\n")
for (i in seq_along(description$other_citations)) {
cat(insight::format_message(description$other_citations[[i]]))
cat('\n')
}
```
The post-processing methods we have shown above are just the tip of the iceberg. For a full list of methods to apply on fitted model objects, type `methods(class = "mvgam")`.
## Extended observation families
`mvgam` was originally designed to analyse and forecast non-negative integer-valued data. But further development of `mvgam` has resulted in support for a growing number of observation families. Currently, the package can handle data for the following:
* `gaussian()` for real-valued data
* `student_t()` for heavy-tailed real-valued data
* `lognormal()` for non-negative real-valued data
* `Gamma()` for non-negative real-valued data
* `betar()` for proportional data on `(0,1)`
* `bernoulli()` for binary data
* `poisson()` for count data
* `nb()` for overdispersed count data
* `binomial()` for count data with known number of trials
* `beta_binomial()` for overdispersed count data with known number of trials
* `nmix()` for count data with imperfect detection (unknown number of trials)
See `??mvgam_families` for more information. Below is a simple example for simulating and modelling proportional data with `Beta` observations over a set of seasonal series with independent Gaussian Process dynamic trends:
```{r beta_sim, message=FALSE, warning=FALSE}
set.seed(100)
data <- sim_mvgam(
family = betar(),
T = 80,
trend_model = GP(),
prop_trend = 0.5,
seasonality = "shared"
)
plot_mvgam_series(
data = data$data_train,
series = "all"
)
```
```{r, include=FALSE}
mod <- mvgam(
y ~ s(season, bs = "cc", k = 7) +
s(season, by = series, m = 1, k = 5),
trend_model = GP(),
data = data$data_train,
newdata = data$data_test,
family = betar()
)
```
```{r, eval=FALSE}
mod <- mvgam(
y ~ s(season, bs = "cc", k = 7) +
s(season, by = series, m = 1, k = 5),
trend_model = GP(),
data = data$data_train,
newdata = data$data_test,
family = betar()
)
```
Inspect the summary to see that the posterior now also contains estimates for the `Beta` precision parameters $\phi$.
```{r}
summary(mod,
include_betas = FALSE)
```
Plot the hindcast and forecast distributions for each series
```{r eval=FALSE}
library(patchwork)
fc <- forecast(mod)
wrap_plots(
plot(fc, series = 1),
plot(fc, series = 2),
plot(fc, series = 3),
ncol = 2
)
```
```{r beta_fc, echo=FALSE, message=FALSE}
library(patchwork)
fc <- forecast(mod)
wrap_plots(
plot(fc, series = 1),
plot(fc, series = 2),
plot(fc, series = 3),
ncol = 2
)
```
There are many more extended uses of `mvgam`, including the ability to fit hierarchical State-Space GAMs that include dynamic and spatially varying coefficient models, dynamic factors, Joint Species Distribution Models and much more. See the [package documentation](https://nicholasjclark.github.io/mvgam/){target="_blank"} for more details. The package can also be used to generate all necessary data structures, initial value functions and modelling code necessary to fit DGAMs using `Stan`. This can be helpful if users wish to make changes to the model to better suit their own bespoke research / analysis goals. The [`Stan` Discourse](https://discourse.mc-stan.org/){target="_blank"} is a helpful place to troubleshoot.
## Citing `mvgam` and related software
When using any software please make sure to appropriately acknowledge the hard work that developers and maintainers put into making these packages available. Citations are currently the best way to formally acknowledge this work (but feel free to ⭐ this repo as well), so we highly encourage you to cite any packages that you rely on for your research.
When using `mvgam`, please cite the following:
> Clark, N.J. and Wells, K. (2023). Dynamic Generalized Additive Models (DGAMs) for forecasting discrete ecological time series. *Methods in Ecology and Evolution*. DOI: https://doi.org/10.1111/2041-210X.13974
As `mvgam` acts as an interface to `Stan`, please additionally cite:
> Carpenter B., Gelman A., Hoffman M. D., Lee D., Goodrich B., Betancourt M., Brubaker M., Guo J., Li P., and Riddell A. (2017). Stan: A probabilistic programming language. *Journal of Statistical Software*. 76(1). DOI: https://doi.org/10.18637/jss.v076.i01
`mvgam` relies on several other `R` packages and, of course, on `R` itself. To find out how to cite `R` and its packages, use `citation()`. There are some features of `mvgam` which specifically rely on certain packages. The most important of these is the generation of data necessary to estimate smoothing splines and Gaussian Processes, which rely on the `mgcv`, `brms` and `splines2` packages. The `rstan` and `cmdstanr` packages together with `Rcpp` makes `Stan` conveniently accessible in `R`. If you use some of these features, please also consider citing the related packages.
## Getting help
If you encounter a clear bug, please file an issue with a minimal reproducible example on [GitHub](https://github.com/nicholasjclark/mvgam/issues). Please also feel free to use the [`mvgam` Discussion Board](https://github.com/nicholasjclark/mvgam/discussions) to hunt for or post other discussion topics related to the package, and do check out the [`mvgam` Changelog](https://nicholasjclark.github.io/mvgam/news/index.html) for any updates about recent upgrades that the package has incorporated.
## Other resources
A series of [vignettes cover data formatting, forecasting and several extended case studies of DGAMs](https://nicholasjclark.github.io/mvgam/){target="_blank"}. A number of other examples, including some step-by-step introductory webinars, have also been compiled:
* [Time series in R and Stan using the `mvgam` package](https://www.youtube.com/playlist?list=PLzFHNoUxkCvsFIg6zqogylUfPpaxau_a3){target="_blank"}
* [Ecological Forecasting with Dynamic Generalized Additive Models](https://www.youtube.com/watch?v=0zZopLlomsQ){target="_blank"}
* [Distributed lags (and hierarchical distributed lags) using `mgcv` and `mvgam`](https://ecogambler.netlify.app/blog/distributed-lags-mgcv/){target="_blank"}
* [State-Space Vector Autoregressions in `mvgam`](https://ecogambler.netlify.app/blog/vector-autoregressions/){target="_blank"}
* [Ecological Forecasting with Dynamic GAMs; a tutorial and detailed case study](https://www.youtube.com/watch?v=RwllLjgPUmM){target="_blank"}
* [Incorporating time-varying seasonality in forecast models](https://ecogambler.netlify.app/blog/time-varying-seasonality/){target="_blank"}
## Interested in contributing?
I'm actively seeking PhD students and other researchers to work in the areas of ecological forecasting, multivariate model evaluation and development of `mvgam`. Please reach out if you are interested (n.clark'at'uq.edu.au). Other contributions are also very welcome, but please see [The Contributor Instructions](https://github.com/nicholasjclark/mvgam/blob/master/.github/CONTRIBUTING.md) for general guidelines. Note that by participating in this project you agree to abide by the terms of its [Contributor Code of Conduct](https://dplyr.tidyverse.org/CODE_OF_CONDUCT).
## License
This project is licensed under an `MIT` open source license