forked from SCAR/EGABIcourse19
-
Notifications
You must be signed in to change notification settings - Fork 0
/
xx_SDMTune.Rmd
314 lines (251 loc) · 11.5 KB
/
xx_SDMTune.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
# Modelos de distribucion de especies con `SDMTune`
Los modelos de distribucion de especie requieren de una serie de pasos para su correcta implementacion. En este documento se muestra un ejemplo de como realizar un modelo de distribucion de especies con la ayuda de `SDMTune`.
## Llamando paquetes relevantes
```{r}
#Manipulacion de datos
library(dplyr)
library(tidyr)
library(stringr)
library(lubridate)
library(tibble)
library(purrr)
#Taxonomia
library(worrms)
#Datos de ocurrencia desde OBIS
library(robis)
#Limpieza de datos de occurrencia
library(CoordinateCleaner)
#Datos ambientales
library(blueant)
#Modelos de distribucion de especie
library(SDMtune)
#Datos en grilla (rasters)
library(terra)
#Graficar datos (incluyendo mapas)
library(ggplot2)
library(rnaturalearth)
library(tidyterra)
```
## Taxonomía y ocurrencia
Revisamos la taxonomía de la especie de interés, *Euphausia superba* (Krill antártico) usando `worrms`, y obtenemos los registros de ocurrencia de la misma con `robis`.
```{r occ, message = FALSE}
#Revisar records de specie de interes en WoRMS
especie_interes <- wm_records_names(name = "Euphausia superba") |>
as.data.frame() |>
#Seleccionar AphiaID
pull(AphiaID) |>
#Usar AphiaID en busqueda de ocurrencias en OBIS
occurrence(taxonid = _)
```
## Limpieza de datos
Vamos a limpiar los datos de ocurrencia, eliminando registros problemáticos, que incluyen coordenadas inválidas, duplicados o ubicaciones erróneas (por ejemplo, registros en áreas tropicales).
```{r limp_occ, message = FALSE}
#Limpiando datos de occurrencia
especie_interes_limpios <- especie_interes |>
#Removiendo ocurrencias de especimenes preservados
filter(basisOfRecord != "PreservedSpecimen") |>
#Removemos ocurrencias sin fechas
drop_na(eventDate) |>
#Removiendo ocurrencias con valores de "coordinate uncertainty" asociados a
#errores
filter(!coordinateUncertaintyInMeters %in% c(301, 3036, 999, 9999)) |>
#Removiendo ocurrencias con poca precision
filter(coordinateUncertaintyInMeters <= 10000 |
is.na(coordinateUncertaintyInMeters)) |>
#Removiendo ocurrencias con coordenadas que tienen problemas
clean_coordinates(lon = "decimalLongitude", lat = "decimalLatitude") |>
cd_ddmm(lon = "decimalLongitude", lat = "decimalLatitude",
ds = "dataset_id") |>
#Removiendo duplicados
cc_dupl(lon = "decimalLongitude", lat = "decimalLatitude",
additions = c("year", "month", "day"))
#Revisando resultados
summary(especie_interes_limpios)
```
Ahora podemos remover las observaciones identificadas como problemáticas.
```{r}
especie_interes_limpios <- especie_interes_limpios |>
#Removiendo filas con latitud y longitud iguales
filter(`.equ` != F) |>
#Removiendo filas donde una coordenada tiene el valor exactamente cero
filter(`.zer` != F) |>
#Manteniendo solo records en el mar
filter(`.sea` == F)
#Finalmente transformamos la columna eventDate en formato fecha
especie_interes_limpios <- especie_interes_limpios |>
mutate(eventDate = as_date(eventDate)) |>
#Las fechas incompletas no son reconocidas por as_date, por lo que las
#eliminamos
drop_na(eventDate)
#Revisemos nuevamente los resultados
summary(especie_interes_limpios)
```
Ahora no tenemos registros duplicados, ni registros con coordenadas problemáticas. Vamos a crear un gráfico de la distribución de las muestras limpias de *Euphausia superba*.
```{r map1, message = FALSE}
#Cargando un mapa de la Antartida
antartida <- ne_countries(scale = "medium", returnclass = "sf",
continent = "Antarctica")
ggplot()+
geom_sf(data = antartida, fill = "grey")+
geom_point(data = especie_interes_limpios,
aes(x = decimalLongitude, y = decimalLatitude),
color = "blue")
```
## Datos ambientales
Vamos a utilizar los datos ambientales que están disponibles a través de `blueant` para ajustar un modelo de distribución de especies.
```{r var_amb, message = FALSE}
#Creamos un directory temporal para guardar los datos ambientales
mi_directorio <- tempdir()
#Buscamos marinos datos del Oceano del Sur
fuente_datos <- sources("Southern Ocean marine environmental data")
#Guardamos los resultados en nuestro directorio temporal
resultados <- bb_get(fuente_datos, local_file_root = mi_directorio,
verbose = TRUE)
#Podemos ver los resultados
resultados$files
```
Algunos archivos no son grillas, así que seleccionamos solo los archivos netCDF. Luego subiremos los archivos netCDF a un objeto `SpatRaster` para poder trabajar con ellos.
Vamos a ajustar un modelo de distribución de especies (presencia/ausencia), así que primero reorganizaremos nuestros datos de presencia/ausencia por sitio de muestreo:
```{r dat1}
#Detectamos los archivos netCDF
predictores <- str_detect(resultados$files[[1]]$file, ".nc$") |>
which() |>
#Subimos como rasters cada archivo - Esto forma una lista
map(~rast(resultados$files[[1]]$file[.x])) |>
#Transformamos la lista en un objeto SpatRaster
rast()
#Podemos verificar los nombres de las variables
names(predictores)
```
Tenemos 58 variables disponibles, pero siempre es buena idea revisar las variables antes de ajustar un modelo, y utilizar solamente variables que sean relevantes para la especie de interés. En este caso, vamos a seleccionar solo las variables de profundidad, clorofila, temperatura, y hielo.
```{r envdat1, message = FALSE}
predictores <- subset(predictores, c("depth", "chla_mean_alltime_2005_2012",
"seafloor_temp_2005_2012_mean",
"ice_cover_mean", "ice_thickness_mean",
"slope"))
#Verificamos nuestras variables
predictores
```
Podemos notar que los datos ambientales tienen información entre el 2005 y el 2012, por lo que filtraremos los datos de krill entre estos años.
```{r envdat2, message = FALSE}
especie_interes_limpios <- especie_interes_limpios |>
filter(eventDate >= "2005-01-01" & eventDate <= "2012-12-31")
#Podemos crear un mapa de distribucion de estos datos
ggplot()+
geom_sf(data = antartida, fill = "grey")+
geom_point(data = especie_interes_limpios,
aes(x = decimalLongitude, y = decimalLatitude),
color = "blue")
```
Hemos perdido datos en gran parte de la Antártida, pero nos concentraremos en explorar la distribución de *Euphausia superba* en las Península Antártica (entre longitudes $30^{\circ}$ y $80^{\circ}$ oeste) donde se encuentran la mayoría de nuestros datos.
```{r envdat3}
especie_interes_limpios <- especie_interes_limpios |>
filter(decimalLongitude >= -80 & decimalLongitude <= -30)
#Aplicamos el mismo rango a los datos ambientales
predictores <- crop(predictores, ext(-80, -30, -75, -50))
#Ahora pongamos todo junto en un mismo mapa
ggplot()+
geom_spatraster(data = predictores$depth)+
geom_sf(data = antartida, fill = "grey")+
geom_point(data = especie_interes_limpios,
aes(x = decimalLongitude, y = decimalLatitude),
color = "red")+
lims(x = c(-80, -30))
```
## Creando puntos de background
Obtengamos todos los datos disponibles de la familia *Euphausiacea* de Antártica desde el conjunto de datos obtenidos en las expediciones "recursos marinos antárticos alemanes": [Antarctic Euphausiacea occurence data from "German Antarctic Marine Living Resources" (GAMLR) Expeditions](https://ipt.biodiversity.aq/resource?r=gamlr). Utilizaremos las ubicaciones de otras especies dentro de esta familia como puntos de background.
```{r bg, message = FALSE}
puntos_bg <- occurrence(datasetid = "cb16377b-56a8-4d95-802d-4eec02466773") |>
#Removemos a las observaciones de Euphasia superba
filter(scientificName != "Euphausia superba") |>
filter(decimalLongitude >= -80 & decimalLongitude <= -30) |>
select(decimalLongitude, decimalLatitude) |>
rename(x = decimalLongitude, y = decimalLatitude)
puntos_bg
```
## Preparando ubicaciones de occurrencia y background
```{r}
presencia <- especie_interes_limpios |>
select(decimalLongitude, decimalLatitude) |>
rename(x = decimalLongitude, y = decimalLatitude)
#Unimos los datos de presencia y background con datos ambientales usando
#el paquete sSDMtune
datos_modelos <- prepareSWD(species = "Euphasia superba",
p = presencia, a = puntos_bg,
env = predictores)
```
## Ajustando el modelo
Vamos a dividir los datos entre entrenamiento y prueba antes de ajustar el modelo. En este caso usaremos Maxnet.
```{r mod1, message = FALSE}
#Nota que usamos "seed" para escoger exactamente los mismos datos cada vez que
#corramos el modelo
datos_dividos <- trainValTest(datos_modelos, test = 0.2,
only_presence = TRUE, seed = 25)
datos_entrenar <- datos_dividos[[1]]
datos_prueba <- datos_dividos[[2]]
#Ahora entrenamos el modelo basico
modelo_maxnet <- train(method = "Maxnet", data = datos_entrenar)
#Podemos verificar que hiperparametros que podemos ajustar
getTunableArgs(modelo_maxnet)
```
Ahora podemos definir los hiperparámetros que queremos ajustar.
```{r mod2, message = FALSE}
hiper <- list(reg = seq(0.5, 3, 0.5),
fc = c("lq", "lh", "lqp", "lqph", "lqpht"))
#Ajustamos el modelo varias veces y encontramos el que tiene el mejor desempeño
hiper_busqueda <- gridSearch(modelo_maxnet, hypers = hiper, metric = "auc",
test = datos_prueba)
#Podemos ver los resultados
hiper_busqueda@results |>
rowid_to_column("id") |>
#Ordenados por su desempeño
arrange(test_AUC) |>
head()
```
Ahora podemos hacer mapas de predicciones con el mejor modelo que sería el que tiene el mejor desempeño (el número 28 en la lista de arriba).
```{r}
#Creamos un mapa de predicciones
pred <- predict(hiper_busqueda@models[[28]],
data = predictores,
type = "cloglog")
#Mapa de Sudamerica
sudam <- ne_countries(scale = "medium", returnclass = "sf",
continent = "South America")
#Creamos el mapa final de distribucion de *Euphausia superba*
ggplot()+
geom_spatraster(data = pred)+
geom_sf(data = antartida, fill = "grey")+
geom_sf(data = sudam, fill = "grey")+
lims(x = c(-80, -30), y = c(-85, -50))+
geom_point(data = presencia, aes(x = x, y = y), color = "red", size = 1,
alpha = 0.2)
```
Al parecer *Euhasia superba* se encuentra en la Península Antártica y en la región de las Islas Malvinas, lo cual no se ve muy probable. Vamos a evaluar los modelos.
## Evaluación del modelo
Con `SDMtune` podemos evaluar el desempeño del modelo de distribución de especies.
```{r eval}
auc(modelo_maxnet); auc(hiper_busqueda@models[[28]])
```
En este caso el modelo de base tiene mejores resultados. Podemos también crear curvas de ROC.
```{r eval2}
plotROC(modelo_maxnet)
```
```{r eval3}
plotROC(hiper_busqueda@models[[28]])
```
Crearemos un mapa con el mejor modelo para comparar resultados.
```{r}
#Creamos un mapa de predicciones
pred_base <- predict(modelo_maxnet,
data = predictores,
type = "cloglog")
#Creamos el mapa final de distribucion de *Euphausia superba*
ggplot()+
geom_spatraster(data = pred_base)+
geom_sf(data = antartida, fill = "grey")+
geom_sf(data = sudam, fill = "grey")+
lims(x = c(-80, -30), y = c(-85, -50))+
geom_point(data = presencia, aes(x = x, y = y), color = "red", size = 1,
alpha = 0.2)
```
Estos resultados son más consistentes con la distribución de *Euphausia superba* en la Antártida.