Skip to content

Commit 2ebd40d

Browse files
authored
Add files via upload
1 parent ee42191 commit 2ebd40d

File tree

1 file changed

+315
-0
lines changed

1 file changed

+315
-0
lines changed

EchidnaPopModel.R

Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
##########################################################################################################################################
2+
## echidna (Tachyglossus aculeatus) demographic model
3+
##
4+
## Corey Bradshaw
5+
## corey.bradshaw@flinders.edu.au
6+
## Flinders University, September 2021
7+
##########################################################################################################################################
8+
9+
## functions
10+
# beta distribution shape parameter estimator function
11+
estBetaParams <- function(mu, var) {
12+
alpha <- ((1 - mu) / var - 1 / mu) * mu ^ 2
13+
beta <- alpha * (1 / mu - 1)
14+
return(params = list(alpha = alpha, beta = beta))
15+
}
16+
17+
AICc <- function(...) {
18+
models <- list(...)
19+
num.mod <- length(models)
20+
AICcs <- numeric(num.mod)
21+
ns <- numeric(num.mod)
22+
ks <- numeric(num.mod)
23+
AICc.vec <- rep(0,num.mod)
24+
for (i in 1:num.mod) {
25+
if (length(models[[i]]$df.residual) == 0) n <- models[[i]]$dims$N else n <- length(models[[i]]$residuals)
26+
if (length(models[[i]]$df.residual) == 0) k <- sum(models[[i]]$dims$ncol) else k <- (length(models[[i]]$coeff))+1
27+
AICcs[i] <- (-2*logLik(models[[i]])) + ((2*k*n)/(n-k-1))
28+
ns[i] <- n
29+
ks[i] <- k
30+
AICc.vec[i] <- AICcs[i]
31+
}
32+
return(AICc.vec)
33+
}
34+
35+
delta.AIC <- function(x) x - min(x) ## where x is a vector of AIC
36+
weight.AIC <- function(x) (exp(-0.5*x))/sum(exp(-0.5*x)) ## Where x is a vector of dAIC
37+
ch.dev <- function(x) ((( as.numeric(x$null.deviance) - as.numeric(x$deviance) )/ as.numeric(x$null.deviance))*100) ## % change in deviance, where x is glm object
38+
39+
linreg.ER <- function(x,y) { # where x and y are vectors of the same length; calls AICc, delta.AIC, weight.AIC functions
40+
fit.full <- lm(y ~ x); fit.null <- lm(y ~ 1)
41+
AIC.vec <- c(AICc(fit.full),AICc(fit.null))
42+
dAIC.vec <- delta.AIC(AIC.vec); wAIC.vec <- weight.AIC(dAIC.vec)
43+
ER <- wAIC.vec[1]/wAIC.vec[2]
44+
r.sq.adj <- as.numeric(summary(fit.full)[9])
45+
return(c(ER,r.sq.adj))
46+
}
47+
48+
## source
49+
source("matrixOperators.r")
50+
51+
52+
##############################
53+
## TACHYGLOSSUS (aculeatus) (TA)
54+
55+
# mass
56+
TA.mass <- 4.0 # mean(c(3.8,3.4,3.7,(mean(c(3.9,7))))) # short-beaked echidna Tachyglossus aculeatus (Nicol & Andersen 2007)
57+
58+
## predicted rm (from Henneman 1983 Oecologia 56:104-108)
59+
## log10rm = 0.6914 - 0.2622*log10m (mass in g)
60+
TA.rm.allom.pred <- 10^(0.6914 - (0.2622*log10(TA.mass*1000)))
61+
TA.rm.pred <- 0.40 # rm/year = 0.40 (Schmidt-Nielsen K, Dawson T J, Crawford EC (1966) Temperature regulation in the echidna (Tachyglossus aculeatus) J Cell Physiol 67 : 63-72)
62+
TA.lm.pred <- exp(TA.rm.pred)
63+
64+
## theoretical population density for mammalian herbivores based on body size (Damuth 1981; Freeland 1990)
65+
TA.D.pred.up <- (10^(1.91 + (-1.02*log10(TA.mass))))/2 # divided by 2 for females only
66+
TA.D.pred.lo <- (10^(-1.17 + (-0.76*log10(TA.mass))))/2 # divided by 2 for females only
67+
TA.D.pred <- TA.D.pred.up # animals/km2 (checks out)
68+
69+
## max age
70+
## non-volant birds & mammals (Healy K et al. 2014 PRSB)
71+
## log10ls = 0.89 + 0.13log10m (mass in grams; ls = years)
72+
TA.age.max.allom <- round(10^(0.89 + (0.13*log10(TA.mass*1000))), 0)
73+
TA.age.max.allom # underestimated (torpor, hibernation, low BMR)
74+
TA.age.max <- 45 # (Nicol & Andersen 2007)
75+
76+
## age vector
77+
TA.age.vec <- 0:TA.age.max
78+
79+
## fertility
80+
## total fecundity from Allainé et al. 1987 (Oecologia)
81+
## lnF = 2.719 - 0.211lnM (all mammals)
82+
TA.F.allom.pred <- exp(2.719 - (0.211*log(TA.mass*1000)))/2 # divided by 2 for females
83+
TA.F.allom.pred
84+
TA.F.egg <- 1/2 # one egg/year; /2 for daughters
85+
TA.F.pbreed <- 0.55 # females reproductively active (Nicol & Morrow 2012)
86+
# 17 females produced 22 young over 7 years: 22/17/7 = 0.1849, or 0.1849/2 = 0.0924 (Rismiller & McKelvey 2000)
87+
TA.F.pred <- TA.F.egg*TA.F.pbreed
88+
89+
## age at primiparity
90+
## lnalpha = 0.214 + 0.263*lnM (https://dx.doi.org/10.1093%2Fgerona%2F62.2.149)
91+
TA.alpha.allom <- ceiling(exp(-1.34 + (0.214*log(TA.mass*1000))))
92+
TA.alpha <- 3
93+
94+
## survival
95+
## mean adult survival (McCarthy et al. 2008 Am Nat)
96+
## ln{-ln[s(t)]} = ln(a) + bln(M) + ln (t)
97+
ln.a.s <- -0.5; b.s <- -0.25
98+
TA.s.tran <- ln.a.s + b.s*log(TA.mass*1000) + log(1)
99+
TA.s.ad.yr.allom <- exp(-exp(TA.s.tran))
100+
TA.s.ad.yr.allom
101+
TA.s.ad.yr <- mean(c(0.94,0.98))
102+
103+
# Siler hazard h(x) (Gurven et al. 2007)
104+
a1 <- 1 - (1.05*TA.s.ad.yr) # initial infant mortality rate (also known as αt)
105+
b1 <- 1.9 # rate of mortality decline (also known as bt)
106+
a2 <- 1 - (TA.s.ad.yr) # age-independent mortality (exogenous mortality due to environment); also known as ct
107+
a3 <- 0.1e-04 # initial adult mortality rate (also known as βt)
108+
b3 <- 0.13 # rate of mortality increase
109+
longev <- TA.age.max
110+
x <- seq(0,longev,1) # age vector
111+
h.x <- a1 * exp(-b1*x) + a2 + a3 * exp(b3 * x) # Siler's hazard model
112+
plot(x,h.x,pch=19,type="l")
113+
plot(x,log(h.x),pch=19,type="l")
114+
l.x <- exp((-a1/b1) * (1 - exp(-b1*x))) * exp(-a2 * x) * exp(a3/b3 * (1 - exp(b3 * x))) # Siler's survival (proportion surviving) model
115+
init.pop <- 10000
116+
lx <- round(init.pop*l.x,0)
117+
len.lx <- length(lx)
118+
dx <- lx[1:(len.lx-1)]-lx[2:len.lx]
119+
qx <- dx/lx[1:(length(lx)-1)]
120+
TA.Sx <- c(0.99*TA.s.ad.yr, 1 - qx)
121+
plot(x, TA.Sx, pch=19, type="l", xlab="age (years)", ylab="Sx")
122+
TA.s.sd.vec <- 0.05*TA.Sx
123+
124+
## pre-breeding design with 0-1 survival in first row
125+
TA.m.vec <- c(0, 0, 0, 0.5*TA.F.pred, 0.75*TA.F.pred, rep(TA.F.pred,(TA.age.max-4))) #
126+
TA.m.sd.vec <- 0.05*TA.m.vec
127+
plot(TA.age.vec, TA.m.vec, type="b", pch=19, xlab="age (yrs)", ylab="m")
128+
129+
# fit sigmoidal function
130+
# logistic power function y = a / (1+(x/b)^c)
131+
TA.m.dat <- data.frame(TA.age.vec, TA.m.vec)
132+
param.init <- c(0.5, 4, -4)
133+
TA.fit.logp <- nls(TA.m.vec ~ a / (1+(TA.age.vec/b)^c),
134+
data = TA.m.dat,
135+
algorithm = "port",
136+
start = c(a = param.init[1], b = param.init[2], c = param.init[3]),
137+
trace = TRUE,
138+
nls.control(maxiter = 1000, tol = 1e-05, minFactor = 1/1024))
139+
TA.fit.logp.summ <- summary(TA.fit.logp)
140+
plot(TA.age.vec, TA.m.vec, type="b", pch=19, xlab="age (yrs)", ylab="m")
141+
TA.age.vec.cont <- seq(0,max(TA.age.vec),1)
142+
TA.pred.p.mm <- coef(TA.fit.logp)[1] / (1+(TA.age.vec.cont/coef(TA.fit.logp)[2])^coef(TA.fit.logp)[3])
143+
#DN.pred.p.mm <- ifelse(DN.pred.p.m > 1, 1, DN.pred.p.m)
144+
lines(TA.age.vec.cont, TA.pred.p.mm,lty=2,lwd=3,col="red")
145+
146+
## create matrix
147+
TA.popmat <- matrix(data = 0, nrow=TA.age.max+1, ncol=TA.age.max+1)
148+
diag(TA.popmat[2:(TA.age.max+1),]) <- TA.Sx[-1]
149+
TA.popmat[TA.age.max+1,TA.age.max+1] <- TA.Sx[TA.age.max+1]
150+
TA.popmat[1,] <- TA.pred.p.mm
151+
colnames(TA.popmat) <- c(0:TA.age.max)
152+
rownames(TA.popmat) <- c(0:TA.age.max)
153+
TA.popmat.orig <- TA.popmat ## save original matrix
154+
155+
## matrix properties
156+
max.lambda(TA.popmat.orig) ## 1-yr lambda
157+
TA.lm.pred
158+
max.r(TA.popmat.orig) # rate of population change, 1-yr
159+
TA.ssd <- stable.stage.dist(TA.popmat.orig) ## stable stage distribution
160+
plot(TA.age.vec, TA.ssd, type="l", pch=19, xlab="age (yrs)", ylab="ssd")
161+
R.val(TA.popmat.orig, TA.age.max) # reproductive value
162+
TA.gen.l <- G.val(TA.popmat.orig, TA.age.max) # mean generation length
163+
164+
## initial population vector
165+
area <- 500*500 # km × km
166+
TA.pop.found <- round(area*TA.D.pred, 0) # founding population size (estimated density * 100 × 100 km region [10,000 km2])
167+
TA.init.vec <- TA.ssd * TA.pop.found
168+
169+
#################
170+
## project
171+
## set time limit for projection in 1-yr increments
172+
yr.st <- 1
173+
#************************
174+
yr.end <- round(40*TA.gen.l, 0) # set projection end date
175+
#************************
176+
t <- (yr.end - yr.st)
177+
178+
TA.tot.F <- sum(TA.popmat.orig[1,])
179+
TA.popmat <- TA.popmat.orig
180+
yr.vec <- seq(yr.st,yr.end)
181+
182+
## set population storage matrices
183+
TA.n.mat <- matrix(0, nrow=TA.age.max+1,ncol=(t+1))
184+
TA.n.mat[,1] <- TA.init.vec
185+
186+
## set up projection loop
187+
for (i in 1:t) {
188+
TA.n.mat[,i+1] <- TA.popmat %*% TA.n.mat[,i]
189+
}
190+
191+
TA.n.pred <- colSums(TA.n.mat)
192+
yrs <- seq(yr.st, yr.end, 1)
193+
plot(yrs, log10(TA.n.pred),type="l",lty=2,pch=19,xlab="year",ylab="log10 N")
194+
195+
# compensatory density feedback
196+
TA.K.max <- 1*TA.pop.found
197+
TA.K.vec <- c(1, 0.25*TA.K.max, TA.K.max/2, 0.75*TA.K.max, TA.K.max)
198+
TA.red.vec <- c(1,0.993,0.97,0.93,0.8845)
199+
plot(TA.K.vec, TA.red.vec,pch=19,type="b")
200+
TA.Kred.dat <- data.frame(TA.K.vec, TA.red.vec)
201+
202+
# logistic power function a/(1+(x/b)^c)
203+
TA.param.init <- c(1, TA.K.max, 2)
204+
TA.fit.lp <- nls(TA.red.vec ~ a/(1+(TA.K.vec/b)^c),
205+
data = TA.Kred.dat,
206+
algorithm = "port",
207+
start = c(a = TA.param.init[1], b = TA.param.init[2], c = TA.param.init[3]),
208+
trace = TRUE,
209+
nls.control(maxiter = 1000, tol = 1e-05, minFactor = 1/1024))
210+
TA.fit.lp.summ <- summary(TA.fit.lp)
211+
plot(TA.K.vec, TA.red.vec, pch=19,xlab="N",ylab="reduction factor")
212+
TA.K.vec.cont <- seq(1,2*TA.pop.found,1)
213+
TA.pred.lp.fx <- coef(TA.fit.lp)[1]/(1+(TA.K.vec.cont/coef(TA.fit.lp)[2])^coef(TA.fit.lp)[3])
214+
lines(TA.K.vec.cont, TA.pred.lp.fx, lty=3,lwd=3,col="red")
215+
216+
TA.a.lp <- coef(TA.fit.lp)[1]
217+
TA.b.lp <- coef(TA.fit.lp)[2]
218+
TA.c.lp <- coef(TA.fit.lp)[3]
219+
220+
## compensatory density-feedback deterministic model
221+
## set population storage matrices
222+
TA.n.mat <- matrix(0, nrow=TA.age.max+1, ncol=(t+1))
223+
TA.n.mat[,1] <- TA.init.vec
224+
TA.popmat <- TA.popmat.orig
225+
226+
## set up projection loop
227+
for (i in 1:t) {
228+
TA.totN.i <- sum(TA.n.mat[,i])
229+
TA.pred.red <- as.numeric(TA.a.lp/(1+(TA.totN.i/TA.b.lp)^TA.c.lp))
230+
diag(TA.popmat[2:(TA.age.max+1),]) <- (TA.Sx[-1])*TA.pred.red
231+
TA.popmat[TA.age.max+1,TA.age.max+1] <- 0
232+
TA.popmat[1,] <- TA.m.vec
233+
TA.n.mat[,i+1] <- TA.popmat %*% TA.n.mat[,i]
234+
}
235+
236+
TA.n.pred <- colSums(TA.n.mat)
237+
plot(yrs, TA.n.pred, type="l",lty=2,pch=19,xlab="year",ylab="N")
238+
abline(h=TA.pop.found, lty=2, col="red", lwd=2)
239+
240+
## stochatic projection with density feedback
241+
## set storage matrices & vectors
242+
iter <- 500
243+
itdiv <- iter/10
244+
245+
TA.n.sums.mat <- matrix(data=NA, nrow=iter, ncol=(t+1))
246+
TA.s.arr <- TA.m.arr <- array(data=NA, dim=c(t+1, TA.age.max+1, iter))
247+
248+
for (e in 1:iter) {
249+
TA.popmat <- TA.popmat.orig
250+
251+
TA.n.mat <- matrix(0, nrow=TA.age.max+1,ncol=(t+1))
252+
TA.n.mat[,1] <- TA.init.vec
253+
254+
for (i in 1:t) {
255+
# stochastic survival values
256+
TA.s.alpha <- estBetaParams(TA.Sx, TA.s.sd.vec^2)$alpha
257+
TA.s.beta <- estBetaParams(TA.Sx, TA.s.sd.vec^2)$beta
258+
TA.s.stoch <- rbeta(length(TA.s.alpha), TA.s.alpha, TA.s.beta)
259+
260+
# stochastic fertilty sampler (gaussian)
261+
TA.fert.stch <- rnorm(length(TA.popmat[,1]), TA.m.vec, TA.m.sd.vec)
262+
TA.m.arr[i,,e] <- ifelse(TA.fert.stch < 0, 0, TA.fert.stch)
263+
264+
TA.totN.i <- sum(TA.n.mat[,i], na.rm=T)
265+
TA.pred.red <- TA.a.lp/(1+(TA.totN.i/TA.b.lp)^TA.c.lp)
266+
267+
diag(TA.popmat[2:(TA.age.max+1),]) <- (TA.s.stoch[-1])*TA.pred.red
268+
TA.popmat[TA.age.max+1,TA.age.max+1] <- 0
269+
TA.popmat[1,] <- TA.m.arr[i,,e]
270+
TA.n.mat[,i+1] <- TA.popmat %*% TA.n.mat[,i]
271+
272+
TA.s.arr[i,,e] <- TA.s.stoch * TA.pred.red
273+
274+
} # end i loop
275+
276+
TA.n.sums.mat[e,] <- ((as.vector(colSums(TA.n.mat))/TA.pop.found))
277+
278+
if (e %% itdiv==0) print(e)
279+
280+
} # end e loop
281+
282+
TA.n.md <- apply(TA.n.sums.mat, MARGIN=2, median, na.rm=T) # mean over all iterations
283+
TA.n.up <- apply(TA.n.sums.mat, MARGIN=2, quantile, probs=0.975, na.rm=T) # upper over all iterations
284+
TA.n.lo <- apply(TA.n.sums.mat, MARGIN=2, quantile, probs=0.025, na.rm=T) # lower over all iterations
285+
286+
par(mfrow=c(1,3))
287+
plot(yrs,TA.n.md,type="l", main = "", xlab="year", ylab="pN1", lwd=2, ylim=c(0.95*min(TA.n.lo),1.05*max(TA.n.up)))
288+
lines(yrs,TA.n.lo,lty=2,col="red",lwd=1.5)
289+
lines(yrs,TA.n.up,lty=2,col="red",lwd=1.5)
290+
291+
TA.s.add <- TA.m.add <- rep(0, TA.age.max+1)
292+
for (m in 1:iter) {
293+
TA.s.add <- rbind(TA.s.add, TA.s.arr[ceiling(TA.gen.l):(t+1),,m])
294+
TA.m.add <- rbind(TA.m.add, TA.m.arr[ceiling(TA.gen.l):(t+1),,m])
295+
}
296+
TA.s.add <- TA.s.add[-1,]
297+
TA.m.add <- TA.m.add[-1,]
298+
299+
TA.s.md <- apply(TA.s.add, MARGIN=2, median, na.rm=T) # mean s over all iterations
300+
TA.s.up <- apply(TA.s.add, MARGIN=2, quantile, probs=0.975, na.rm=T) # upper over all iterations
301+
TA.s.lo <- apply(TA.s.add, MARGIN=2, quantile, probs=0.025, na.rm=T) # lower over all iterations
302+
303+
plot(TA.age.vec,TA.s.md,type="l", main = "", xlab="age", ylab="s", lwd=2, ylim=c(0.95*min(TA.s.lo),1.05*(max(TA.s.up))))
304+
lines(TA.age.vec,TA.s.lo,lty=2,col="red",lwd=1.5)
305+
lines(TA.age.vec,TA.s.up,lty=2,col="red",lwd=1.5)
306+
307+
TA.m.md <- apply(TA.m.add, MARGIN=2, median, na.rm=T) # mean s over all iterations
308+
TA.m.up <- apply(TA.m.add, MARGIN=2, quantile, probs=0.975, na.rm=T) # upper over all iterations
309+
TA.m.lo <- apply(TA.m.add, MARGIN=2, quantile, probs=0.025, na.rm=T) # lower over all iterations
310+
311+
plot(TA.age.vec,TA.m.md,type="l", main = "", xlab="age", ylab="m", lwd=2, ylim=c(0.95*min(TA.m.lo),1.05*max(TA.m.up)))
312+
lines(TA.age.vec,TA.m.lo,lty=2,col="red",lwd=1.5)
313+
lines(TA.age.vec,TA.m.up,lty=2,col="red",lwd=1.5)
314+
par(mfrow=c(1,1))
315+

0 commit comments

Comments
 (0)