##################################################################################
# FSECchange: R function to compute change in functional diversity following disturbance #
# INPUTS:
# - "abundances" = matrix (2 x S) with the abundances of the S species "before" and "after" disturbance
# - "coord" = matrix (S x T) with coordinates of the S species present in the "abundances" matrix on the T axes of the functional space (NA not allowed)
# NB: number of species before and after disturbance must be strictly higher than T
# R Libraries 'ape', 'geometry' and 'rcdd' need to be downloaded before running this function
#
# OUTPUTS: a list with
# $ NbSp: a vector (3) with number of species before and after disturbance and change between the 2 periods (after-before)
# $ sp_ab: a matrix (S*3), with species relative abundances (%) before and after disturbance,
# and their status given their changes in abundance : "extirpated", "loser", "unchanged", "winner", "introduced"
# $ FId: a matrix (4*T) with Community Weighted Mean (CWM) values before and after disturbance for each functional axis
# delta= change in CWM (after-before), and delta_st= delta CWM standardized by trait range in the species pool
# $ FD: a matrix (3*7) with indices values before and after disturbance and their temporal change (delta=after-before)
# - FRic: functional richness (expressed as a percentage of the functional space filled by the S species)
# - FEve: functional evenness
# - FDiv: functional divergence
# - FDis: functional dispersion (expressed as a percentage of the maximal pairwise distance observed in the species pool)
# - FEnt: functional entropy (Rao's quadratic entropy expressed as an equivalent number of species)
# - FSpe: functional specialization (expressed as a percentage of the maximal specialization observed in the species pool)
# - FOri: functional originality (expressed as a percentage of the maximal originality observed in the species pool)
# $ FShift: a single value vector with functional shift expressed as a percentage of non-overlap between convex hulls shaping communities before and after disturbance respectively
# $ details: a list with details about FD indices
# - vertices_before and vertices_after: vectors with identities of species being vertices before and after disturbance
# - mstvect_before and mstvect_after: matrix describing the topology of Minimum Spanning Trees for each period
# - B_before and B_after: vector(length T) with coordinates of the center of gravity (B) of the vertices for each period
# - meandDB_before and mean_dB_after: mean distance to B for each period
# - O: vector (length T) with coordinates of the center of gravity of the species pool
# - speS: vector (length S) with functional specialization of each of the S species
# - NN: matrix 0/1 (S*S) with pairs of nearest neighbours (focal species=rows)
# - oriS: vector (length S) with functional originality of each of the S species
# An example of how using this function is provided at this end of the script (cf figure in Box2)
##################################################################################
FSECchange<-function(coord,abundances) {
#loading required libraries
require(ape)
require(geometry)
require(rcdd)
##################################################################################
# identities of species present before and after disturbance
sp_before<-which(abundances[1,]!=0)
sp_after<-which(abundances[2,]!=0)
# number of species
NbSp<-c( length(sp_before), length(sp_after), length(sp_after)-length(sp_before) )
names(NbSp)<-c("before","after","delta")
# check coord matrix
if (is.numeric(coord)==F) stop ("coord values must be numeric")
if(is.na(sum(coord))) stop ("NA are not allowed in 'coord' matrix")
T<-ncol(coord) # T = number of functional axes
if (dim(abundances)[2]!=dim(coord)[1]) stop(" error : different number of species in 'coord' and 'abundances' matrices ")
if (NbSp["before"]<=T) stop(paste("error : community 'before' must contain at least ",T+1, " species",sep=""))
if (NbSp["after"]<=T) stop(paste("error : community 'after' must contain at least ",T+1, " species",sep=""))
##################################################################################
# computing relative abundances
relab<-abundances/apply(abundances,1,sum,na.rm=T)
# status given changes in abundances
status<-rep(NA,nrow(coord))
status[which(relab["Before",] == relab["After",])]<-"unchanged"
status[which(relab["Before",] > relab["After",])]<-"loser"
status[which(relab["Before",] < relab["After",])]<-"winner"
status[which(relab["Before",]!=0 & relab["After",]==0)]<-"extirpated"
status[which(relab["Before",]==0 & relab["After",]!=0)]<-"introduced"
sp_ab<data.frame(relab_before=round(relab[1,]*100),relab_after=round(relab[2,]*100),status=as.factor(status) )
row.names(sp_ab)<-row.names(coord)
# definition of matrix FId
FId<-matrix(0,4,T,dimnames=list(c("before","after","delta","delta_st"),colnames(coord)) )
# computing changes in community weighted mean trait values
FId["before",]<-relab["Before",sp_before] %*% coord[sp_before,]
FId["after",]<-relab["After",sp_after] %*% coord[sp_after,]
FId["delta",]<-FId["after",]-FId["before",]
FId["delta_st",]<-FId["delta",] / ( apply(coord,2,max) - apply(coord,2,min) )
##################################################################################
# function to compute functional richness, evenness and divergence
# ab: relative abundances of species (no 0) ; tr: matrix of species coord
FRicFEveFDiv<-function(ab,tr) {
# S = number of species
S<-length(ab)
# FRIC
FRic<-round(convhulln(tr,"FA")$vol,6)
# identity of vertices
vert0<-convhulln(tr,"Fx TO 'vert.txt'")
vert1<-scan("vert.txt",quiet=T)
vertices<-(vert1+1)[-1]
# FEve
# computation of inter-species euclidian distances
distT<-dist(tr, method="euclidian")
# computation of Minimum Spanning Tree and conversion of the 'mst' matrix into 'dist' class
linkmst<-mst(distT)
mstvect<-as.dist(linkmst)
# computation of the pairwise cumulative relative abundances and conversion into 'dist' class
ab2<-matrix(0,nrow=S,ncol=S)
for (q in 1:S)
for (r in 1:S)
ab2[q,r]<-ab[q]+ab[r] # end of q,r
ab2vect<-as.dist(ab2)
# computation of EW for the (S-1) segments
EW<-rep(0,S-1)
flag<-1
for (m in 1:((S-1)*S/2))
{if (mstvect[m]!=0) {EW[flag]<-distT[m]/(ab2vect[m]) ; flag<-flag+1}} # end of m
# computation of the PEW and comparison with 1/S-1, finally computation of FEve
minPEW<-rep(0,S-1) ; OdSmO<-1/(S-1)
for (l in 1:(S-1))
minPEW[l]<-min( (EW[l]/sum(EW)) , OdSmO ) # end of l
FEve<-round( ( (sum(minPEW))- OdSmO) / (1-OdSmO ) ,6)
# FDiv
# coord values of vertices
trvertices<-tr[vertices,]
# coordinates of the center of gravity of the vertices (B)
B<-apply(trvertices,2,mean)
# computing euclidian dstances to B (dB)
dB<-apply(tr, 1, function(x) { (sum((x-B)^2) )^0.5} )
# mean of dB values, deviations to mean and relative abundances-weighted mean deviation
meandB<-mean(dB)
devdB<-dB-meandB
abdev<-ab*devdB
ababsdev<-ab*abs(devdB)
# computation of FDiv
FDiv<-round( (sum(abdev)+meandB) / (sum(ababsdev)+meandB) ,6)
RED<-c(FRic=FRic, FEve=FEve, FDiv=FDiv, details=list(vertices=vertices, mst=linkmst, B=B, meandB=meandB) )
invisible(RED)
} # end of function FRicFEveFDiv
############################################################################################
# definition of matrix FD
FD<matrix(0,7,3,dimnames=list(c("FRic","FEve","FDiv","FDis","FEnt","FSpe","FOri"),c("before","after","delta")) )
# computing convex hull volume of the total pool of species
CHV_pool<-as.numeric( FRicFEveFDiv(ab=rep(1,nrow(coord)), tr=coord)["FRic"] )
# computing FRic FEve and FDiv before and after disturbance
F_RED_before<-FRicFEveFDiv( relab["Before",sp_before] , coord[sp_before,] )
F_RED_after<-FRicFEveFDiv( relab["After",sp_after] , coord[sp_after,] )
FD["FRic",c("before","after")]<-c(F_RED_before$FRic , F_RED_after$FRic) / CHV_pool
FD["FEve",c("before","after")]<-c(F_RED_before$FEve, F_RED_after$FEve)
FD["FDiv",c("before","after")]<-c(F_RED_before$FDiv , F_RED_after$FDiv)
vertices.before<-row.names(coord[sp_before,])[F_RED_before$details.vertices]
vertices.after<-row.names(coord[sp_after,])[F_RED_after$details.vertices]
# FDis: abundance-weighted mean distance to abundance-weighted centroid
FDis_before<-relab["Before",sp_before] %*% apply(coord[sp_before,], 1, function(x) { (sum((x-FId["before",])^2) )^0.5} )
FDis_after<-relab["After",sp_after] %*% apply(coord[sp_after,], 1, function(x) { (sum((x-FId["after",])^2) )^0.5} )
FD["FDis",c("before","after")]<-c(FDis_before , FDis_after)/ max(dist(coord))
# standardized distance between species
dist2<- as.matrix(dist(coord)) / max(dist(coord))
# Rao's quadratic entropy (Q): sum of abundance-weighted distances before and after disturbance
d2ab_before<-relab["Before",sp_before] %*% dist2[sp_before,sp_before] %*% relab["Before",sp_before]
d2ab_after<-relab["After",sp_after] %*% dist2[sp_after,sp_after] %*% relab["After",sp_after]
# Q expressed as an equivalent number of species
Q_before<- 1/(1-d2ab_before)
Q_after<- 1/(1-d2ab_after)
FD["FEnt",c("before","after")]<-c(Q_before , Q_after)
# computing functional specialization of all species : distance to hypothetical mean species
O<-apply(coord, 2, mean)
speS<-apply(coord, 1, function(x) { (sum((x-O)^2) )^0.5} )
# computing functional specialization before and after disturbance
# values are standardized by dividing by the maximal value of specialization
FD["FSpe","before"]<-speS[sp_before] %*% relab["Before",sp_before] / max(speS)
FD["FSpe","after"]<-speS[sp_after] %*% relab["After",sp_after] / max(speS)
# computing functional originality of all species and identity of nearest neighbour
dist_F<-as.matrix(dist(coord,method="euclidean")) ; dist_F[which(dist_F==0)]<-NA
oriS<-apply(dist_F, 1, min, na.rm=T )
NN<-dist_F ; NN<-NN-apply(NN,1,min,na.rm=T) ; NN[which(NN!=0)]<-NA ; NN[which(NN==0)]<-1
# computing functional specialization and originality before and after disturbance
# values are standardized by dividing by the maximal value of originality
FD["FOri","before"]<-oriS[sp_before] %*% relab["Before",sp_before] / max(oriS)
FD["FOri","after"]<-oriS[sp_after] %*% relab["After",sp_after] / max(oriS)
FD[,"delta"]<-FD[,"after"]-FD[,"before"]
##################################################################################
# compute functional shift between periods
set1<-coord[sp_before,]
set2<-coord[sp_after,]
# transforming species coordinates in the multidimensional space in true rational number written as character string
# reduce set of points to vertices only using redundant function
# changing polytope representation: vertices to inequality constraints
set1rep <- d2q(cbind(0, cbind(1, set1)))
polytope1 <- redundant(set1rep, representation = "V")$output
H_chset1 <- scdd(polytope1, representation = "V")$output
set2rep <- d2q(cbind(0, cbind(1, set2)))
polytope2 <- redundant(set2rep, representation = "V")$output
H_chset2 <- scdd(polytope2, representation = "V")$output
# intersection between the two polytopes
H_inter <- rbind(H_chset1, H_chset2)
V_inter <- scdd(H_inter, representation = "H")$output
# extracting coordinates of vertices
vert_1n2 <- q2d(V_inter[ , - c(1, 2)])
# computing convex hull volume of the intersection (if it exists)
vol_inter<-0
if (is.matrix(vert_1n2)) # vector if one vertex in common
if( nrow(vert_1n2)>ncol(vert_1n2) ) vol_inter<-convhulln(vert_1n2,"FA")$vol
FShift<- 1- (vol_inter / CHV_pool )
##################################################################################
# grouping results
res<-list(NbSp=NbSp, sp_ab=sp_ab, FId=FId, FD=FD, FShift=FShift,
detailsFD=list(vertices_before=vertices.before, vertices_after=vertices.after,
mst_before=F_RED_before$details.mst, mst_after=F_RED_after$details.mst,
B_before=F_RED_before$details.B, B_after=F_RED_after$details.B,
meandB_before=F_RED_before$details.meandB, meandB_after=F_RED_after$details.meandB,
O=O, speS=speS, NN=NN, oriS=oriS)
)
invisible(res)
}# end of function FSECchange
################################# END OF FUNCTION #############################################
# EXAMPLE (cf figure in Box2)
do_example<-FALSE # TURN TO TRUE TO RUN EXAMPLE
if(do_example==TRUE) {
# species abundances before and after disturbance
abund_ex<-rbind( c(10,10,25,10,10,5,0,5,5,10,10,0), c(0,0,5,5,5,20,5,20,15,5,15,5) )
row.names(abund_ex)<-c("Before","After")
colnames(abund_ex)<-c("A","B","C","D","E","F","G","H","I","J","K","L")
# species functional traits values
traits_ex<-cbind( c(-4,-3,-3,-2,0,1,1,1,1,2,3,3), c(-1,-3,2,-1,3,-2,-1,0,2,-3,1,3) )
colnames(traits_ex)<-c("Trait 1","Trait 2")
row.names(traits_ex)<-c("A","B","C","D","E","F","G","H","I","J","K","L")
# computinf change in functional diversity using the FSECchange function
chgeFD<-FSECchange(coord=traits_ex, abundances=abund_ex)
# printing outputs
print("# OUTPUTS") ; cat("\n")
print("# species status")
print(chgeFD$sp_ab) ; cat("\n")
print("# species richness")
print(chgeFD$NbSp) ; cat("\n")
print("# aggregated trait values")
print(chgeFD$FId) ; cat("\n")
print("# FD indices values")
print(chgeFD$FD) ; cat("\n")
print("# functional shift")
print(chgeFD$FShift) ; cat("\n")
print("# examples of how to get details of FD computation") ; cat("\n")
print("# identities of the species being vertices before disturbance")
print(chgeFD$detailsFD$vertices_before) ; cat("\n")
print("# functional specialization of each species")
print(chgeFD$detailsFD$speS) ; cat("\n")
} # end of if do_example
################################# END OF EXAMPLE #############################################