B R code collection
This appendix includes a collection of the R functions used in the compendium.
An RData-file containing all the functions as well as relevant R objects for the songbird example can be downloaded here:
Download CompendiumCode.RDataTo import this file into R, use load("CompendiumCode.RData")
(after saving the file in your R working directory folder). Then all functions and objects should be visible in the ‘Environment’ tab.
B.2 Matrix population models
B.2.1 Create Leslie matrix
Build Leslie matrix from vectors of survival and fertility coefficients (need to have same length). If the last survival coefficient is not zero, individuals can accumulate in the final class.
<- function(Svec, Fvec){
Create.Amat <- length(Svec)
k <- matrix(0,k,k)
MatA1,] <- Fvec
MatA[for(i in 1:(k-1)){
+1,i] <- Svec[i]
MatA[i
}<- Svec[k]
MatA[k,k]
MatA }
B.2.2 Decompose \(\mathbf{U}\) and \(\mathbf{F}\)
Decompose the matrices \(\mathbf{U}\) and \(\mathbf{F}\) based on equation to obtain the transition matrix \(\mathbf{G}\) vector of survival probability \(\mathbf{s}\) (from \(\mathbf{U}\)), and the transition matrix \(\mathbf{Q}\) the fertility vector \(\mathbf{f}\) (from \(\mathbf{F}\))
<- function(MatU){
DecomposeU <-dim(MatU)[1]
kif(is.na(MatU[k,k])){
<-0
MatU[k,k]
}<- apply(MatU,2,sum)
Svec <- t(t(MatU)/Svec)
MatG for(i in 1:k){
if(Svec[i]==0){
<- 0
MatG[,i] <- 1
MatG[i,i]
}
}list("Gmat"=MatG, "Survival"=Svec)
}
<- function(MatF){
DecomposeF <- dim(MatF)[1]
k <- apply(MatF,2,sum)
Fvec <- matrix(0,k,k)
MatQ for(i in 1:k){
if(Fvec[i]==0){
1,i]<-1
MatQ[
}else{
<-MatF[,i]/Fvec[i]
MatQ[,i]
}
}list("Qmat"=MatQ, "Fertility"=Fvec)
}
B.2.3 Population projection
Project size of stages over time given a projection matrix and an initial population vector. Return as data frame.
<- function(MatA, Tmax = 50, n0){
projection <- dim(MatA)[1]
k if(length(n0)!=k){
warning("n0 should have length
k corresponding to
number of stages in Amat")
}<- matrix(NA, nrow= Tmax+1, ncol=k)
Nmat <- paste("n", 1:k, sep="")
cnames <- 0:Tmax
timesteps 1,] = n0
Nmat[for(i in 2:(Tmax+1)){
= MatA%*%Nmat[i-1,]
Nmat[i,]
}<- data.frame(timesteps, Nmat)
frame colnames(frame) <- c("time", cnames)
frame }
B.2.4 Long-term growth rate, stable structure and reproductive value
For a projection matrix \(\mathbf{A}\), the function returns the asymptotic growth rate \(\lambda\), a vector describing the stable stage structure \(\mathbf{u}\), and a vector describing the stage-specific reproductive values \(\mathbf{v}'\). NB: We scale the reproductive values so that \(\mathbf{v}'\mathbf{u}=1\).
<- function(MatA){
uvlambda <- eigen(MatA)
ev <- eigen(t(MatA))
tev <- which.max(Re(ev$values))
lmax <- ev$vectors
U <- tev$vectors
V <- as.matrix(abs(Re(U[, lmax]))/
u sum(abs(Re(U[, lmax]))))
<- u/(sum(u)) #scale u
u <- as.matrix(abs(Re(V[, lmax])))
v <- v/sum(u*v ) #scale v
v <- t(ifelse(u*v <= 0, 0, v))
v return(list("lambda"=max(Re(ev$values)),
"u"=u,
"v"=v))
}
B.2.5 Life expectancy
For a projection matrix MatU, the function returns the vector of life expectancies for given stages. Life expectancy at the first stage is the first element of this vector.
<- function(MatU){
LifeExpectancy <-dim(MatU)[1]
kif(is.na(MatU[k,k])){
<-0
MatU[k,k]
}=dim(MatU)[1]
uDim= solve(diag(uDim[1])-MatU)
N colSums(N)
}
B.2.6 Lifetime reproduction
The function returns the mean lifetime reproduction \(R_0\), based on the matrices \(\mathbf{A}\) and \(\mathbf{U}\). and the vector of expected remaining lifetime reproduction by stage. If all offspring are born into the first stage, the first element also corresponds to \(R_0\).
<- function(MatU, MatF){
R0function <- dim(MatU)[[1]]
k <- MatF%*%solve(diag(1, k, k)-MatU)
Rmat <- apply(Rmat,2,sum)
Rvec <- uvlambda(MatA=Rmat)$lam
R0 return(list("R0"= R0, "Rvec"= Rvec))
}
B.2.7 Generation time
For a projection matrix MatA and a reproduction matrix MatF, the function returns the generation time estimated as the mean age of mothers in a population with stable structure.
<- function(MatA, FMat){
GenTime <- uvlambda(MatA=MatA)
res <- res$lam
lam <- res$u
u <- res$v
v /(v%*%FMat%*%u)
lam }
B.2.8 Sensitivities and elasticities
For a given projection matrix \(\mathbf{A}\), the function sensitivity.matrix()
returns the matrix of sensitivities of \(\lambda\) to each corresponding element of MatA, and the function elasticity.matrix()
returns the elasticities. The argument ‘zeroes’ can be set to FALSE to return also the sensitivities / elasticities corresponding to elements of MatA that are zero.
<- function(MatA, zeroes=T){
sensitivity.matrix <- uvlambda(MatA=MatA)
res <- t(res$u%*%res$v)
sensmat if (zeroes==T){
<- ifelse (MatA==0, 0, sensmat)
sensmat
}
sensmat }
<- function(MatA, zeroes=T){
elasticity.matrix <- uvlambda(MatA=MatA)
res <- t(res$u%*%res$v)
sensmat if (zeroes==T){
<- ifelse (MatA==0, 0, sensmat)
sensmat
}*MatA/res$lam
sensmat }
B.3 Stochastic models
B.3.1 Arithmetic and geometric mean
The function calculates the arithmetic and geometric mean of a sequence of annual population growth rates \(\Lambda_t\) with a given mean and standard deviation. It returns a data frame with time steps, the generated random sequence of annual growth rates \(\Lambda_t\), and the two means.
<- function(Tmax=100, mean.lambda=1.05, sd.lambda=.3){
AGMeans #Use lognormal distribution to avoid negative values
<- rlnorm(n=Tmax,
Lambdavec log(mean.lambda)-
5*log(1+ sd.lambda^2/
.^2)),
(mean.lambdasdlog=sqrt(log(1+sd.lambda^2/
^2))))
(mean.lambda<- exp(mean(log(Lambdavec)))
geomLambda <- mean(Lambdavec)
meanLambda data.frame("Time"=0:(length(Lambdavec)-1),
"Lambda"=Lambdavec,
"Geometric"=geomLambda,
"Arithmetic"=meanLambda)
}
B.3.2 Simulations in Lewontin-Cohen model
Function to generate one realization of the model, given initial population size, mean and standard deviation of the annual growth rates \(\Lambda_t\):
<- function(Tmax=30,
LC n0=100,
mean.lambda=1.05,
sd.lambda=0.1){
<- rnorm(Tmax,
stochastic.lambdas mean=mean.lambda,
sd=sd.lambda)
<- rep(NA, Tmax+1)
Nvec 1] <- n0
Nvec[for(i in 1:Tmax){
+1] <- stochastic.lambdas[i]*Nvec[i]
Nvec[i
}data.frame("Time"=0:Tmax, "N"=Nvec)
}
Function to generate a chosen number (nsim
) of realizations of the Lewontin-Cohen model, returned in a data frame.
<- function(nsim=100,
nsimLC Tmax=30,
n0=100,
mean.lambda=1.05,
sd.lambda=0.1){
<- data.frame("Time"=0:Tmax)
frame for(j in 1:nsim){
<- rnorm(Tmax,
stochastic.lambdas mean=mean.lambda,
sd=sd.lambda)
<- rep(NA, Tmax+1)
Nvec 1] <- n0
Nvec[for(i in 1:Tmax){
+1] <- stochastic.lambdas[i]*Nvec[i]
Nvec[i
}<- cbind(frame, Nvec)
frame names(frame)[j+1] <- paste("N",j,sep="")
}
frame }
B.3.3 Simulations of stochastic structured model
B.3.3.1 Generate sequence of projection matrices
The function below returns a time series of stochastic projection matrices as an array, for a model assuming stochasticity in survival coefficients and fertility coefficients only, and no correlation between matrix elements (can be modified to include such correlation). Fertility is modeled with a log-link (normally distributed on log-scale), while survival is modeled with a log-log-link (normally distribued on log-log-scale). The mean (constant) environment is determined by the input matrices \(\mathbf{A}\), \(\mathbf{U}\) and \(\mathbf{F}\). The input vectors VarF
and VarS
specify the variances of fertility and survival on their respective scales.
<- function(MatA,
Amat.array
MatU,
MatF, tmax=30,
VarF=rep(0.03, 3),
VarS=rep(0.03, 3)){
#Split survival/transition matrix:
<- DecomposeU(MatU)
SplitU #Split fertility matrix:
<- DecomposeF(MatF)
SplitF #Transition matrix:
<- SplitU$Gmat
Gmat #Survival vector:
<- SplitU$Survival
Svec #Define beta (log-log link function):
<- -log(-log(Svec))
Beta.S #Offspring transition matrix:
<- SplitF$Qmat
Qmat #Fertility vector:
<- SplitF$Fertility
Fvec #Define beta (log link function):
<- log(Fvec)
Beta.F
#variance covariance matrix
<- diag(c(VarS, VarF))
SigMat #Draw random (scaled) values for S and F:
<- mvrnorm(tmax, mu=c(Beta.S, Beta.F), Sigma=SigMat)
SFTime #Number of matrix classes
<- length(Svec)
k #Rescaled survival probabilities (loglog link):
<- exp(-exp(-SFTime[,1:k]))
SvecTime#Rescaled fertilities (log link):
<- exp(SFTime[,(k+1):(2*k)])
FvecTime#Build projection matrix for each time step:
<- array(NA,dim=c(k,k,tmax))
A.array for (i in 1:tmax){
<- diag(SvecTime[i,])
Smat <- diag(FvecTime[i,])
Fmat <- Gmat%*%Smat+Qmat%*%Fmat
A.array[,,i]
}return("Amats" = A.array)
}
B.3.3.2 Stochastic population projection
The function below returns a data frame with the size of each stage over time, for a given array of projection matrices (third dimension should correspond to time) and initial population vector n0
.
<-function(Amats, n0){
projection.stochastic <- dim(Amats)[3]
Tmax <- dim(Amats)[1]
k if(length(n0)!=k){
warning("n0 should have length k
corresponding to number of
stages in the Amats object")
}<- matrix(NA,nrow= Tmax+1, ncol=k)
Nmat 1,] <- n0
Nmat[<- paste("n", 1:k, sep="")
cnames <- 0:Tmax
timesteps for(i in 2:(Tmax+1)){
<- Amats[,,i-1] %*% Nmat[i-1,]
Nmat[i,]
}<- data.frame(timesteps,Nmat)
frame colnames(frame) <- c("time", cnames)
frame }
The following function generates a series of stochastic matrices nsim
times, each series of length Tmax
, and returns the projected total population size over time for each series, in a data frame. Other input variables are the constant matrices and variance vectors, and initial population vector.
<- function(nsim=100,
nsim.stochastic tmax=30,
n0=rep(10,3),
MatA,
MatU,
MatF,
VarF,
VarS){<- data.frame("Time"=0:tmax)
frame for(m in 1:nsim){
<- Amat.array(MatA=MatA,
AMATS MatU=MatU,
MatF=MatF,
VarF=VarF,
VarS=VarS,
tmax=tmax)
<- apply(projection.stochastic(Amats=AMATS,
projectN n0=n0)[,-1],1,sum)
<- cbind(frame, projectN)
frame names(frame)[m+1] <- paste("Rep",m,sep="")
}
frame }
B.3.4 Estimate stochastic growth rate
The following code can be used to calculate the stochastic growth rate following the definition of equation (5.5), given one sequence of projection matrices. The cutoff argument removes the first time steps (corresponding to cutoff number).
<- function(Amats, n0, cutoff=20){
EstimateSGR <- dim(Amats)[3]
Tmax if(cutoff>=Tmax){
warning("cutoff must be lower
than the number of
matrices in Amats")
}<- projection.stochastic(Amats=Amats, n0=n0)
project <- apply(project[,-1],1,sum)
Ntot <- log(Ntot[cutoff:length(Ntot)])
logN <- length(logN)
TM -logN[1])/TM
(logN[TM] }
B.4 Parameters and matrices for the songbird example
Copy paste if you want to look at example code without running all the previous code.
#------------------------------
#Life table parameters
#------------------------------
<- 0:3
x <- c(0, 0, 3, 6)
mx <- c(1, 0.2, 0.18, 0.108)
lx <- c(.2, .9, .6, 0)
px
#------------------------------
#Life table calculations
#------------------------------
<- TC.lifetable(x, lx, mx)
TC_Bird <- R0.lifetable(lx, mx)
R0_Bird
#------------------------------
#Apply uniroot to find lambda
#(EulerLotka defined among funcions)
#------------------------------
<- uniroot(EulerLotka,
lambdabird x=x,
lx=lx,
mx=mx,
interval=c(.1,2))$root
#------------------------------
#Projection matrices
#------------------------------
<- Create.Amat(
Abird.pre <- c(.9, .6, 0),
Svec Fvec= c(0, .6, 1.2))
<- Create.Amat(
Abird.post <- c(.2, .9, 0),
Svec Fvec= c(0, 2.7, 3.6))
#------------------------------
#Projection
#------------------------------
<- projection(
Pop.bird.pre MatA=Abird.pre,
n0=rep(10,3),
Tmax=30)
<- projection(
Pop.bird.post MatA=Abird.post,
n0=rep(10,3),
Tmax=30)