### Source code for required functions for FS CoDisp analyses ################################## ### INSTALL AND LOAD REQUIRED PACKAGES ################################## # Function to Install and Load R Packages # Install_And_Load <- function(Required_Packages) { # Remaining_Packages <- Required_Packages[!(Required_Packages %in% installed.packages()[,"Package"])]; # if(length(Remaining_Packages)) {install.packages(Remaining_Packages)} # for(package_name in Required_Packages) # { library(package_name,character.only=TRUE,quietly=TRUE) } # } # end function # # Required_Packages=c("spatstat","geoR","fields","SpatialPack","ggplot2","raster","gstat") # Install_And_Load(Required_Packages) library(spatstat) library(geoR) library(fields) library(SpatialPack) library(ggplot2) library(grid) library(raster) library(gstat) # library(ggmap) # for graphing on a map # library(ggsubplot) # for embedding graphs # library(map) # for graphing on a map # library(mapdata) # for graphing on a map ################################## ### SIMPLE FUNCTIONS ################################## # basal area function: calculates basal area from DBH values (must be in cm) basal.area.fn <- function(x){ (pi*(x)^2)/40000 } # calculate basal area in m^2 ### Function to draw random values from a truncated log normal distribution rtlnorm <- function (n, meanlog = 0, sdlog = 1, lower = -Inf, upper = Inf) { ret <- numeric() if (n > 1) n <- n while (length(ret) < n) { y <- rlnorm(n - length(ret), meanlog, sdlog) y <- y[y >= lower & y <= upper] ret <- c(ret, y) } stopifnot(length(ret) == n) ret } ### Function for simulating a bivariate normal distribution bivariate <- function(x,y){ mu1 <- 0 # expected value of x mu2 <- 0 # expected value of y sig1 <- 1 # variance of x sig2 <- 1 # variance of y rho <- 0.5 # corr(x, y) term1 <- 1 / (2 * pi * sig1 * sig2 * sqrt(1 - rho^2)) term2 <- (x - mu1)^2 / sig1^2 term3 <- -(2 * rho * (x - mu1)*(y - mu2))/(sig1 * sig2) term4 <- (y - mu2)^2 / sig2^2 z <- term2 + term3 + term4 term5 <- term1 * exp((-z / (2 *(1 - rho^2)))) return (term5) } ################################## ### DATA MANIPULATION ################################## # List to array function for Co_disp null model output objects list2ary = function(input.list){ #input a list of lists temp.ls <- vector("list") for(i in 1:length(input.list)) { temp.ls[i] <- input.list[[i]][1] } # take the dataframes out of the list and put them in a new list rows.cols <- dim(temp.ls[[1]]) sheets <- length(temp.ls) output.ary <- array(unlist(temp.ls), dim = c(rows.cols, sheets)) colnames(output.ary) <- colnames(temp.ls[[1]]) row.names(output.ary) <- row.names(temp.ls[[1]]) return(output.ary) # output as a 3-D array } ### Function to extract values from an environmental grid (raster) at point locations in a ppp object. # Inputs are the ppp and geoR.env, which is a geoR object holding the environmental data layer. # Extract works with a buffer around each point (10cm in this case) # geoR.env <- geo.elev extract.env.fn <- function(ppp.dat,geoR.env,xmin,xmax,ymin,ymax,quad.size=quad.size){ ppp.df <- data.frame(x=ppp.dat$x,y=ppp.dat$y) # create a dataframe from the ppp object X = geoR.env$coords[,1]+quad.size/2 Y = geoR.env$coords[,2]+quad.size/2 rast <- raster() # create empty raster to add data to extent(rast) <- extent(c(xmin=xmin,xmax=xmax,ymin=ymin,ymax=ymax)) ncol(rast) <- xmax/quad.size nrow(rast) <- ymax/quad.size raster.env <- rasterize(cbind(X,Y), rast, geoR.env$data) env.dat <- extract(x=raster.env,y=ppp.df,df=TRUE) # extract environmental data at tree locations env.out <- data.frame(x=ppp.df$x,y=ppp.df$y,z=env.dat$layer) env.geo <- as.geodata(env.out,coords.col=1:2,data.col=3) return(env.geo) } #### Function to generate a geodata object (used by packages geoR and the codispersion function) from a ppp object. # ppp.dat = input ppp object # xmin, xmax, ymin, ymax = plot dimensions # method = the measure that is used to generate the 'data' value for the geodata object ppp.to.geoR.fn <- function(ppp.dat,xmin,xmax,ymin,ymax,quad.size,method=c("abundance","mean.mark","mean.ba","total.ba")){ # function to generate geoR objects with abundance and basal area in 20x20m quadrats. Note that DBH must be measured in cm. Input data= ppp object. x <- ppp.dat$x # extract x coordinate y <- ppp.dat$y # extract y coordinate z <- ppp.dat$marks # extract DBH values ba <- (pi*(z)^2)/40000 # calculate basal area in m^2 xt <- cut(x,seq(xmin,xmax,quad.size)) # cut x coordinates using 20m spacing yt <- cut(y,seq(ymin,ymax,quad.size)) # cut y coordinates using 20m spacing coords <- dimnames(table(yt,xt)) # extract quadrat coordinate lists qx <- rep(seq(xmin,xmax-quad.size,length=length(coords$xt)),each=length(coords$yt)) # vector of x coordinates for the bottom left corner of the quadrat qy <- rep(seq(ymin,ymax-quad.size,length=length(coords$yt)),length(coords$xt)) # vector of y coordinates for the bottom left corner of the quadrat if(method=="abundance"){ out.grid <- table(yt,xt) # count the trees in each quadrat out.grid[is.na(out.grid)==T] <- 0 # replace NAs in table with zeros for empty quadrats } if(method=="mean.mark"){ out.grid <- tapply(z,list(yt,xt),mean) # calculate mean DBH in each quadrat out.grid[is.na(out.grid)==T] <- 0 } if(method=="mean.ba"){ out.grid <- tapply(ba,list(yt,xt),mean) # calculate mean ba in each quadrat out.grid[is.na(out.grid)==T] <- 0 } if(method=="total.ba"){ out.grid <- tapply(ba,list(yt,xt),sum) # calculate total ba in each quadrat out.grid[is.na(out.grid)==T] <- 0 } out.df <- data.frame(qx,qy,as.vector(out.grid)) out.geo <- as.geodata(out.df,coords.col=1:2,data.col=3) return(out.geo) } # end function ################################## ### CODISP ANALYSIS ################################## #### Modified codispersion function (modified from Cuevas et al. 2013) ## See 'Box 1' in paper for a detailed explanation. Codisp.Kern<-function(X,Y,k,h,gamma=1) { Kernel<-function(u,gamma) { v=0 v=ifelse(abs(u)<=1,(1/beta(0.5,gamma+1))*(1-u^2)^gamma,0) } ifelse(X$coords==Y$coords,1, { break print("The coordinates of X and Y are different") }) n=length(X$data) mX <- matrix(X$data,nrow=n,ncol=n,byrow=FALSE) mY <- matrix(Y$data,nrow=n,ncol=n,byrow=FALSE) MatriXX <- (mX - t(mX))^2 MatriYY <- (mY - t(mY))^2 MatriXY <- (mX - t(mX))*(mY - t(mY)) mX <- matrix(X$coords[,1],nrow=n,ncol=n,byrow=FALSE) DesignX <- mX - t(mX) mY <- matrix(X$coords[,2],nrow=n,ncol=n,byrow=FALSE) DesignY <- mY - t(mY) KERNMATRIXX=Kernel((k[1]-DesignX)/h[1],gamma)*Kernel((k[2]-DesignY)/h[1],gamma) if(h[1]==h[2]&h[1]==h[3]){ KERNMATRIYY=KERNMATRIXX KERNMATRIXY=KERNMATRIXX } else{ KERNMATRIYY=Kernel((k[1]-DesignX)/h[2],gamma)*Kernel((k[2]-DesignY)/h[2],gamma) KERNMATRIXY=Kernel((k[1]-DesignX)/h[3],gamma)*Kernel((k[2]-DesignY)/h[3],gamma) } Numerador=sum(KERNMATRIXY*MatriXY)/(2*sum(KERNMATRIXY)) Denominador1=sum(KERNMATRIYY*MatriYY)/(2*sum(KERNMATRIYY)) Denominador2=sum(KERNMATRIXX*MatriXX)/(2*sum(KERNMATRIXX)) v1=Denominador1 v2=Denominador2 v3=Numerador v4=Numerador/sqrt(Denominador1*Denominador2) print(c(v1,v2,v3,v4)) } #### Function to run co-dispersion plot-level analyses. Input is two geodata objects (e.g. abundance and basal area) codisp.coef.fn <- function(geodata1,geodata2){ out <- vector("list",length=3) out[[1]] <- codisp(geodata1$data,geodata2$data,geodata1$coords,nclass=20) # calculate the codispersion coefficent for the whole plot out[[2]] <- cor.spatial(geodata1$data,geodata2$data,geodata1$coords) # Tjostheim's Coef out[[3]] <- modified.ttest(geodata1$data,geodata2$data,geodata1$coords,nclass=20) # modified t-test return(out) } #### Function to run co-dispersion window analysis (modified from Cuevas et al. 2013) # geodata1 = first input data object (a geoR geodata object) # geodata2 = second input object # h = c(h1, h2, h3) = a vector of three bandwidth values for X, Y and XY # grid.cell.size = the size of the cells in the geodata objects # max.window.size = the maximum lag distance codisp.fn <- function(geodata1,geodata2,h=h,grid.cell.size=grid.cell.size,max.window.size=max.window.size){ out <- vector("list",length=2) X=geodata1 # input data process 1 Y=geodata2 # input data process 2 h=c(h[1],h[2],h[3]) # Set the bandwith for the kernel k_range <- max.window.size # set the spatial lags over which to calculate codisp k1=seq(-k_range,k_range,l=20) # x-axis values for codispersion graph (lags) k2=seq(grid.cell.size,k_range,l=10) # y-axis values for codispersion graph (lags) MCodisp=matrix(0,ncol=10,nrow=20) # loop through the lags for(i in 1:20) # 'left-right' lags { for(j in 1:10) # 'up' lags { MCodisp[i,j]=Codisp.Kern(X,Y,c(k1[i],k2[j]),h)[4]; # calculate codisp } } Codispersion <- as.numeric(MCodisp) # save codisp object as output xx <- rep(k1,length(k2)) # write out values for x-axis yy <- rep(k2,each=length(k1)) # write out values for y-axis graphing.data <- data.frame(xx,yy,Codispersion) # graphing object # put both the graphing object and the original object in an output list out[[1]] <- graphing.data out[[2]] <- MCodisp return(out) } ### NULL MODELS #### Function to generate a list of 'nsim' ppp objects (marked point patterns) under three different null models ppp.null.fn <- function(ppp.dat,nsim,model=c("RLM","HomP","HetP")){ #ppp.dat <- ppp.dat[[1]] ppp.out <- vector("list",nsim) # create output list object if(model=="RLM"){ # Random labelling model for(i in 1:nsim){ # start loop to generate simulations ppp.out[[i]] <- rlabel(ppp.dat, labels=marks(ppp.dat), permute=TRUE) # randomise marks } # end simulations loop } # end RLM loop if(model=="HomP"){ # Homogeneous Poisson model for(i in 1:nsim){ # start loop to generate simulations ppp.HomP <- rpoint(ppp.dat$n,win=ppp.dat$win) # randomise the observed ppp ppp.HomP$marks <- sample(ppp.dat$marks, replace=F) # assign shuffled marks to new ppp ppp.out[[i]] <- ppp.HomP # add new marked ppp to output list } # end simulations loop } # end HomP loop if(model=="HetP"){ # this null model generates random marks based on a lognormal fit to the DBH distribution intensity_function <- density.ppp(ppp.dat, bw.diggle) # generate the intensity function LN_params <- fitdistr(ppp.dat$marks,"log-normal") # fit lognormal to DBH distribution for(i in 1:nsim){ # start loop to generate simulations ppp.HetP <- rpoispp(intensity_function) # generate randomised ppp using intensity function ppp.HetP$marks <- rtlnorm(ppp.HetP$n,meanlog=LN_params$estimate[[1]],sdlog=LN_params$estimate[[2]],1,max(ppp.dat$marks)) # generate marks using parameters of DBH distribution ppp.out[[i]] <- ppp.HetP # add new marked ppp to output list } # end simulations loop } # end HetP loop return(ppp.out) } # end function ################################## ### DEALING WITH CODISP OUTPUTS ################################## # Comparing observed values to null model results from output arrays and graphing average null model results # inputs are the null model input array object, the observed CoDisp result list, and a choice of null model # This function deals with abundance and basal area values comparison.abba.fn = function(null.input.ary,CoDisp_obs,spe=spe,model=c("RLM","HomP","HetP")){ out.df <- CoDisp_obs[[1]] # extract the observed Codispersion result as a dataframe for(i in 1:length(null.input.ary[,1,1])){ # loop through each cell nsims <- length(null.input.ary[1,1,]) obser <- out.df$Codispersion[i] # observed codispersion value expec <- null.input.ary[i,3,] prop.greater.than <- length(which(expec>obser))/nsims prop.less.than <- length(which(expecobser))/nsims prop.less.than <- length(which(expecobser))/nsims prop.less.than <- length(which(expecobser))/nsims prop.less.than <- length(which(expec(pattern.scale/100)),n) new.x <- pp$x[nndists.rows] new.y <- pp$y[nndists.rows] ppp.pairs.sim[[2]] <- as.ppp(cbind(new.x,new.y),W=owin(c(xmin/100,xmax/100),c(ymin/100,ymax/100))) } # end segregated associaton # 4. Print map of points if desired if(Print=="TRUE"){ par(mfrow=c(1,2)) plot(ppp.pairs.sim[[1]],main=paste("sp 1\n pattern =",sp1.pattern),cex.main=0.7) plot(ppp.pairs.sim[[2]],main=paste("sp 2\n association =\n",association),cex.main=0.7) # 5. Rescale the point patterns in metres for(i in 1:2){ ppp.pairs.sim[[i]]$window <- owin(c(xmin,xmax),c(ymin,ymax)) ppp.pairs.sim[[i]]$x <- ppp.pairs.sim[[i]]$x*100 ppp.pairs.sim[[i]]$y <- ppp.pairs.sim[[i]]$y*100 } } return(ppp.pairs.sim) } # end function