-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBIOMOD_ProjectionWrap.R
409 lines (374 loc) · 19.3 KB
/
BIOMOD_ProjectionWrap.R
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
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
###################################################################################################
##' @name BIOMOD_ProjectionWrap
##' @author Helene Blancheteau
##'
##' @title Project a range of calibrated species distribution models onto new environment
##'
##' @description This function allows to project a range of models built with the
##' \code{\link{BIOMOD_Wrap}} function onto new environmental data (\emph{which can
##' represent new areas, resolution or time scales for example}).
##'
##'
##' @param bm.wrap a \code{\link{BIOMOD.wrap.out}} object returned by the
##' \code{\link{BIOMOD_Wrap}} function
##' @param proj.name a \code{character} corresponding to the name (ID) of the projection set
##' (\emph{a new folder will be created within the simulation folder with this name})
##' @param new.env A \code{matrix}, \code{data.frame} or
##' \code{\link[terra:rast]{SpatRaster}} object containing the new
##' explanatory variables (in columns or layers, with names matching the
##' variables names given to the \code{\link{BIOMOD_FormatingData}} function to build
##' \code{bm.wrap}) that will be used to project the species distribution model(s)
##' \cr \emph{Note that old format from \pkg{raster} are still supported such as
##' \code{RasterStack} objects. }
##'
##' @param new.env.xy (\emph{optional, default} \code{NULL}) \cr
##' If \code{new.env} is a \code{matrix} or a \code{data.frame}, a 2-columns \code{matrix} or
##' \code{data.frame} containing the corresponding \code{X} and \code{Y} coordinates that will be
##' used to project the species distribution model(s)
##' @param models.chosen a \code{vector} containing model names to be kept, must be either
##' \code{all}, \code{single}, \code{ensemble} or a sub-selection of model names that can be obtained with the
##' \code{\link{get_built_models}} function
##'
##' @param metric.binary (\emph{optional, default} \code{NULL}) \cr
##' A \code{vector} containing evaluation metric names to be used to transform prediction values
##' into binary values based on models evaluation scores obtained with the
##' \code{\link{BIOMOD_Modeling}} function. Must be among \code{all} (same evaluation metrics than
##' those of \code{bm.wrap}) or \code{POD}, \code{FAR}, \code{POFD}, \code{SR}, \code{ACCURACY},
##' \code{BIAS}, \code{ROC}, \code{TSS}, \code{KAPPA}, \code{OR}, \code{ORSS}, \code{CSI},
##' \code{ETS}, \code{BOYCE}, \code{MPA}
##' @param metric.filter (\emph{optional, default} \code{NULL}) \cr
##' A \code{vector} containing evaluation metric names to be used to transform prediction values
##' into filtered values based on models evaluation scores obtained with the
##' \code{\link{BIOMOD_Modeling}} function. Must be among \code{all} (same evaluation metrics than
##' those of \code{bm.wrap}) or \code{POD}, \code{FAR}, \code{POFD}, \code{SR}, \code{ACCURACY},
##' \code{BIAS}, \code{ROC}, \code{TSS}, \code{KAPPA}, \code{OR}, \code{ORSS}, \code{CSI},
##' \code{ETS}, \code{BOYCE}, \code{MPA}
##'
##' @param compress (\emph{optional, default} \code{TRUE}) \cr
##' A \code{logical} or a \code{character} value defining whether and how objects should be
##' compressed when saved on hard drive. Must be either \code{TRUE}, \code{FALSE}, \code{xz} or
##' \code{gzip} (see Details)
##' @param digits (\emph{optional, default} \code{0}) \cr
##' A \code{integer} value defining the number of digits of the predictions.
##' @param build.clamping.mask (\emph{optional, default} \code{TRUE}) \cr
##' A \code{logical} value defining whether a clamping mask should be built and saved on hard
##' drive or not (see Details)
##'
##' @param nb.cpu (\emph{optional, default} \code{1}) \cr
##' An \code{integer} value corresponding to the number of computing resources to be used to
##' parallelize the single models computation
##' @param seed.val (\emph{optional, default} \code{NULL}) \cr
##' An \code{integer} value corresponding to the new seed value to be set
##'
##' @param \ldots (\emph{optional, see Details)})
##'
##'
##' @return
##'
##' A \code{\link{BIOMOD.projection.out}} object containing models projections, or links to saved
##' outputs. \cr Models projections are stored out of \R (for memory storage reasons) in
##' \code{proj.name} folder created in the current working directory :
##' \enumerate{
##' \item the output is a \code{data.frame} if \code{new.env} is a \code{matrix} or a
##' \code{data.frame}
##' \item it is a \code{\link[terra:rast]{SpatRaster}} if \code{new.env} is a
##' \code{\link[terra:rast]{SpatRaster}} (or several \code{\link[terra:rast]{SpatRaster}}
##' objects, if \code{new.env} is too large)
##' \item raw projections, as well as binary and filtered projections (if asked), are saved in
##' the \code{proj.name} folder
##' }
##'
##' @examples
##' library(terra)
##' library(biomod2)
##'
##' # Load species occurrences (6 species available)
##' data(DataSpecies)
##' myRespName <- 'GuloGulo'
##' myResp <- as.numeric(DataSpecies[, myRespName])
##' myRespXY <- DataSpecies[, c('X_WGS84', 'Y_WGS84')]
##'
##' # Load environmental variables extracted from BIOCLIM (bio_3, bio_4, bio_7, bio_11 & bio_12)
##' data(bioclim_current)
##' myExpl <- terra::rast(bioclim_current)
##'
##' \dontshow{
##' myExtent <- terra::ext(0,30,45,70)
##' myExpl <- terra::crop(myExpl, myExtent)
##' }
##' # ---------------------------------------------------------------#
##' # Data formating, modeling and ensemble modeling in one workflow
##' myWrap <- BIOMOD_Wrap(modeling.id = "Example",
##' data.type = "binary",
##' resp.name = myRespName,
##' resp.var = myResp,
##' resp.xy = myRespXY,
##' expl.var = myExpl,
##' models = c('RF', 'GLM'),
##' metric.eval = c("TSS", "ROC"),
##' params.CV = list(CV.strategy = 'random',
##' CV.nb.rep = 3,
##' CV.perc = 0.7),
##' params.OPT = list(OPT.strategy = "bigboss"),
##' em.algo = c('EMmean', 'EMca'),
##' params.EM = list(models.chosen = 'all',
##' em.by = 'all',
##' metric.select = 'TSS',
##' metric.select.thresh = 0.6),
##' var.import = 2,
##' seed.val = 42)
##'
##' myProjections <- BIOMOD_ProjectionWrap(bm.wrap = myWrap,
##' proj.name = "Current",
##' new.env = myExpl,
##' models.chosen = 'all')
##' # plot(myProjections)
##' \dontshow{
##' unlink('GuloGulo', recursive = TRUE)
##' }
##'
##' @importFrom biomod2 BIOMOD_Projection BIOMOD_EnsembleForecasting get_evaluations get_formal_data
##' @export
##'
##'
###################################################################################################
BIOMOD_ProjectionWrap <- function(bm.wrap,
proj.name,
new.env,
new.env.xy = NULL,
models.chosen = 'all',
metric.binary = NULL,
metric.filter = NULL,
compress = TRUE,
digits = 0,
build.clamping.mask = TRUE,
nb.cpu = 1,
seed.val = NULL,
...) {
## 0. Check arguments ---------------------------------------------------------------------------
args <- .BIOMOD_ProjectionWrap.check.args(bm.wrap, proj.name, new.env, new.env.xy
, models.chosen, metric.binary, metric.filter, compress, seed.val, ...)
for (argi in names(args)) { assign(x = argi, value = args[[argi]]) }
rm(args)
## 1. Create output object ----------------------------------------------------------------------
proj_out <- new('BIOMOD.projection.out',
proj.name = proj.name,
dir.name = [email protected]@dir.name,
sp.name = [email protected]@sp.name,
expl.var.names = [email protected]@expl.var.names,
#models.projected = models.chosen,
scale.models = [email protected]@scale.models,
coord = new.env.xy,
data.type = [email protected]@data.type,
modeling.id = [email protected]@modeling.id)
cat("\n")
cat("\n\t Projection of single models")
# output <- capture.output(
proj_single <- BIOMOD_Projection([email protected],
proj.name = proj.name,
new.env = new.env,
new.env.xy = new.env.xy,
models.chosen = models.chosen.single,
metric.binary = metric.binary,
metric.filter = metric.filter,
compress = compress,
build.clamping.mask = build.clamping.mask,
nb.cpu = nb.cpu,
digits = digits,
seed.val = seed.val,
omit.na = omit.na,
on_0_1000 = on_0_1000,
do.stack = do.stack,
keep.in.memory = keep.in.memory,
output.format = output.format,
overwrite = overwrite)
# )
cat("\n\t Projection of ensemble models")
# output <- capture.output(
proj_ens <- BIOMOD_EnsembleForecasting([email protected],
proj.name = proj.name,
new.env = new.env,
new.env.xy = new.env.xy,
models.chosen = models.chosen.ens,
metric.binary = metric.binary,
metric.filter = metric.filter,
compress = compress,
nb.cpu = nb.cpu,
digits = digits,
on_0_1000 = on_0_1000,
do.stack = do.stack,
keep.in.memory = keep.in.memory,
output.format = output.format)
# )
proj_out@type <- proj_single@type
if (proj_out@type == "SpatRaster"){
} else {
}
#.bm_cat("Done")
return(proj_out)
}
# .BIOMOD_ProjectionWrap.check.args---------------------------------------------
.BIOMOD_ProjectionWrap.check.args <- function(bm.wrap, proj.name, new.env, new.env.xy,
models.chosen, metric.binary, metric.filter, compress, seed.val, ...)
{
args <- list(...)
## 1. Check bm.wrap ----------------------------------------------------------
.fun_testIfInherits(TRUE, "bm.wrap", bm.wrap, "BIOMOD.wrap.out")
## 2. Check proj.name -------------------------------------------------------
if (is.null(proj.name)) {
stop("\nYou must define a name for Projection Outputs")
}
## 3. Check new.env ---------------------------------------------------------
.fun_testIfInherits(TRUE, "new.env", new.env, c('matrix', 'data.frame', 'SpatRaster','Raster'))
if (inherits(new.env, 'matrix')) {
if (any(sapply(get_formal_data([email protected], "expl.var"), is.factor))) {
stop("new.env cannot be given as matrix when model involves categorical variables")
}
new.env <- data.frame(new.env)
} else if (inherits(new.env, 'data.frame')) {
# ensure that data.table are coerced into classic data.frame
new.env <- as.data.frame(new.env)
}
if (inherits(new.env, 'Raster')) {
# conversion into SpatRaster
if (any(raster::is.factor(new.env))) {
new.env <- .categorical_stack_to_terra(raster::stack(new.env),
expected_levels = head(get_formal_data([email protected], subinfo = "expl.var"))
)
} else {
new.env <- rast(new.env)
}
}
if (inherits(new.env, 'SpatRaster')) {
.fun_testIfIn(TRUE, "names(new.env)", names(new.env), [email protected]@expl.var.names, exact = TRUE)
new.env <- new.env[[[email protected]@expl.var.names]]
new.env.mask <- .get_data_mask(new.env, value.out = 1)
new.env <- mask(new.env, new.env.mask)
} else {
.fun_testIfIn(TRUE, "colnames(new.env)", colnames(new.env), [email protected]@expl.var.names, exact = TRUE)
new.env <- new.env[ , [email protected]@expl.var.names, drop = FALSE]
}
which.factor <- which(sapply(new.env, is.factor))
if (length(which.factor) > 0) {
new.env <- .check_env_levels(new.env,
expected_levels = head(get_formal_data([email protected], subinfo = "expl.var")))
}
## 4. Check new.env.xy ------------------------------------------------------
if (!is.null(new.env.xy) & !inherits(new.env, 'SpatRaster')) {
new.env.xy = as.data.frame(new.env.xy)
if (ncol(new.env.xy) != 2 || nrow(new.env.xy) != nrow(new.env)) {
stop("invalid xy coordinates argument given -- dimensions mismatch !")
}
} else {
new.env.xy = data.frame()
}
## 5. Check models.chosen ---------------------------------------------------
if (length(models.chosen) == 1 && models.chosen == 'all') {
models.chosen.single <- "all"
models.chosen.ens <- "all"
} else {
models.chosen.single <- intersect(models.chosen, [email protected]@models.computed)
models.chosen.ens <- intersect(models.chosen, [email protected]@em.computed)
}
## 6. Check metric.binary & metric.filter -----------------------------------
if (!is.null(metric.binary) | !is.null(metric.filter)) {
if ( [email protected]@data.type != "binary"){
cat ("No metric.binary or metric.filter are needed with", [email protected]@data.type, "data")
metric.binary <- NULL
metric.filter <- NULL
} else {
models.evaluation <- biomod2::get_evaluations([email protected])
if (is.null(models.evaluation)) {
warning("Binary and/or Filtered transformations of projection not ran because of models evaluation information missing")
} else {
available.evaluation <- unique(models.evaluation$metric.eval)
if (!is.null(metric.binary) && metric.binary[1] == 'all') {
metric.binary <- available.evaluation
} else if (!is.null(metric.binary) && sum(!(metric.binary %in% available.evaluation)) > 0) {
warning(paste0(toString(metric.binary[!(metric.binary %in% available.evaluation)]),
" Binary Transformation were switched off because no corresponding evaluation method found"))
metric.binary <- metric.binary[metric.binary %in% available.evaluation]
}
if (!is.null(metric.filter) && metric.filter[1] == 'all') {
metric.filter <- available.evaluation
} else if (!is.null(metric.filter) && sum(!(metric.filter %in% available.evaluation)) > 0) {
warning(paste0(toString(metric.filter[!(metric.filter %in% available.evaluation)]),
" Filtered Transformation were switched off because no corresponding evaluation method found"))
metric.filter <- metric.filter[metric.filter %in% available.evaluation]
}
}
}
}
## 7. Check compress --------------------------------------------------------
if (compress == 'xz') {
compress <- ifelse(.Platform$OS.type == 'windows', 'gzip', 'xz')
}
## 9. Check output.format ---------------------------------------------------
output.format <- args$output.format # raster output format
if (!is.null(output.format)) {
if (!output.format %in% c(".img", ".grd", ".tif", ".RData")) {
stop(paste0("output.format argument should be one of '.img','.grd', '.tif' or '.RData'\n"
, "Note : '.img','.grd', '.tif' are only available if you give environmental condition as a SpatRaster object"))
}
if (output.format %in% c(".img", ".grd", ".tif") && !inherits(new.env, "SpatRaster")) {
warning("output.format was automatically set to '.RData' because environmental conditions are not given as a raster object")
}
} else {
output.format <- ifelse(!inherits(new.env, "SpatRaster"), ".RData", ".tif")
}
## 9. Check do.stack --------------------------------------------------------
do.stack <- ifelse(is.null(args$do.stack), TRUE, args$do.stack)
if (!inherits(new.env, 'SpatRaster')) {
if (!do.stack) {
cat("\n\t\t! 'do.stack' arg is always set as TRUE for data.frame/matrix dataset")
}
do.stack <- TRUE
} else if (do.stack) {
# test if there is enough memory to work with multilayer SpatRaster
capture.output({
test <-
mem_info(
subset(new.env, 1),
n = 2 * length(models.chosen[1]) + nlyr(new.env)
)
})
if (test["needed"] >= test["available"]) {
terraOptions(todisk = TRUE)
}
} else if (output.format == "RData"){
cat("\n\t\t! 'do.stack' arg is always set as TRUE for .RData output format")
do.stack <- TRUE
}
## 10.on_0_1000 --------------------------------
on_0_1000 <- ifelse(is.null(args$on_0_1000), TRUE, args$on_0_1000)
if ([email protected]@data.type %in% c("count","abundance","ordinal")) {on_0_1000 <- FALSE}
## 11.Check overwrite
overwrite <- ifelse(is.null(args$overwrite), ifelse(do.stack, TRUE, FALSE), args$overwrite)
if (!overwrite){
cat("\n\t\t! 'overwrite' arg is set as FALSE. Projections that have already been saved will not be redone.
Please be carfeul if you have changed the models in the meantime. ")
}
return(list(proj.name = proj.name,
new.env = new.env,
new.env.xy = new.env.xy,
models.chosen.single = models.chosen.single,
models.chosen.ens = models.chosen.ens,
metric.binary = metric.binary,
metric.filter = metric.filter,
compress = compress,
do.stack = do.stack,
output.format = output.format,
omit.na = ifelse(is.null(args$omit.na), TRUE, args$omit.na),
do.stack = do.stack,
keep.in.memory = ifelse(is.null(args$keep.in.memory), TRUE, args$keep.in.memory),
on_0_1000 = on_0_1000,
seed.val = seed.val,
overwrite = overwrite))
}