|
| 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