Skip to content

Commit 7b8569c

Browse files
committed
Add newdata arg to simulate.sdmTMB()
1 parent b3c0ac7 commit 7b8569c

File tree

5 files changed

+92
-4
lines changed

5 files changed

+92
-4
lines changed

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Type: Package
22
Package: sdmTMB
33
Title: Spatial and Spatiotemporal SPDE-Based GLMMs with 'TMB'
4-
Version: 0.6.0.9011
4+
Version: 0.6.0.9012
55
Authors@R: c(
66
person(c("Sean", "C."), "Anderson", , "[email protected]",
77
role = c("aut", "cre"),

NEWS.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# sdmTMB (development version)
22

3+
* Add `newdata` argument to `simulate.sdmTMB()`. This enables simulating on
4+
a new data frame similar to how one would predict on new data.
5+
6+
* Add `mle_mvn_samples` argument to `simulate.sdmTMB()`. Defaults to "single".
7+
If "multiple", then a sample from the random effects is taken for each
8+
simulation iteration.
9+
310
* Add `project()` experimental function.
411

512
* Add print method for `sdmTMB_cv()` output. #319

R/tmb-sim.R

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -332,8 +332,12 @@ sdmTMB_simulate <- function(formula,
332332
#' effects (this only simulates observation error). `~0` or `NA` to simulate
333333
#' new random affects (smoothers, which internally are random effects, will
334334
#' not be simulated as new).
335+
#' @param mle_mvn_samples Applies if `type = "mle-mvn"`. If `"single"`, take
336+
#' a single MVN draw from the random effects. If `"multiple"`, take an MVN
337+
#' draw from the random effects for each of the `nsim`.
335338
#' @param model If a delta/hurdle model, which model to simulate from?
336339
#' `NA` = combined, `1` = first model, `2` = second mdoel.
340+
#' @param newdata Optional new data frame from which to simulate.
337341
#' @param mcmc_samples An optional matrix of MCMC samples. See `extract_mcmc()`
338342
#' in the \href{https://github.com/pbs-assess/sdmTMBextra}{sdmTMBextra}
339343
#' package.
@@ -381,10 +385,16 @@ sdmTMB_simulate <- function(formula,
381385
simulate.sdmTMB <- function(object, nsim = 1L, seed = sample.int(1e6, 1L),
382386
type = c("mle-eb", "mle-mvn"),
383387
model = c(NA, 1, 2),
384-
re_form = NULL, mcmc_samples = NULL, silent = FALSE, ...) {
388+
newdata = NULL,
389+
re_form = NULL,
390+
mle_mvn_samples = c("single", "multiple"),
391+
mcmc_samples = NULL,
392+
silent = FALSE,
393+
...) {
385394
set.seed(seed)
386395
type <- tolower(type)
387396
type <- match.arg(type)
397+
mle_mvn_samples <- match.arg(mle_mvn_samples)
388398
assert_that(as.integer(model[[1]]) %in% c(NA_integer_, 1L, 2L))
389399

390400
# need to re-attach environment if in fresh session
@@ -403,6 +413,16 @@ simulate.sdmTMB <- function(object, nsim = 1L, seed = sample.int(1e6, 1L),
403413
stopifnot(length(object$tmb_data$sim_re) == 6L) # in case this gets changed
404414
tmb_dat$sim_re <- c(rep(1L, 5L), 0L) # last is smoothers; don't simulate them
405415
}
416+
417+
if (!is.null(newdata)) {
418+
# generate prediction TMB data list
419+
p <- predict(object, newdata = newdata, return_tmb_data = TRUE, ...)
420+
# move data elements over
421+
p <- move_proj_to_tmbdat(p, object, newdata)
422+
p$sim_re <- tmb_dat$sim_re
423+
tmb_dat <- p
424+
}
425+
406426
newobj <- TMB::MakeADFun(
407427
data = tmb_dat, map = object$tmb_map,
408428
random = object$tmb_random, parameters = object$tmb_obj$env$parList(), DLL = "sdmTMB"
@@ -411,9 +431,17 @@ simulate.sdmTMB <- function(object, nsim = 1L, seed = sample.int(1e6, 1L),
411431
# params MLE/MVN stuff
412432
if (is.null(mcmc_samples)) {
413433
if (type == "mle-mvn") {
414-
new_par <- .one_sample_posterior(object)
434+
if (mle_mvn_samples == "single") {
435+
new_par <- .one_sample_posterior(object)
436+
new_par <- replicate(nsim, new_par)
437+
} else {
438+
new_par <- lapply(seq_len(nsim), \(i) .one_sample_posterior(object))
439+
new_par <- do.call(cbind, new_par)
440+
}
415441
} else if (type == "mle-eb") {
416442
new_par <- object$tmb_obj$env$last.par.best
443+
new_par <- lapply(seq_len(nsim), \(i) new_par)
444+
new_par <- do.call(cbind, new_par)
417445
} else {
418446
cli_abort("`type` type not defined")
419447
}
@@ -432,7 +460,7 @@ simulate.sdmTMB <- function(object, nsim = 1L, seed = sample.int(1e6, 1L),
432460
} else {
433461
for (i in seq_len(nsim)) {
434462
if (!silent) cli::cli_progress_update()
435-
ret[[i]] <- newobj$simulate(par = new_par, complete = FALSE)$y_i
463+
ret[[i]] <- newobj$simulate(par = new_par[, i, drop = TRUE], complete = FALSE)$y_i
436464
}
437465
}
438466
if (!silent) cli::cli_progress_done()

man/simulate.sdmTMB.Rd

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-6-tmb-simulation.R

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,51 @@ test_that("simulate() behaves OK with or without random effects across types", {
180180
expect_length(s, 969)
181181
})
182182

183+
test_that("simulate() method works with newdata", {
184+
skip_on_cran()
185+
fit <- sdmTMB(
186+
present ~ 1,
187+
time = "year",
188+
data = pcod_2011, spatial = "on",
189+
spatiotemporal = "iid",
190+
family = binomial(),
191+
mesh = pcod_mesh_2011
192+
)
193+
s <- simulate(fit)
194+
expect_true(nrow(s) == nrow(pcod_2011))
195+
g <- replicate_df(qcs_grid, "year", unique(pcod_2011$year))
196+
s <- simulate(fit, newdata = g)
197+
expect_true(nrow(s) == nrow(g))
198+
s <- simulate(fit, newdata = subset(g, year == 2011))
199+
nrow(s)
200+
expect_true(nrow(s) == nrow(subset(g, year == 2011)))
201+
202+
gg <- subset(g, year == 2011)
203+
set.seed(1)
204+
s <- simulate(fit, newdata = gg, nsim = 400L)
205+
a <- apply(s, 1, mean)
206+
p <- predict(fit, newdata = gg)
207+
plot(a, plogis(p$est))
208+
expect_gt(cor(a, plogis(p$est)), 0.98)
209+
210+
set.seed(1)
211+
s1 <- simulate(fit, type = "mle-mvn", mle_mvn_samples = "single", nsim = 100)
212+
set.seed(1)
213+
s2 <- simulate(fit, type = "mle-mvn", mle_mvn_samples = "multiple", nsim = 100)
214+
set.seed(1)
215+
s3 <- simulate(fit, type = "mle-eb", nsim = 100)
216+
217+
expect_false(identical(s1, s2))
218+
expect_false(identical(s1, s3))
219+
220+
sd1 <- apply(s1, 1, sd)
221+
sd2 <- apply(s2, 1, sd)
222+
sd3 <- apply(s3, 1, sd)
223+
224+
expect_lt(mean(sd1), mean(sd2))
225+
})
226+
227+
183228
# test_that("TMB Delta simulation works", {
184229
# skip_on_cran()
185230
# skip_if_not_installed("INLA")

0 commit comments

Comments
 (0)