Data analysis in R: linking species distribution data to ... · PDF fileThe niche concept 2/90...

Preview:

Citation preview

Ecologie et Dynamique des Systèmes Anthropisés FRE 3498 CNRS-UPJV www.u-picardie.fr/edysan

Photo: Jonathan Lenoir

5th IRSAE Summer School – Bø – 04-08/08/2014

Data analysis in R: linking species distribution data to climatic data

Niche & Distribution

Niche & Distribution Data Presence-Absence Presence-Only 1/90

htt

p:/

/sci

ence

asa

verb

.file

s.w

ord

pre

ss.c

om

/20

10

/10

/tw

o_v

ar_

exp

erim

ent.

pn

g

The niche concept

2/90

Key papers (Pulliam, 2000; Soberon, 2007; Colwell & Rangel, 2009)

Niche & Distribution Data Presence-Absence Presence-Only

Climatic gradient 1

The niche concept

3/90

The Hutchinson’s “fundamental niche” (Pulliam, 2000)

= 1.0

Presence

Absence

Clim

atic

gra

die

nt

2

Focal species distribution

Niche & Distribution Data Presence-Absence Presence-Only

Climatic gradient 1

= 1.0

The niche concept

4/90

The Hutchinson’s “realized niche” (Pulliam, 2000)

Presence

Absence

Clim

atic

gra

die

nt

2

Focal species distribution

Dominant competitor

Niche & Distribution Data Presence-Absence Presence-Only

Climatic gradient 1

= 1.0

The niche concept

5/90

The dispersal limitation effect (Pulliam, 2000)

Presence

Absence

Clim

atic

gra

die

nt

2

Focal species distribution

Dominant competitor

Niche & Distribution Data Presence-Absence Presence-Only

Climatic gradient 1

= 1.0

The niche concept

6/90

The source-sink dynamic effect (Pulliam, 2000)

Presence

Absence

Clim

atic

gra

die

nt

2

Focal species distribution

Dominant competitor

Source

Sink

Niche & Distribution Data Presence-Absence Presence-Only

The niche concept

7/90

Modified from Soberon (2007) & Anderson (2013)

Niche & Distribution Data Presence-Absence Presence-Only

Assisted Migration

The niche concept

8/90

Lessons from biological invasions (Guisan et al., 2014)

Niche & Distribution Data Presence-Absence Presence-Only

The niche concept

9/90

Lessons from biological invasions (Guisan et al., 2014)

Niche & Distribution Data Presence-Absence Presence-Only

The n-dimensions of the niche

10/90

scenopoetic factors (i.e., broad-scale conditions): Grinnell bionomic factors (i.e., resource-consumer dynamics): Elton

The Grinnellian niche & the Eltonian niche (Soberon, 2007)

Niche & Distribution Data Presence-Absence Presence-Only

indirect variables (e.g., elevation, slope & aspect) direct variables (e.g., air temperature & soil pH) resource variables (e.g., nutrients, soil or air water & light)

Variables to consider to capture the niche (Austin & Smith, 1989)

Towards modelling species distribution

11/90

Example for plant distribution (Guisan & Zimmermann, 2000)

Niche & Distribution Data Presence-Absence Presence-Only

Species distribution models (SDMs)

12/90

The modelling trade-off (Guisan & Zimmermann, 2000)

Niche & Distribution Data Presence-Absence Presence-Only

Species distribution models (SDMs)

13/90

Four main types of SDMs regarding the algorithm

Niche & Distribution Data Presence-Absence Presence-Only

profile methods regression methods machine learning methods geographic methods

Two main types of SDMs regarding the response variable

presence-only models presence-absence models

NB: profile methods are always presence-only models but machine learning & regression methods can be either presence-absence or presence-only models depending on whether survey-absence or pseudo-absence/background data have been generated

Species distribution models (SDMs)

14/90 Niche & Distribution Data Presence-Absence Presence-Only

If you also have survey-absence data from a well designed survey

use regression or machine learning methods do not use profile methods

If you only have occurrences, you can still substitute absences with

background data (do not depend on where occurrences are) pseudo-absence data (depend on where occurrences are)

NB: survey-absence data can be biased due to detectability issues

CCL: be careful not to get mixed up between presence-absence (survey-absence) & presence-only (pseudo-absence/background) models when using regression or machine learning methods

Species distribution models (SDMs)

15/90

The BIOCLIM Algorithm: BIOCLIM (P) The Domain Algorithm: Domain (P) Generalized Linear Models: GLMs (R) Generalized Linear Mixed Models: GLMMs (R) Generalized Additive Models: GAMs (R) Generalized Additive Mixed Models: GAMMs (R) Structural Equation Models: SEMs (R) Random Forests: RFs (ML) Boosted Regreesion Trees: BRTs (ML) Artificial Neural Networks: ANNs (ML) The Maximum Entropy Approach: Maxent (ML) Auto-Logistic Models: SAR (R- and ML-compatible) Residual Auto-Covariate Models: RAC (R- and ML-compatible)

Few Profile (P), Regression (R) & Machine Learning (ML) methods

Niche & Distribution Data Presence-Absence Presence-Only

Species distribution models (SDMs)

16/90

GLMs tipically used with presence-absence (survey-absence) data Maxent classically used with presence-only (background) data

During this course, we will specifically focus on

Niche & Distribution Data Presence-Absence Presence-Only

Data

17/90 Niche & Distribution Data Presence-Absence Presence-Only

htt

p:/

/fr.

wik

iped

