forked from danlwarren/ENMTools
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathReadme.Rmd
More file actions
401 lines (266 loc) · 22.5 KB
/
Readme.Rmd
File metadata and controls
401 lines (266 loc) · 22.5 KB
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
ENMTools
======================
This package implements various tests, visualizations, and metrics for use with environmental niche models (ENMs) and species distribution models (SDMs).
-----
# Installation
At present, ENMTools is downloadable from https://github.com/danlwarren/ENMTools. There are multiple ways to download it. The easiest is to use devtools and install from GitHub.
### Installing from GitHub using devtools
Run the following code from your R console:
```{r git_install, eval=FALSE}
install.packages("devtools")
library(devtools)
install_github("danlwarren/ENMTools")
library(ENMTools)
```
### Install from zip file
A zipped version of the package is available at https://github.com/danlwarren/ENMTools/archive/master.zip. To install from the zip file, download a copy of it to your system. Once it's finished downloading, type the following (where PATH is the path to the zip file):
```{r zip_install, eval=FALSE}
install.packages("devtools")
library(devtools)
install_local("PATH")
library(ENMTools)
```
```{r load_enmtools, include=FALSE}
library(ENMTools)
```
-----
# Interacting with ENMTools
### Creating enmtools.species objects
First we're going to load in some environmental data.
```{r read_files}
env.files <- list.files(path = "test/testdata/", pattern = "pc", full.names = TRUE)
env <- stack(env.files)
names(env) <- c("layer.1", "layer.2", "layer.3", "layer.4")
env <- setMinMax(env)
```
ENMTools is primarily designed to examine patterns of similarity and difference between ENMs for different species. In order to simplify interactions with the functions in ENMTools, you need to put your data for each of your species into an enmtools.species object. You can create and view an empty enmtools.species object just by typing:
```{r empty_species}
ahli <- enmtools.species()
ahli
```
You can add data to this object manually:
```{r build_ahli}
names(ahli)
ahli$species.name <- "ahli"
ahli$presence.points <- read.csv("test/testdata/ahli.csv")[,3:4]
ahli$range <- background.raster.buffer(ahli$presence.points, 50000, mask = env)
ahli$background.points <- background.points.buffer(points = ahli$presence.points,
radius = 20000, n = 1000, mask = env[[1]])
ahli
```
Or you can add bits of it when the object is created:
```{r build_allogus}
allogus <- enmtools.species(species.name = "allogus",
presence.points = read.csv("test/testdata/allogus.csv")[,3:4])
allogus$range <- background.raster.buffer(allogus$presence.points, 50000, mask = env)
allogus$background.points <- background.points.buffer(points = allogus$presence.points,
radius = 20000, n = 1000, mask = env[[1]])
allogus
```
## Building an ENM
ENMTools contains functions to simplify the ENM construction process. Using enmtools.species objects and the correct modeling commands, we can build models very quickly. These commands are primarily wrappers to dismo model construction and projection functions, and at present are only available for GLM, Maxent, Domain, and Bioclim models. One of the nice bits about this setup is that it allows enmtools to automatically generate suitability maps, do model evaluation, and plot the marginal suitability of habitat for each variable separately.
### GLM
GLMs usually require the user to supply a formula, an enmtools.species object, and some environmental data. If your formula is a strictly additive function of all of the environmental layers in env, though, enmtools.glm will build a formula automatically.
```{r build_glms}
ahli.glm <- enmtools.glm(species = ahli, env = env, f = pres ~ layer.1 + layer.2 + layer.3 + layer.4, test.prop = 0.2)
allogus.glm <- enmtools.glm(species = allogus, env = env, f = pres ~ layer.1 + layer.2 + layer.3 + layer.4, test.prop = 0.2)
ahli.glm
```
Notice this produces the same formula as:
```{r build_glms2}
ahli.glm <- enmtools.glm(species = ahli, env = env, test.prop = 0.2)
ahli.glm
```
If you want a more complicated formula, though (e.g., with interactions or polynomial effects), you'll need to supply that manually.
```{r build_glms3}
ahli.glm <- enmtools.glm(species = ahli, env = env, f = pres ~ poly(layer.1, 2) + poly(layer.2, 2) + poly(layer.3, 2) + poly(layer.4, 2), test.prop = 0.2)
ahli.glm
```
To check out the marginal response functions, you only need to type
```{r response_plots}
ahli.glm$response.plots
```
You can also visualize your models and data in a 2D environment space using any pair of layers from your environment stack. These plots hold all non-plotted variables (layer.1 and layer.3 in this case) constant at their mean value across all presence points, then vary the plotted variables between the minimum and maximum values in env.
The suit.plot shows you suitability in environment space as a function of your two variables, with brighter colors representing variable combinations predicted to be more suitable. The points represent the occurrence points for your species in that environment space.
The colored raster of the background.plot shows you the density of background points in environment space, while the white points again represent your occurrence points in environment space.
```{r visualize.enm}
visualize.enm(ahli.glm, env, layers = c("layer.2", "layer.4"))
```
### GAM, Bioclim, Domain, and Maxent
The procedure for building Bioclim, Domain, and Maxent models is similar to the procedure for GLMs, with the exception that you do not need to pass a formula to the model function. Note that running Maxent models requires a bit of extra setup; see dismo documentation for details.
```{r eval = FALSE}
ahli.gam <- enmtools.gam(ahli, env, test.prop = 0.2)
ahli.dm <- enmtools.dm(ahli, env, test.prop = 0.2)
ahli.bc <- enmtools.bc(ahli, env, test.prop = 0.2)
ahli.mx <- enmtools.maxent(ahli, env, test.prop = 0.2)
```
## Metrics: breadth, correlation, and overlap
ENMTools provides a number of metrics for ENMs and for similarities between ENMs. These include measures of niche breadth, based on Levins(1968). An important caveat when interpreting these metrics is that they are driven to some (variable) extent by the availability of different combinations of environmental variables. As such they are more accurately interpreted as a measurment of the smoothness of the geographic distribution of suitability scores than as an estimate of the breadth of the fundamental niche; an organism with narrow fundamental niche breadth that nonetheless encompasses a set of environmental conditions that is quite common will have a high breadth when measured using ENMs, while having a low breadth in environment space.
```{r raster_breadth}
raster.breadth(ahli.glm)
```
ENMTools also provides metrics for measuring similarity between ENMs. These include Schoener's D (Schoener 1968), I (Warren et al. 2008), and the Spearman rank correlation coefficient between two rasters. While D and I are commonly used in the ENM literature, they may tend to overestimate similarity between ENMs when many grid cells are of similar values (e.g., when two species prefer different habitat but the region contains a great deal of habitat that is unsuitable for both).
```{r raster_overlap}
raster.overlap(ahli.glm, allogus.glm)
```
A new feature of the R version of ENMTools is that you can now use these same metrics in the n-dimensional space of all combinations of environmental variables, instead of restricting your measures of model similarity to those sets of conditions that appear in the training region. This is done by repeatedly drawing Latin hypercube samples from the space of all possible combinations of environmental variables given the min and max of each variable within the training region. ENMTools continues to draw samples until subsequent iterations differ by less than a specified tolerance value. Lower tolerance values result in more precise estimates of overlap, but can take much longer to calculate.
```{r env_overlap}
env.overlap(ahli.glm, allogus.glm, env, tolerance = .001)
```
## Hypothesis testing
### Niche identity or equivalency test
In this example, we will run a niche identity (also called equivalency) test, as in Warren et al. 2008. This test takes the presence points for a pair of species and randomly reassigns them to each species, then builds ENMs for these randomized occurrences. By doing this many times, we can estimate the probability distribution for ENM overlap between species under the null hypothesis that the two species' occurrences in the environment are effectively a random draw from the same underlying distribution. Note that niche evolution is only one of many reasons why two species' realized environmental distributions might cause departures from this null hypothesis. See Warren et al. 2014 for details.
To run an identity test, we need to decide what type of models we will build, how many replicates we will run, and (in the case of GLM) a model formula to use for empirical models and the Monte Carlo replicates. The resulting object contains the replicate models, p values, and plots of the results. Typically idenity tests are run with at least 99 replicates, but we are using a smaller number here for the sake of execution time.
_NOTE:_ In order for models to be comparable, both empirical and pseudoreplicate models for the identity test are conducted with pseudoabsence points pooled for the two species being compared.
```{r id_glm, include = FALSE}
id.glm <- identity.test(species.1 = ahli, species.2 = allogus, env = env, type = "glm", nreps = 4)
```
```{r eval = FALSE}
id.glm <- identity.test(species.1 = ahli, species.2 = allogus, env = env, type = "glm", nreps = 4)
```
```{r, warnings = FALSE, fig.width = 12, fig.height=8}
id.glm
```
### Background or similarity test
The background or similarity test compares the overlap seen between two species' ENMs to the overlap expected by chance if one or both species was effectively choosing habitat at random from within their broad geographic range. The purpose of this test is to correct for the availability of habitat and ask whether the observed similarity between species or populations is significantly more (or less) than expected given the available set of environments in the regions in which they occur.
_NOTE:_ In order for models to be comparable, both empirical and pseudoreplicate models for the background test are conducted with pseudoabsence points pooled for the two species being compared.
In Warren et al. 2008, we developed this test in the context of comparing one species' actual occurrence to the random background occurrences of the other species. This is what we call an "asymmetric" test, and in our case we did the test in both directions with the idea that we might compare the results of A vs. B background to the results of B vs. A background. This may be informative in some cases, but many people have also found this asymmetry confusing (and indeed it is often difficult to interpret). For that reason, the background test here can be conducted against a null hypothesis that is generated from "asymmetric" (species.1 vs species.2 background) or "symmetric" (species.1 background vs. species.2 background) comparisons.
Here, for instance, is a Bioclim background test using the classical asymmetric approach:
```{r bg_bc_asym, include = FALSE}
bg.bc.asym <- background.test(species.1 = ahli, species.2 = allogus, env = env, type = "bc", nreps = 4, test.type = "asymmetric" )
```
```{r eval = FALSE}
bg.bc.asym <- background.test(species.1 = ahli, species.2 = allogus, env = env, type = "bc", nreps = 4, test.type = "asymmetric" )
```
```{r, warnings = FALSE, fig.width = 12, fig.height=8}
bg.bc.asym
```
And here is a Domain background test using the symmetric approach:
```{r bg_dm_sym, include = FALSE}
bg.dm.sym <- background.test(species.1 = ahli, species.2 = allogus, env = env, type = "dm", nreps = 4, test.type = "symmetric" )
```
```{r eval = FALSE}
bg.dm.sym <- background.test(species.1 = ahli, species.2 = allogus, env = env, type = "dm", nreps = 4, test.type = "symmetric" )
```
```{r, warnings = FALSE, fig.width = 12, fig.height=8}
bg.dm.sym
```
### Ecospat tests
Using enmtools.species objects also provides a simplified interface to the niche equivalency and similarity tests (or identity and background tests, respectively) that were developed by Broennimann et al. (2012). These tests do not rely on ENMs, instead using kernel density smoothing to estimate density of the species in environment space. Ecospat also uses the density of the available environment to correct for availability when measuring overlaps, so that overlaps are not strictly driven by availability of combinations of environmental variables.
These tests only work with two environmental axes, so they are often done with the top two PC axes of a set of environments. In our case we'll just pick a couple of environmental layers, though (layer.1 and layer.2). Here's an equivalency/identity test:
```{r ecospat_identity, fig.width = 12, fig.height=8}
esp.id <- enmtools.ecospat.id(ahli, allogus, env[[c("layer.1", "layer.2")]])
esp.id
```
And here's a symmetric background test. The difference between symmetric and asymmetric for these tests is the same as for the background tests presented above.
```{r ecospat_background, fig.width = 12, fig.height=8}
esp.bg.sym <- enmtools.ecospat.bg(ahli, allogus, env[[c("layer.1", "layer.3")]], test.type = "symmetric")
esp.bg.sym
```
### Rangebreak tests
ENMTools also allows you to perform linear, blob, and ribbon rangebreak tests as developed in Glor and Warren 2011. The linear and blob tests are two versions of a test that permit one to ask whether the geographic regions occupied by two species are more environmentally different than expected by chance. The ribbon test, meanwhile, is designed to test whether the ranges of two species are divided by a region that is relatively unsuitable to one or both forms.
For the linear and blob tests, you call them very much like you would the identity and background tests. Here's a linear one using GLM models:
```{r rangebreak_linear}
rbl.glm <- rangebreak.linear(ahli, allogus, env, type = "glm", nreps = 4)
rbl.glm
```
And here's a blob test using Bioclim:
```{r rangebreak_blob}
rbb.bc <- rangebreak.blob(ahli, allogus, env, type = "bc", nreps = 4)
rbb.bc
```
If you want to access the individual replicates (for instance to see how your ranges are being split up), you can find them in the list named "replicate.models" inside your rangebreak test object.
```{r rbl_reps}
rbl.glm$replicate.models$ahli.rep.1
rbl.glm$replicate.models$allogus.rep.1
```
For the ribbon rangebreak test, you will need one extra thing; a third enmtools.species object representing the occurrence points (for one or both species) that fall within the ribbon of putatively unsuitable habitat. In the case of these two anoles we don't have such a ribbon, so we'll just simulate one based on some random points.
```{r build_ribbon}
ribbon <- enmtools.species(species.name = "ribbon")
ribbon$presence.points <- data.frame(Longitude = runif(n = 10, min = -79, max = -78.5),
Latitude = runif(n = 10, min = 21.7, max = 22.1))
plot(env[[1]])
points(ribbon$presence.points, pch = 16)
ribbon$range <- background.raster.buffer(ribbon$presence.points, 20000, mask = env)
ribbon
```
Now we'll run a ribbon rangebreak test using GLM models with quadratic effects. We also need to tell it the width of the ribbons to generate for the replicates. The units for the width argument are the same units that the presence points are in; e.g., if the points are in decimal degrees you should supply the width of the barrier in decimal degrees.
```{r rangebreak_ribbon, warnings = FALSE, fig.width = 12, fig.height=8}
rbr.glm <- rangebreak.ribbon(ahli, allogus, ribbon, env, type = "glm", f = pres ~ poly(layer.1, 2) + poly(layer.2, 2) + poly(layer.3, 2) + poly(layer.4, 2), width = 0.5, nreps = 4)
rbr.glm
```
Note that the output table here has slope, intercept, and intercept offset.
```{r ribbon_df}
rbr.glm$lines.df
```
The intercept denotes the intercept corresponding to the CENTER of each ribbon. To get the lines denoting the edges of the ribbons (for example if you want to plot the ribbons on a map), you add and substract the offset. In other words, the top edge of the ribbon is given by y = (slope * x) + intercept + offset, while the bottom edge is given by y = (slope * x) + intercept - offset.
### Building an enmtools.clade object
Some of the tests in ENMTools, including some really neat ones that are still in development, require you to build an enmtools.clade object. These objects are simply lists that contain a phylogeny and a set of enmtools.species objects. It's important that the names of the species objects and their species.name attributes match the names in the phylogeny's tip.labels. For demonstration, we're going to build an object for a clade of five anoles from Hispaniola. We have the tree, so we're just going to grab occurrence data from GBIF using the rgbif package.
```{r read_tree}
library(rgbif)
library(ape)
hisp.anoles <- read.nexus(file = "test/testdata/StarBEAST_MCC.species.txt")
keepers <- c("brevirostris", "marron", "caudalis", "websteri", "distichus")
hisp.anoles <- drop.tip(phy = hisp.anoles, tip = hisp.anoles$tip.label[!hisp.anoles$tip.label %in% keepers])
plot(hisp.anoles)
```
So there's our tree. Now we're going to grab some environmental data.
```{r read_env}
hisp.env <- stack(list.files("test/testdata/Hispaniola_Worldclim", full.names = TRUE))
hisp.env <- setMinMax(hisp.env)
```
And then we'll create a function to build species from GBIF.
```{r gbif_function}
# Automate the process of downloading data and removing duds and dupes
species.from.gbif <- function(genus, species, name = NA, env){
# Name it after the species epithet unless told otherwise
if(is.na(name)){
name <- species
}
# Get GBIF data
this.sp <- enmtools.species(presence.points = gbif(genus = genus, species = species)[,c("lon", "lat")],
species.name = name)
# Rename columns, get rid of duds
colnames(this.sp$presence.points) <- c("Longitude", "Latitude")
this.sp$presence.points <- this.sp$presence.points[complete.cases(extract(env, this.sp$presence.points)),]
this.sp$presence.points <- this.sp$presence.points[!duplicated(this.sp$presence.points),]
this.sp$range <- background.raster.buffer(this.sp$presence.points, 50000, mask = hisp.env)
return(this.sp)
}
```
Now we'll create five species and add them to a species.clade object that is called brev.clade.
```{r build_clade, message = FALSE, warning = FALSE}
brevirostris <- species.from.gbif(genus = "Anolis", species = "brevirostris", env = hisp.env)
marron <- species.from.gbif(genus = "Anolis", species = "marron", env = hisp.env)
caudalis <- species.from.gbif(genus = "Anolis", species = "caudalis", env = hisp.env)
websteri <- species.from.gbif(genus = "Anolis", species = "websteri", env = hisp.env)
distichus <- species.from.gbif(genus = "Anolis", species = "distichus", env = hisp.env)
brev.clade <- enmtools.clade(species = list(brevirostris, marron, caudalis, websteri, distichus), tree = hisp.anoles)
check.clade(brev.clade)
```
### Age-overlap correlation tests (AOC)
The AOC tests allow you to examine patterns of range, point, and ENM overlap in the context of a phylogeny. This is effectively a generalized version of several analyses: age-range correlation (e.g., Fitzpatrick and Turelli 2006), ENM overlap in the context of a phylogeny (e.g., Knouft et al. 2006, Warren et al. 2008), and point overlaps (e.g., Cardillo and Warren 2016).
These tests require the creation of an enmtools.clade object, as above. AOC tests consist of two steps: first, the average overlap at each node in the phylogeny is calcualted using a method that takes tree topology into account (see Fitzpatrick and Turelli 2006), then we perform a linear regression to measure the relationship between node age and average overlap. Due to the fact that these overlaps violate many of the assumptions of a regular linear regression, however (e.g., errors are not iid), we can't calculate significance in the typical way. Instead we performa Monte Carlo test, permuting the identity of the tips of the tree and repeating the node averaging and modeling steps. Finally we measure statistical significance by comparing the empirical slope and intercept to the distribution of slopes and intercepts from the Monte Carlo replicates.
First, let's do one using geog.range.overlaps, as in Fitzpatrick and Turelli 2006. Note that this analysis requires that each of your species have a range raster stored in their species object (we did that as part of the function used above).
```{r range_aoc}
range.aoc <- enmtools.aoc(clade = brev.clade, nreps = 50, overlap.source = "range")
summary(range.aoc)
```
Now we can do one using point overlaps just by changing the overlap.source argument:
```{r point_aoc}
point.aoc <- enmtools.aoc(clade = brev.clade, nreps = 50, overlap.source = "points")
summary(point.aoc)
```
Or we can use similarity between ENMs built for each species. Here we'll use GLM models:
```{r enm_aoc}
glm.aoc <- enmtools.aoc(clade = brev.clade, env = hisp.env, nreps = 50, overlap.source = "glm", f = pres ~ snv_1 + snv_12)
summary(glm.aoc)
```
### Literature cited
*Broennimann, O., Fitzpatrick, M. C., Pearman, P. B., Petitpierre, B., Pellissier, L., Yoccoz, N. G., Thuiller, W., Fortin, M.-J., Randin, C., Zimmermann, N. E., Graham, C. H. and Guisan, A. (2012), Measuring ecological niche overlap from occurrence and spatial environmental data. Global Ecology and Biogeography, 21: 481–497. doi:10.1111/j.1466-8238.2011.00698.x*
*Fitzpatrick, B. M., & Turelli, M. (2006). The geography of mammalian speciation: mixed signals from phylogenies and range maps. Evolution, 60(3), 601-615.*
*Knouft, J. H., Losos, J. B., Glor, R. E., & Kolbe, J. J. (2006). Phylogenetic analysis of the evolution of the niche in lizards of the Anolis sagrei group. Ecology, 87(sp7).*
*Levins, R. 1968. Evolution In Changing Environments. Monographs in Population Biology, volume 2. Princeton University Press, Princeton, New Jersey, USA.*
*Schoener, T. W. 1968. Anolis lizards of Bimini: resource partitioning in a complex fauna. Ecology 49:704- 726.*
*Warren, D.L., R.E. Glor, and M. Turelli. 2008. Environmental niche identity versus conservatism: quantitative approaches to niche evolution. Evolution 62:2868-2883. doi: 10.1111/j.1558-5646.2008.00482.x*
*Warren, D.L., M. Cardillo, D.F. Rosauer, and D.I. Bolnick. 2014. Mistaking geography for biology: inferring processes from species distributions. Trends in Ecology and Evolution 29 (10), 572-580. doi: 10.1016/j.tree.2014.08.003*