ia.o

rg/w

iki/

%C

3%

89

rab

le_s

yco

mo

re#

med

iavi

ewe

r/Fi

chie

r:M

ap

le_l

eave

s.jp

g

The dependent or response variable (Y)

18/90 Niche & Distribution Data Presence-Absence Presence-Only

Distribution of Sycamore maple (Acer pseudoplatanus L.)

presence-only data (1) @ GBIF (http://www.gbif.org/) presence-absence data (0/1) @ IGN (http://www.ign.fr/)

The independent or predictor variables (Xi)

19/90 Niche & Distribution Data Presence-Absence Presence-Only

Climatic variables @ WorldClim (http://www.worldclim.org/)

Annual Mean Temperature (BIO1) Max Temperature of Warmest Month (BIO5) Min Temperature of Coldest Month (BIO6) Temperature Annual Range (BIO7 = BIO5-BIO6) Annual Precipitation (BIO12) Precipitation of Wettest Month (BIO13) Precipitation of Driest Month (BIO14) Precipitation Seasonality (Coefficient of Variation) (BIO15) Water Balance (sum of monthly prec. minus monthly PET) (WBAL)

Quick look at the data in R

20/90 Niche & Distribution Data Presence-Absence Presence-Only

Import data into R

> setwd("C:/Users/admin2/Documents/Enseignements/IRSAE-2014")

> ap <- read.table("Data/FR/apPAfr.txt", header=TRUE, sep="\t")

> str(ap)

'data.frame': 46589 obs. of 15 variables:

$ x : num 5.75 7.15 4.99 -0.76 1.16 ...

$ y : num 48.3 48.8 47.8 43 47.2 ...

$ year : int 2005 2005 2005 2005 2005 ...

$ pa : int 1 0 0 0 0 ...

$ wbal : int 88 -27 67 239 -164 ...

$ bio1 : num 8.7 9.1 9.1 8.5 11 ...

$ bio5 : num 22.2 23.1 22.7 21.9 24.6 ...

$ bio6 : num -2.6 -2.3 -2.2 -2.4 0.2 ...

$ bio7 : num 24.8 25.4 24.9 24.3 24.4 ...

$ bio12: int 852 750 849 1068 697 ...

$ bio13: int 87 78 85 114 67 ...

$ bio14: int 55 50 55 60 50 ...

$ bio15: int 14 15 15 15 9 ...

Quick look at the data in R

21/90 Niche & Distribution Data Presence-Absence Presence-Only

Plot the empirical distribution of Sycamore maple on a map

Quick look at the data in R

22/90 Niche & Distribution Data Presence-Absence Presence-Only

Plot the empirical distribution of Sycamore maple on a map

> library(raster)

> fr <- getData("GADM", country="FRA", level=0, path="Data/FR")

> projection(fr)

[1] "+proj=longlat +ellps=WGS84 +datum=WGS84 +towgs84=0,0,0"

> plot(fr)

> occ <- which(ap$pa==1)

> length(occ)

[1] 6455

> abs <- which(ap$pa==0)

> length(abs)

[1] 40134

> points(ap$x[abs], ap$y[abs], pch=4, col="red", cex=0.1)

> points(ap$x[occ], ap$y[occ], pch=3, col="green", cex=0.1)

> leg <- c("Presence", "Absence")

> legend("bottomleft", leg, pch=c(3, 4), col=c("green", "red"))

Quick look at the data in R

23/90 Niche & Distribution Data Presence-Absence Presence-Only

Import raster layers of all predictor variables

> TMEAN <- raster("Data/FR/bio1.tif")

> TMAX <- raster("Data/FR/bio5.tif")

> TMIN <- raster("Data/FR/bio6.tif")

> TSEA <- raster("Data/FR/bio7.tif")

> PANN <- raster("Data/FR/bio12.tif")

> PMAX <- raster("Data/FR/bio13.tif")

> PMIN <- raster("Data/FR/bio14.tif")

> PSEA <- raster("Data/FR/bio15.tif")

> WBAL <- raster("Data/FR/wbal.tif")

Quick look at the data in R

24/90 Niche & Distribution Data Presence-Absence Presence-Only

Plot predictor variables

Quick look at the data in R

25/90 Niche & Distribution Data Presence-Absence Presence-Only

Plot predictor variables

> library(fBasics)

> colT <- rev(divPalette(n=100, name="RdYlBu"))

> colP <- seqPalette(n=100, name="YlGnBu")

> colWBAL <- divPalette(n=100, name="RdYlGn")

> par(mfrow=c(3, 3), mai=c(bottom=0.1, 0, top=0.2, 0))

> plot(TMEAN, col=colT, axes=FALSE, main="tmean")

> plot(TMIN, col=colT, axes=FALSE, main="tmin")

> plot(TMAX, col=colT, axes=FALSE, main="tmax")

> plot(PANN, col=colP, axes=FALSE, main="pann")

> plot(PMIN, col=colP, axes=FALSE, main="pmin")

> plot(PMAX, col=colP, axes=FALSE, main="pmax")

> plot(TSEA, col=colT, axes=FALSE, main="tsea")

> plot(PSEA, col=colP, axes=FALSE, main="psea")

> plot(WBAL, col=colWBAL, axes=FALSE, main="wbal")

Data preparation

26/90 Niche & Distribution Data Presence-Absence Presence-Only

Remove NA values & check correlations among predictors

> dim(ap)

[1] 46589 15

> ap <- na.omit(ap)

> dim(ap)

[1] 46549 15

> round(cor(ap[, 7:15]), 2)

wbal bio1 bio5 bio6 bio7 bio12 bio13 bio14 bio15

wbal 1.00

bio1 -0.70 1.00

bio5 -0.74 0.86 1.00

bio6 -0.58 0.91 0.59 1.00

bio7 -0.13 -0.13 0.39 -0.51 1.00

bio12 0.91 -0.46 -0.43 -0.46 0.07 1.00

bio13 0.76 -0.21 -0.29 -0.16 -0.12 0.88 1.00

bio14 0.75 -0.61 -0.47 -0.63 0.21 0.77 0.43 1.00

bio15 -0.12 0.37 0.15 0.46 -0.36 -0.13 0.31 -0.66 1.00

Data preparation

27/90 Niche & Distribution Data Presence-Absence Presence-Only

Remove highly correlated variables to avoid multicollinearity issues

> ap <- ap[, c(-8, -9, -12, -13, -14)]

> names(ap)[8:10] <- c("tmin", "tsea", "psea")

> str(ap)

'data.frame': 46549 obs. of 10 variables:

$ x : num 5.75 7.15 4.99 -0.76 1.16 ...

$ y : num 48.3 48.8 47.8 43 47.2 ...

$ day : int 29 24 13 26 14 ...

$ month: int 6 3 9 7 3 ...

$ year : int 2005 2005 2005 2005 2005 ...

$ pa : int 1 0 0 0 0 ...

$ wbal : int 88 -27 67 239 -164 ...

$ tmin : num -2.6 -2.3 -2.2 -2.4 0.2 ...

$ tsea : num 24.8 25.4 24.9 24.3 24.4 ...

$ psea : int 14 15 15 15 9 ...

Presence-Absence

28/90 Niche & Distribution Data Presence-Absence Presence-Only

The case of presence-absence data (0/1) or logistic regression

Generalized Linear Models (GLMs)

29/90 Niche & Distribution Data Presence-Absence Presence-Only

Y: the response variable which values p [0 : 1] Xi: explanatory variables being either qualitative or quantitative i: coefficient parameters : the error term (mean 0 & variance dependent on the Xis)

𝑙𝑜𝑔𝑖𝑡 𝑌 = 𝑙𝑛𝑝

1 − 𝑝= 𝛽0 +𝛽1𝑋1 + 𝛽2𝑋2 +⋯+ 𝛽𝑘𝑋𝑘 + 𝜀

𝑝 =1

1 + 𝑒− 𝛽0+𝛽1𝑋1+𝛽2𝑋2+⋯+𝛽𝑘𝑋𝑘+𝜀∈ 0 ∶ 1

Generalized Linear Models (GLMs)

30/90 Niche & Distribution Data Presence-Absence Presence-Only

GLMs’ properties

the family distribution (gaussian, poisson, binomial, gamma) the linear predictor (η = 0 + 1X1 + 2X2 + ... + kXk) the link function (identity, log, logit, inverse)

R syntax

glm(formula, family, data, …) family=gaussian(link="identity") family=poisson(link="log") family=binomial (link="logit") formula=y~x formula=y~poly(x, 2) formula=y~x1+x2 formula=y~x1+x2+x1:x2

Generalized Linear Models (GLMs)

31/90 Niche & Distribution Data Presence-Absence Presence-Only

Split the original dataset into train (4/5) & test (1/5) datasets

> library(dismo)

> fold <- kfold(ap, k=5)

> calib <- ap[which(fold!=1), ]

> dim(calib)

[1] 37239 10

> write.table(calib, "Data/FR/calib.txt", sep="\t")

> valid <- ap[which(fold==1), ]

> dim(valid)

[1] 9310 10

> write.table(valid, "Data/FR/valid.txt", sep="\t")

Divide the initial dataset into five groups of similar sizes

Generalized Linear Models (GLMs)

32/90 Niche & Distribution Data Presence-Absence Presence-Only

Model fitting: Akaike Information Criterion (AIC) & goodness of fit

> M1 <- glm(pa~tmin, family=binomial, data=calib)

> summary(M1)

Estimate Std. Error z value Pr(>|z|)

(Intercept) -2.411640 0.021762 -110.82 <2e-16 ***

tmin -0.379508 0.007279 -52.14 <2e-16 ***

(Dispersion parameter for binomial family taken to be 1)

Null deviance: 29952 on 37238 degrees of freedom

Residual deviance: 26924 on 37237 degrees of freedom

AIC: 26928

> k <- length(M1$coefficients)

> aic <- (2*k)-(2*logLik(M1)[[1]])

> round(aic)

[1] 26928

> gof <- (M1$null.deviance-M1$deviance)/M1$null.deviance

> gof

[1] 0.1010928

Formula for AIC

Formula for goodness of fit

Number of parametres

Generalized Linear Models (GLMs)

33/90 Niche & Distribution Data Presence-Absence Presence-Only

Model prediction: linear or response predictions?

> tmin <- calib$tmin[1:5]

> tmin <- data.frame(tmin)

> tmin

tmin

1 -2.6

2 -2.3

3 -2.4

4 0.2

5 -1.6

> predict(M1, newdata=tmin)

1 2 3 4 5

-1.424919 -1.538772 -1.500821 -2.487542 -1.804427

> predict(M1, newdata=tmin, type="response")

1 2 3 4 5

0.19389155 0.17671390 0.18230312 0.07673617 0.14131298

Raw predictions from the linear

predictor (η)

𝑝[0, 1]

Generalized Linear Models (GLMs)

34/90 Niche & Distribution Data Presence-Absence Presence-Only

Model prediction: plot response curve

> xmax <- max(calib$tmin)

> xmin <- min(calib$tmin)

> tmin <- seq(xmin, xmax, length.out=30)

> tmin <- data.frame(tmin)

> p <- predict(M1, newdata=tmin, type="response")

> plot(tmin$tmin, p, type="l", lwd=2, cex.lab=1.5)

Generalized Linear Models (GLMs)

35/90 Niche & Distribution Data Presence-Absence Presence-Only

Model prediction: plot response curve & empirical data

Generalized Linear Models (GLMs)

36/90 Niche & Distribution Data Presence-Absence Presence-Only

Model prediction: plot response curve & empirical data

> occ <- which(calib$pa==1)

> abs <- which(calib$pa==0)

> hall <- hist(calib$tmin, breaks=tmin$tmin, plot=FALSE)

> hocc <- hist(calib$tmin[occ], breaks=tmin$tmin, plot=FALSE)

> mat <- matrix(c(2, 1), 2, 1, byrow=TRUE)

> nf <- layout(mat, width=4, height=c(1,3), respect=TRUE)

> layout.show(nf)

> par(mar=c(5, 4, 1, 1))

> plot(calib$tmin, calib$pa, type="n", cex.lab=1.5)

> points(calib$tmin[occ], calib$pa[occ], col="green", pch=3)

> points(calib$tmin[abs], calib$pa[abs], col="red", pch=4)

> lines(tmin$tmin, p, lwd=2)

> par(mar=c(0, 4, 1, 1))

> barplot(hall$counts, axes=FALSE, col="red")

> barplot(hocc$counts, axes=FALSE, col="green", add=TRUE)

Generalized Linear Models (GLMs)

37/90 Niche & Distribution Data Presence-Absence Presence-Only

Model prediction: map probabilities of occurrence

> tmin <- getValues(TMIN)

> tmin <- data.frame(tmin)

> p <- predict(M1, newdata=tmin, type="response")

> p <- setValues(TMIN, p)

> colAP <- seqPalette(n=100, name="Greens")

> plot(p, col=colAP)

Generalized Linear Models (GLMs)

38/90 Niche & Distribution Data Presence-Absence Presence-Only

Model prediction: map predicted occurrences & absences

> p <- getValues(p)

> n <- rep(1, length(p))

> size <- rep(1, length(p))

> p <- rbinom(n=n, size=size, prob=p)

> p <- setValues(TMIN, p)

> plot(p, col=c("gray", "green"), legend=FALSE)

Generalized Linear Models (GLMs)

39/90 Niche & Distribution Data Presence-Absence Presence-Only

Model evaluation: Area Under Curve (AUC), sensitivity & specificity

> p <- predict(M1, newdata=valid, type="response")

> occ <- which(valid$pa==1)

> abs <- which(valid$pa==0)

> e <- evaluate(p=p[occ], a=p[abs])

> class(e)

[1] "ModelEvaluation"

attr(,"package")

[1] "dismo"

> e

class : ModelEvaluation

n presences : 1293

n absences : 8017

AUC : 0.7226648

cor : 0.2876606

max TPR+TNR at : 0.1183897

Generalized Linear Models (GLMs)

40/90 Niche & Distribution Data Presence-Absence Presence-Only

Model evaluation: confusion matrix

true positive (tp) true negative (tn) false positive (fp) false negative (fn)

Observations

1 0

Pre

dic

tio

ns

1 tp fp

0 fn tn

sensitivity or True Positive Rate (TPR) specificity or True Negative Rate (TNR)

Sensitivity & specificity

𝑇𝑃𝑅 =𝑡𝑝

𝑡𝑝 + 𝑓𝑛∈ 0 ∶ 1

𝐹𝑃𝑅 =𝑓𝑝

𝑓𝑝 + 𝑡𝑛= 1 − 𝑇𝑁𝑅 ∈ 0 ∶ 1

𝑇𝑁𝑅 =𝑡𝑛

𝑓𝑝 + 𝑡𝑛∈ 0 ∶ 1

Generalized Linear Models (GLMs)

41/90 Niche & Distribution Data Presence-Absence Presence-Only

Model evaluation: confusion matrix for several threshold values

> head(e@t, 4)

[1] 0.00569990 0.00690331 0.00868921 0.00972660

> head(e@confusion, 4)

tp fp fn tn

[1,] 1293 8017 0 0

[2,] 1293 8009 0 8

[3,] 1293 8001 0 16

[4,] 1293 7997 0 20

> tail(e@t, 4)

[1] 0.6928991 0.7672997 0.7673997 0.7674997

> tail(e@confusion, 4)

tp fp fn tn

[235,] 1 8 1292 8009

[236,] 0 2 1293 8015

[237,] 0 2 1293 8015

[238,] 0 0 1293 8017

Vector of threshold

values

Each row is the confusion matrix of a given threshold

value

Generalized Linear Models (GLMs)

42/90 Niche & Distribution Data Presence-Absence Presence-Only

Model evaluation: Receiver Operating Characteristic (ROC) curve

> plot(e, "ROC", cex.lab=1.5, col="blue", type="l", lwd=2)

Generalized Linear Models (GLMs)

43/90 Niche & Distribution Data Presence-Absence Presence-Only

Model evaluation: threshold value maximizing TPR + TNR

> plot(e@t, e@TPR+e@TNR, type="l", lwd=2, cex.lab=1.5)

> max(e@TPR+e@TNR)

[1] 1.350668

> e@t[which(e@TPR+e@TNR==max(e@TPR+e@TNR))]

[1] 0.1183897

> mat <- e@confusion[which(e@TPR+e@TNR==max(e@TPR+e@TNR)), ]

> mat

tp fp fn tn

980 3265 313 4752

> num <- (mat[1]*mat[4])-(mat[3]*mat[2])

> den <- (mat[1]+mat[2])*(mat[3]+mat[4])

> tss <- num/den

> tss

[1] 0.1690632

Generalized Linear Models (GLMs)

44/90 Niche & Distribution Data Presence-Absence Presence-Only

Model evaluation: True Skill Statistic (TSS) at max[TPR + TNR]

Observations

1 0

Pre

dic

tio

ns

1 tp fp

0 fn tn

𝑇𝑆𝑆 =𝑡𝑝 ∗ 𝑡𝑛 − (𝑓𝑛 ∗ 𝑓𝑝)

𝑡𝑝 + 𝑓𝑛 ∗ (𝑓𝑝 + 𝑡𝑛)∈ −1 ∶ 1

Generalized Linear Models (GLMs)

45/90 Niche & Distribution Data Presence-Absence Presence-Only

Model fitting: test for second-order polynomial relationship

> M2 <- glm(pa~poly(tmin, 2), family=binomial, data=calib)

> summary(M2)

Estimate Std. Error z value Pr(>|z|)

(Intercept) -2.11902 0.01983 -106.844 < 2e-16 ***

poly(tmin, 2)1 -183.17107 4.28577 -42.739 < 2e-16 ***

poly(tmin, 2)2 -30.51618 3.77913 -8.075 6.75e-16 ***

(Dispersion parameter for binomial family taken to be 1)

Null deviance: 29952 on 37238 degrees of freedom

Residual deviance: 26856 on 37236 degrees of freedom

AIC: 26862

> AIC(M1)

[1] 26927.64

Drop in AIC from M1 to

M2, but is it significant?

Generalized Linear Models (GLMs)

46/90 Niche & Distribution Data Presence-Absence Presence-Only

Model fitting: is it worth complicating the initial model?

> anova(M1, M2, test="Chisq")

Analysis of Deviance Table

Model 1: pa ~ tmin

Model 2: pa ~ poly(tmin, 2)

Resid. Df Resid. Dev Df Deviance Pr(>Chi)

1 37237 26924

2 37236 26856 1 68.009 < 2.2e-16 ***

Generalized Linear Models (GLMs)

47/90 Niche & Distribution Data Presence-Absence Presence-Only

Model prediction: compare response curves from M1 & M2

Generalized Linear Models (GLMs)

48/90 Niche & Distribution Data Presence-Absence Presence-Only

Model prediction: compare response curves from M1 & M2

> xmax <- max(calib$tmin)

> xmin <- min(calib$tmin)

> tmin <- seq(xmin, xmax, length.out=30)

> tmin <- data.frame(tmin)

> p1 <- predict(M1, newdata=tmin, type="response")

> p2 <- predict(M2, newdata=tmin, type="response")

> plot(calib$tmin, calib$pa, type="n", cex.lab=1.5)

> lines(tmin$tmin, p1, lty=2, lwd=2, cex.lab=1.5)

> lines(tmin$tmin, p2, lwd=2, cex.lab=1.5)

> leg <- c("M1", "M2")

> legend(x="topright", leg, lwd=c(2, 2), lty=c(2, 1))

Generalized Linear Models (GLMs)

49/90 Niche & Distribution Data Presence-Absence Presence-Only

Model evaluation: is M2 performing better than M1?

> p <- predict(M2, newdata=valid, type="response")

> occ <- which(valid$pa==1)

> abs <- which(valid$pa==0)

> e <- evaluate(p=p[occ], a=p[abs])

> e

class : ModelEvaluation

n presences : 1293

n absences : 8017

AUC : 0.7226648

cor : 0.2900685

max TPR+TNR at : 0.1241536

Generalized Linear Models (GLMs)

50/90 Niche & Distribution Data Presence-Absence Presence-Only

Model fitting: test the full model

> f <- ~.+poly(tsea, 2)+poly(psea, 2)+ poly(wbal, 2)

> M3 <- update(M2, f)

> summary(M3)

Estimate Std. Error z value Pr(>|z|)

(Intercept) -2.32488 0.02461 -94.487 < 2e-16 ***

poly(tmin, 2)1 -267.56293 9.11505 -29.354 < 2e-16 ***

poly(tmin, 2)2 -117.68537 6.38682 -18.426 < 2e-16 ***

poly(tsea, 2)1 -137.51657 6.16349 -22.311 < 2e-16 ***

poly(tsea, 2)2 69.54516 4.09620 16.978 < 2e-16 ***

poly(psea, 2)1 -63.20803 7.18893 -8.792 < 2e-16 ***

poly(psea, 2)2 52.39951 6.42492 8.156 3.47e-16 ***

poly(wbal, 2)1 2.17989 5.74146 0.380 0.704

poly(wbal, 2)2 19.19854 3.64584 5.266 1.40e-07 ***

(Dispersion parameter for binomial family taken to be 1)

Null deviance: 29952 on 37238 degrees of freedom

Residual deviance: 25728 on 37230 degrees of freedom

AIC: 25746

Generalized Linear Models (GLMs)

51/90 Niche & Distribution Data Presence-Absence Presence-Only

Model prediction: compare response curves from M1, M2 & M3

Generalized Linear Models (GLMs)

52/90 Niche & Distribution Data Presence-Absence Presence-Only

Model prediction: compare response curves from M1, M2 & M3

> xmax <- max(calib$tmin)

> xmin <- min(calib$tmin)

> tmin <- seq(xmin, xmax, length.out=30)

> tsea <- rep(mean(calib$tsea), 30)

> psea <- rep(mean(calib$psea), 30)

> wbal <- rep(mean(calib$wbal), 30)

> predictors <- cbind(tmin, tsea, psea, wbal)

> predictors <- data.frame(predictors)

> p1 <- predict(M1, newdata=predictors, type="response")

> p2 <- predict(M2, newdata=predictors, type="response")

> p3 <- predict(M3, newdata=predictors, type="response")

> plot(calib$tmin, calib$pa, type="n", cex.lab=1.5)

> lines(predictors$tmin, p1, lty=3, lwd=2, cex.lab=1.5)

> lines(predictors$tmin, p2, lty=2, lwd=2, cex.lab=1.5)

> lines(predictors$tmin, p3, lwd=2, cex.lab=1.5)

> leg <- c("M1", "M2", "M3")

> legend(x="topright", leg, lwd=c(2, 2, 2), lty=c(3, 2, 1))

Generalized Linear Models (GLMs)

53/90 Niche & Distribution Data Presence-Absence Presence-Only

Model prediction: compare probability maps from M1, M2 & M3

> predictors <- stack(TMIN, TSEA, PSEA, WBAL)

> names(predictors)

[1] "bio6" "bio7" "bio15" "wbal"

> names(predictors) <- c("tmin", "tsea", "psea", "wbal")

> p1 <- predict(predictors, M1, type="response")

> p2 <- predict(predictors, M2, type="response")

> p3 <- predict(predictors, M3, type="response")

Generalized Linear Models (GLMs)

54/90 Niche & Distribution Data Presence-Absence Presence-Only

Model prediction: compare probability maps from M1, M2 & M3

> windows(12, 4)

> par(mfrow=c(1, 3), mar=c(1, 1, 2, 4))

> plot(p1, col=colAP, axes=FALSE, main="M1")

> plot(p2, col=colAP, axes=FALSE, main="M2")

> plot(p3, col=colAP, axes=FALSE, main="M3")

Generalized Linear Models (GLMs)

55/90 Niche & Distribution Data Presence-Absence Presence-Only

Model evaluation: is M3 performing better than M1 & M2?

> p <- predict(M3, newdata=valid, type="response")

> occ <- which(valid$pa==1)

> abs <- which(valid$pa==0)

> e <- evaluate(p=p[occ], a=p[abs])

> e

class : ModelEvaluation

n presences : 1293

n absences : 8017

AUC : 0.7651777

cor : 0.3129263

max TPR+TNR at : 0.1226597

Yes, it does

Generalized Linear Models (GLMs)

56/90 Niche & Distribution Data Presence-Absence Presence-Only

Model selection: which model shall I use between M1, M2 & M3?

> library(AICcmodavg)

> Mcands <- list(M1, M2, M3)

> Mnames <- c("M1", "M2", "M3")

> aictab(cand.set=Mcands, modnames=Mnames, second.ord=FALSE)

Model selection based on AIC :

K AIC Delta_AIC AICWt Cum.Wt LL

M3 9 25745.85 0.00 1 1 -12863.92

M2 3 26861.63 1115.78 0 1 -13427.81

M1 2 26927.64 1181.79 0 1 -13461.82

𝐴𝐼𝐶𝑊𝑡𝑖 =𝑒−

12∆𝐴𝐼𝐶𝑖

𝑒−12∆𝐴𝐼𝐶𝑘𝑁

𝑘=1

NB: if ΔAICi 0, the ith model is having some support and can be also considered in addition to the best model (cf. model averaging)

Where N is the total number of candidate

models

M3 is the best model

Generalized Linear Models (GLMs)

57/90 Niche & Distribution Data Presence-Absence Presence-Only

Check residuals from the best model (M3): raw data

> plot(predict(M3), residuals(M3), type="n", cex.lab=1.5)

> occ <- which(calib$pa==1)

> abs <- which(calib$pa==0)

> points(predict(M3)[occ], residuals(M3)[occ], col="green")

> points(predict(M3)[abs], residuals(M3)[abs], col="red")

> abline(h=0, lty=2, lwd=2)

Not meaningful for residuals’

diagnosis

Generalized Linear Models (GLMs)

58/90 Niche & Distribution Data Presence-Absence Presence-Only

Check residuals from the best model (M3): using a spline function

Generalized Linear Models (GLMs)

59/90 Niche & Distribution Data Presence-Absence Presence-Only

Check residuals from the best model (M3): using a spline function

> library(splines)

> resVSfit <- lm(residuals(M3)~bs(predict(M3), degree=8))

> Y <- predict(resVSfit, se=TRUE)

> cisup <- Y$fit+2*Y$se.fit

> ciinf <- Y$fit-2*Y$se.fit

> ord <- order(predict(M3))

> x.coord <- c(predict(M3)[rev(ord)], predict(M3)[ord])

> y.coord <- c(ciinf[rev(ord)], cisup[ord])

> plot(predict(M3), residuals(M3), type="n", cex.lab=1.5)

> polygon(x.coord, y.coord, col="grey", border="grey")

> lines(predict(M3)[ord], ciinf[ord], lty=2, lwd=2, col="red")

> lines(predict(M3)[ord], cisup[ord], lty=2, lwd=2, col="red")

> abline(h=0, lty=2, lwd=2)

Generalized Linear Models (GLMs)

60/90 Niche & Distribution Data Presence-Absence Presence-Only

Check residuals from the best model (M3): spatial autocorrelation

> library(spdep)

> sel <- sample(c(1:nrow(calib)), 1000, replace=FALSE)

> xy <- as.matrix(cbind(calib$x[sel], calib$y[sel]))

> nb <- dnearneigh(xy, d1=0, d2=25, longlat=TRUE)

> nb

Neighbour list object:

Number of regions: 1000

Number of nonzero links: 4578

Percentage nonzero weights: 0.4578

Average number of links: 4.578

49 regions with no links:

4 26 40 41 44 62 77 107 132 153 ...

In kms

Generalized Linear Models (GLMs)

61/90 Niche & Distribution Data Presence-Absence Presence-Only

Check residuals from the best model (M3): spatial autocorrelation

> plot(nb, xy, cex=0.7, pch=20)

Neighboorhood relationships

Generalized Linear Models (GLMs)

62/90 Niche & Distribution Data Presence-Absence Presence-Only

Check residuals from the best model (M3): spatial autocorrelation

> z <- residuals(M3)[sel]

> c <- sp.correlogram(nb, z, "I", order=15, zero.policy=TRUE)

> plot(correlog.res)

Spatial autocorrelation in the residuals of M3 remains significant within 0-75 kms

Generalized Linear Models (GLMs)

63/90 Niche & Distribution Data Presence-Absence Presence-Only

Solutions

look at relationships between the residuals & the predictors look for additional predictors able to capture the spatial signal incorporate an autocovariate in the model’s predictors (SAR/RAC) incorporate spatial eigenvectors (PCNMs) in the model’s predictors

NB: if you incorporate an autocovariate in the model’s predictors, compute it so that it captures spatial autocorrelation in the residuals (cf. 0-75 kms)

Presence-Only

64/90 Niche & Distribution Data Presence-Absence Presence-Only

Maximum entropy modelling (Maxent)

65/90 Niche & Distribution Data Presence-Absence Presence-Only

Key papers (Phillips et al., 2006; Elith et al., 2011)

Maximum entropy modelling (Maxent)

66/90 Niche & Distribution Data Presence-Absence Presence-Only

Main concepts & terms used in Maxent (Elith et al., 2011)

occurrences: geographic coordinates where a species is present background: a random sample of coordinates from the mask mask: a grid of locations to be sampled for the background covariates: set of predictor variables at each sampled location features: transformations (linear, quadratic, product, hinge, etc.) regularization: smoothing of the model to limit complexity prevalence: 0.5 by default (the strongest assumption)

NB: background points can be sampled at locations where species occurrences have been recorded and thus there is independency between the background sample & occurrence records, which is not the case for pseudo-absences

Maximum entropy modelling (Maxent)

67/90 Niche & Distribution Data Presence-Absence Presence-Only

Background matters (VanDerWal et al., 2009)

Maximum entropy modelling (Maxent)

68/90 Niche & Distribution Data Presence-Absence Presence-Only

Background matters (VanDerWal et al., 2009)

Maximum entropy modelling (Maxent)

69/90 Niche & Distribution Data Presence-Absence Presence-Only

Some golden rules for Maxent modelling (Elith et al. 2011)

make a mask including the full environmental range of the species use the mask to exclude areas which were probably not surveyed exclude also areas where the species cannot have dispersed use a reduced set of covariates based on meaningful assumptions project covariate grids using equal area projections (area effect) use product features only if you are interested in interactions increase regularization parameters to get more diffused outputs decrease regularization parameters to get more localized outputs tune the prevalence parameter for cross-species comparisons

Maximum entropy modelling (Maxent)

70/90 Niche & Distribution Data Presence-Absence Presence-Only

1. Download Maxent

Maximum entropy modelling (Maxent)

71/90 Niche & Distribution Data Presence-Absence Presence-Only

2. Copy/paste the file “maxent.jar” into C:/…/R/library/dismo/java

Maximum entropy modelling (Maxent)

72/90 Niche & Distribution Data Presence-Absence Presence-Only

3. Use the maxent() function from the R-package “dismo”

Maximum entropy modelling (Maxent)

73/90 Niche & Distribution Data Presence-Absence Presence-Only

Application: distribution of Acer Pseudoplatanus L. in France

we assume that we only have occurrence records across France we use the French territory as a mask for the background sample

Maximum entropy modelling (Maxent)

74/90 Niche & Distribution Data Presence-Absence Presence-Only

Prepare the occurrence records to be used for training the model

> library(rgdal)

> EPSG <- make_EPSG()

> laea <- EPSG[which(EPSG$code==3035), "prj4"]

> occ <- which(calib$pa==1)

> occtrain <- data.frame(cbind(calib$x[occ], calib$y[occ]))

> names(occtrain) <- c("lon", "lat")

> occtrain <- SpatialPoints(occtrain, fr@proj4string)

> occtrain <- spTransform(occtrain, CRS(laea))

Maximum entropy modelling (Maxent)

75/90 Niche & Distribution Data Presence-Absence Presence-Only

Prepare the mask & covariates

> mask <- rasterize(fr, TMIN, field=1)

> crs <- CRS(laea)

> res <- c(1000, 1000)

> mask <- projectRaster(mask, method="ngb", res=res, crs=crs)

> predictors <- stack(TMIN, TSEA, PSEA, WBAL)

> predictors <- projectRaster(predictors, mask)

> predictors <- predictors*mask

> names(predictors)

[1] "layer.1" "layer.2" "layer.3" "layer.4"

> names(predictors) <- c("tmin", "tsea", "psea", "wbal")

Maximum entropy modelling (Maxent)

76/90 Niche & Distribution Data Presence-Absence Presence-Only

Fit the model with Maxent’s default parameters

> arg <- c("responsecurves=TRUE")

> M4 <- maxent(x=predictors, p=occtrain, args=arg)

> class(M4)

[1] "MaxEnt"

attr(,"package")

[1] "dismo"

> M4

Display all Maxent’s

outputs on an html page

Maximum entropy modelling (Maxent)

77/90 Niche & Distribution Data Presence-Absence Presence-Only

Maxent’s outputs

Maximum entropy modelling (Maxent)

78/90 Niche & Distribution Data Presence-Absence Presence-Only

Variable contributions

> plot(M4, pch=20, cex.lab=1.5, col="blue")

Maximum entropy modelling (Maxent)

79/90 Niche & Distribution Data Presence-Absence Presence-Only

Response curves

> response(M4, cex.lab=1.5, col="black")

Maximum entropy modelling (Maxent)

80/90 Niche & Distribution Data Presence-Absence Presence-Only

Predictions of the geographic distribution of Sycamore maple

> p4 <- predict(M4, predictors)

> p4

class : RasterLayer

dimensions : 1182, 1206, 1425492 (nrow, ncol, ncell)

resolution : 1000, 1000 (x, y)

extent : 3086567, 4292567, 2025783, 3207783

coord. ref. : +proj=laea +lat_0=52 +lon_0=10 ...

data source : in memory

names : layer

values : 0.007738914, 0.8186277 (min, max)

Maximum entropy modelling (Maxent)

81/90 Niche & Distribution Data Presence-Absence Presence-Only

Predictions of the geographic distribution of Sycamore maple

> plot(p4, col=colAP, axes=FALSE, box=FALSE)

Maximum entropy modelling (Maxent)

82/90 Niche & Distribution Data Presence-Absence Presence-Only

Occurrence records from the train dataset overlaid on the map

> points(occtrain, pch=3, cex=0.5)

Maximum entropy modelling (Maxent)

83/90 Niche & Distribution Data Presence-Absence Presence-Only

Predictions from GLM & Maxent with the same set of predictors

Maximum entropy modelling (Maxent)

84/90 Niche & Distribution Data Presence-Absence Presence-Only

Predictions from GLM & Maxent with the same set of predictors

> windows(16, 8)

> par(mfrow=c(1, 2), mar=c(1, 1, 2, 4))

> p3 <- projectRaster(p3, mask)

> p3 <- p3*mask

> plot(p3, col=colAP, axes=FALSE, main="GLM")

> plot(p4, col=colAP, axes=FALSE, main="Maxent")

Maximum entropy modelling (Maxent)

85/90 Niche & Distribution Data Presence-Absence Presence-Only

Evaluation of maxent performances

> occ <- which(valid$pa==1)

> occtest <- data.frame(cbind(valid$x[occ], valid$y[occ]))

> names(occtest) <- c("lon", "lat")

> occtest <- SpatialPoints(occtest, fr@proj4string)

> occtest <- spTransform(occtest, CRS(laea))

> bg <- randomPoints(mask=mask, n=nrow(valid)-length(occ))

> e <- evaluate(model=M4, p=occtest, a=bg, x=predictors)

> e

class : ModelEvaluation

n presences : 1354

n absences : 7940

AUC : 0.8145358

cor : 0.3862409

max TPR+TNR at : 0.3821696

Performs better than M3 (GLM)

Maximum entropy modelling (Maxent)

86/90 Niche & Distribution Data Presence-Absence Presence-Only

Change Maxent’s default parameters to fit a smother model

> a1 <- c("product=FALSE")

> a2 <- c("threshold=FALSE")

> a3 <- c("hinge=FALSE")

> a4 <- c("responsecurves=TRUE")

> M5 <- maxent(predictors, occtrain, args=c(a1, a2, a3, a4))

Maximum entropy modelling (Maxent)

87/90 Niche & Distribution Data Presence-Absence Presence-Only

Response curves

> response(M5, cex.lab=1.5, col="black")

Maximum entropy modelling (Maxent)

88/90 Niche & Distribution Data Presence-Absence Presence-Only

Compare predictions between M4 & M5

Maximum entropy modelling (Maxent)

89/90 Niche & Distribution Data Presence-Absence Presence-Only

Predictions from GLM & Maxent with the same set of predictors

> windows(16, 8)

> par(mfrow=c(1, 2), mar=c(1, 1, 2, 4))

> p5 <- predict(M5, predictors)

> plot(p4, col=colAP, axes=FALSE, main="M4 (Maxent)")

> plot(p5, col=colAP, axes=FALSE, main="M5 (Maxent)")

Maximum entropy modelling (Maxent)

90/90 Niche & Distribution Data Presence-Absence Presence-Only

Model evaluation

> e <- evaluate(model=M5, p=occtest, a=bg, x=predictors)

> e

class : ModelEvaluation

n presences : 1354

n absences : 7940

AUC : 0.8001492

cor : 0.3670693

max TPR+TNR at : 0.4720257

Recommended