#Code for Figure 3.2. Try varying the value of rv from 0.5-2.8 yrs<-100; rv=2.8; Kv<-1000.0; Nz=100; catch=0.0; p=1.0ans<-discretelogistic(r=rv,K=Kv,N0=Nz,Ct=catch,Yrs=yrs,p=p)avcatch<-mean(ans[(yrs-50):yrs,"nt"],na.rm=TRUE)#used in text label<-paste0("r=",rv," K=",Kv," Ct=",catch, " N0=",Nz," p=",p=p)plot(ans, main=label, cex=0.9, font=7)#Schaefer dynamics
图 3.2: Schaefer 模型动态。左图是按年份的数值,展示了从 r 值为 2.8 开始的混沌动态。右图是时间 t+1 的数值与时间 t 的关系,称为相图。最后 20%的点用红色标出以展示任何平衡行为。灰色对角线是 1:1 线。
我们可以通过求 ans 中 \(nt\) 和 \(Nt1\) 列的行平均值来找到一个稳定的限制循环,查看最后 100 个值(四舍五入到小数点后三位)。table() 函数的名称标识循环点的值(如果有的话)。如果只有一个或两个平均值来确定一个渐近平衡或一个两周期稳定的限制循环。通过检查每个 ans 对象中值的时间序列,可以搜索所标识值的首次出现,从而确定循环行为(精确到小数点后三位)何时首次出现。我们将这些值四舍五入,并使用 600 年或更长的时间,因为如果我们使用所有 15 位小数,任何超过 8 的循环都可能无法被清楚地识别出来(尝试将 r = 2.63 更改为 5;并试着用 plot(ans) 绘图)。
代码
#run discretelogistic and search for repeated values of Nt yrs<-600ans<-discretelogistic(r=2.55,K=1000.0,N0=100,Ct=0.0,Yrs=yrs)avt<-round(apply(ans[(yrs-100):(yrs-1),2:3],1,mean),2)count<-table(avt)count[count>1]# with r=2.55 you should find an 8-cycle limit
我们可以建立一个程序来寻找 \(r\) 的值生成不同周期的稳定限制环,尽管下面的代码只是部分成功。通过设置一个 for 循环,将不同的值替换为 \(r\) 值输入到 discretelogistic(),我们可以搜索一个长时间序列的最后几年在数值的时间序列中唯一的值。然而,四舍五入误差可能会导致意想不到的结果,特别是在不同类型的动态行为之间的边界。我们可以通过将检查的数值四舍五入到小数点后三位来避免这些问题,但请尝试在下面的代码中散列该行,以查看问题变得更糟。
代码
#searches for unique solutions given an r value see Table 3.2testseq<-seq(1.9,2.59,0.01)nseq<-length(testseq)result<-matrix(0,nrow=nseq,ncol=2, dimnames=list(testseq,c("r","Unique")))yrs<-600for(iin1:nseq){# i = 31 rval<-testseq[i]ans<-discretelogistic(r=rval,K=1000.0,N0=100,Ct=0.0,Yrs=yrs)ans<-ans[-yrs,]# remove last year, see str(ans) for why ans[,"nt1"]<-round(ans[,"nt1"],3)#try hashing this out result[i,]<-c(rval,length(unique(tail(ans[,"nt1"],100))))}
#the R code for the bifurcation function bifurcation<-function(testseq,taill=100,yrs=1000,limy=0,incx=0.001){nseq<-length(testseq)result<-matrix(0,nrow=nseq,ncol=2, dimnames=list(testseq,c("r","Unique Values")))result2<-matrix(NA,nrow=nseq,ncol=taill)for(iin1:nseq){rval<-testseq[i]ans<-discretelogistic(r=rval,K=1000.0,N0=100,Ct=0.0,Yrs=yrs)ans[,"nt1"]<-round(ans[,"nt1"],4)result[i,]<-c(rval,length(unique(tail(ans[,"nt1"],taill))))result2[i,]<-tail(ans[,"nt1"],taill)}if(limy[1]==0)limy<-c(0,getmax(result2,mult=1.02))parset()# plot taill values against taill of each r value plot(rep(testseq[1],taill),result2[1,],type="p",pch=16,cex=0.1, ylim=limy,xlim=c(min(testseq)*(1-incx),max(testseq)*(1+incx)), xlab="r value",yaxs="i",xaxs="i",ylab="Equilibrium Numbers", panel.first=grid())for(iin2:nseq)points(rep(testseq[i],taill),result2[i,],pch=16,cex=0.1)return(invisible(list(result=result,result2=result2)))}# end of bifurcation
代码
#Alternative r value arrangements for you to try; Fig 3.3 #testseq <- seq(2.847,2.855,0.00001) #hash/unhash as needed #bifurcation(testseq,limy=c(600,740),incx=0.0001) # t #testseq <- seq(2.6225,2.6375,0.00001) # then explore #bifurcation(testseq,limy=c(660,730),incx=0.0001) testseq<-seq(1.9,2.975,0.0005)# modify to explore bifurcation(testseq,limy=0)
图 3.3: Schaefer 模型动态。一个经典的分岔图(May,1976),绘制了平衡动态与 r 值的函数关系,展示了从 2-、4-、8-循环以及混沌行为的转变。
#Effect of catches on stability properties of discretelogistic yrs=50; Kval=1000.0nocatch<-discretelogistic(r=2.56,K=Kval,N0=500,Ct=0,Yrs=yrs)catch50<-discretelogistic(r=2.56,K=Kval,N0=500,Ct=50,Yrs=yrs)catch200<-discretelogistic(r=2.56,K=Kval,N0=500,Ct=200,Yrs=yrs)catch300<-discretelogistic(r=2.56,K=Kval,N0=500,Ct=300,Yrs=yrs)
#Effect of different catches on n-cyclic behaviour Fig3.4 plottime<-function(x,ylab){yrs<-nrow(x)plot1(x[,"year"],x[,"nt"],ylab=ylab,defpar=FALSE)avB<-round(mean(x[(yrs-40):yrs,"nt"],na.rm=TRUE),3)mtext(avB,side=1,outer=F,line=-1.1,font=7,cex=1.0)}# end of plottime #the oma argument is used to adjust the space around the graph par(mfrow=c(2,2),mai=c(0.25,0.4,0.05,0.05),oma=c(1.0,0,0.25,0))par(cex=0.75, mgp=c(1.35,0.35,0), font.axis=7,font=7,font.lab=7)plottime(nocatch,"Catch = 0")plottime(catch50,"Catch = 50")plottime(catch200,"Catch = 200")plottime(catch300,"Catch = 300")mtext("years",side=1,outer=TRUE,line=-0.2,font=7,cex=1.0)
#Phase plot for Schaefer model Fig 3.5 plotphase<-function(x,label,ymax=0){#x from discretelogistic yrs<-nrow(x)colnames(x)<-tolower(colnames(x))if(ymax[1]==0)ymax<-getmax(x[,c(2:3)])plot(x[,"nt"],x[,"nt1"],type="p",pch=16,cex=1.0,ylim=c(0,ymax), yaxs="i",xlim=c(0,ymax),xaxs="i",ylab="nt1",xlab="", panel.first=grid(),col="darkgrey")begin<-trunc(yrs*0.6)#last 40% of yrs = 20, when yrs=50 points(x[begin:yrs,"nt"],x[begin:yrs,"nt1"],pch=18,col=1,cex=1.2)mtext(label,side=1,outer=F,line=-1.1,font=7,cex=1.2)}# end of plotphase par(mfrow=c(2,2),mai=c(0.25,0.25,0.05,0.05),oma=c(1.0,1.0,0,0))par(cex=0.75, mgp=c(1.35,0.35,0), font.axis=7,font=7,font.lab=7)plotphase(nocatch,"Catch = 0",ymax=1300)plotphase(catch50,"Catch = 50",ymax=1300)plotphase(catch200,"Catch = 200",ymax=1300)plotphase(catch300,"Catch = 300",ymax=1300)mtext("nt",side=1,outer=T,line=0.0,font=7,cex=1.0)mtext("nt+1",side=2,outer=T,line=0.0,font=7,cex=1.0)
#Exponential population declines under different Z. Fig 3.6 yrs<-50; yrs1<-yrs+1# to leave room for B[0] years<-seq(0,yrs,1)B0<-1000# now alternative total mortality rates Z<-c(0.05,0.1,0.2,0.4,0.55)nZ<-length(Z)Bt<-matrix(0,nrow=yrs1,ncol=nZ,dimnames=list(years,Z))Bt[1,]<-B0for(jin1:nZ)for(iin2:yrs1)Bt[i,j]<-Bt[(i-1),j]*exp(-Z[j])plot1(years,Bt[,1],xlab="Years",ylab="Population Size",lwd=2)if(nZ>1)for(jin2:nZ)lines(years,Bt[,j],lwd=2,col=j,lty=j)legend("topright",legend=paste0("Z = ",Z),col=1:nZ,lwd=3, bty="n",cex=1,lty=1:5)
图 3.6: 不同总死亡率水平下的指数型种群衰退。最上方的曲线是 Z = 0.05,最陡峭的曲线是 Z = 0.55。
#Prepare matrix of harvest rate vs time to appoximate F Z<--log(0.5)timediv<-c(2,4,12,52,365,730,2920,8760,525600)yrfrac<-1/timedivnames(yrfrac)<-c("6mth","3mth","1mth","1wk","1d","12h", "3h","1h","1m")nfrac<-length(yrfrac)columns<-c("yrfrac","divisor","yrfracH","Remain")result<-matrix(0,nrow=nfrac,ncol=length(columns), dimnames=list(names(yrfrac),columns))for(iin1:nfrac){timestepmort<-Z/timediv[i]N<-1000for(jin1:timediv[i])N<-N*(1-timestepmort)result[i,]<-c(yrfrac[i],timediv[i],timestepmort,N)}
# Simple Yield-per-Recruit see Russell (1942) age<-1:11; nage<-length(age); N0<-1000# some definitions # weight-at-age values WaA<-c(NA,0.082,0.175,0.283,0.4,0.523,0.7,0.85,0.925,0.99,1.0)# now the harvest rates H<-c(0.01,0.06,0.11,0.16,0.21,0.26,0.31,0.36,0.55,0.8)nH<-length(H)NaA<-matrix(0,nrow=nage,ncol=nH,dimnames=list(age,H))# storage CatchN<-NaA; CatchW<-NaA# define some storage matrices for(iin1:nH){# loop through the harvest rates NaA[1,i]<-N0# start each harvest rate with initial numbers for(agein2:nage){# loop through over-simplified dynamics NaA[age,i]<-NaA[(age-1),i]*(1-H[i])CatchN[age,i]<-NaA[(age-1),i]-NaA[age,i]}CatchW[,i]<-CatchN[,i]*WaA}# transpose the vector of total catches to totC<-t(colSums(CatchW,na.rm=TRUE))# simplify later printing
#Logistic S shaped cureve for maturity ages<-seq(0,50,1)sel1<-mature(-3.650425,0.146017,sizeage=ages)#-3.65/0.146=25 sel2<-mature(-6,0.2,ages)sel3<-mature(-6,0.24,ages)plot1(ages,sel1,xlab="Age Yrs",ylab="Selectivity",cex=0.75,lwd=2)lines(ages,sel2,col=2,lwd=2,lty=2)lines(ages,sel3,col=3,lwd=2,lty=3)abline(v=25,col="grey",lty=2)abline(h=c(0.25,0.5,0.75),col="grey",lty=2)legend("topleft",c("25_15.04","30_10.986","25_9.155"),col=c(1,2,3), lwd=3,cex=1.1,bty="n",lty=1:3)
图 3.9: 使用 mature() 函数的逻辑斯蒂 S 形曲线示例。图例包含每条曲线的 L50 和 IQ,其参数在代码中定义。
当目标是最大化产量时,这样的分析是有意义的。但是,恒定补充量的假设和对不确定性的忽视意味着这些分析通常不是保守的。人们尝试改进 YPR 分析提出的建议,其中比较有用的是 \(F_{0.1}\) 的出现(发音为 F zero point one)。定义为产量曲线起点处产量增长率 1/10 的渔获率 (Hilborn 和 Walters 1992)。\(F_{0.1}\) 的优势这是捕捞努力量的相对 大幅下降,只会导致产量的很小损失。这可以提高渔业的经济效益和可持续性。然而,本质上使用 \(F_{0.1}\) 仍然是一个经验规则,它在实践中比 \(F_{max}\) (最高产量点)更可持续,通常比 \(F_{msy}\) (平衡时可产生 MSY 的捕捞死亡率)更好。
代码
# A more complete YPR analysis age<-0:20; nage<-length(age)#storage vectors and matrices laa<-vB(c(50.0,0.25,-1.5),age)# length-at-age WaA<-(0.015*laa^3.0)/1000# weight-at-age as kg H<-seq(0.01,0.65,0.05); nH<-length(H)FF<-round(-log(1-H),5)# Fully selected fishing mortality N0<-1000M<-0.1numt<-matrix(0,nrow=nage,ncol=nH,dimnames=list(age,FF))catchN<-matrix(0,nrow=nage,ncol=nH,dimnames=list(age,FF))as50<-c(1,2,3)yield<-matrix(0,nrow=nH,ncol=length(as50),dimnames=list(H,as50))for(selin1:length(as50)){sa<-logist(as50[sel],1.0,age)# selectivity-at-age for(harvin1:nH){Ft<-sa*FF[harv]# Fishing mortality-at-age out<-bce(M,Ft,N0,age)numt[,harv]<-out[,"Nt"]catchN[,harv]<-out[,"Catch"]yield[harv,sel]<-sum(out[,"Catch"]*WaA,na.rm=TRUE)}# end of harv loop }# end of sel loop
代码
#A full YPR analysis Figure 3.10 plot1(H,yield[,3],xlab="Harvest Rate",ylab="Yield",cex=0.75,lwd=2)lines(H,yield[,2],lwd=2,col=2,lty=2)lines(H,yield[,1],lwd=2,col=3,lty=3)legend("bottomright",legend=as50,col=c(3,2,1),lwd=3,bty="n", cex=1.0,lty=c(3,2,1))
在本章中,我们只考虑了简单的种群模型,但我们也可以很好地使用 R 来探索物种之间在竞争、捕食、寄生、共生等过程中的相互作用。所有的早期模型(Lotka,1925;Wolterra,1927;Gause,1934)考虑了没有空间结构的种群。使用 R 会相对直接地增加这种模型的复杂性,并包括空间细节,如 Huffaker(1958) 的实验探索。Huffaker 后来的一些实验性捕食者-补捕食者安排持续了 490 天。在这种情况下,模拟研究对于扩展探索的可能性非常有用。这样有趣的工作将需要成为另一本书的一部分,因为在渔业领域,我们还有许多其他领域需要探索和讨论。
使用 R 作为进行此类模拟的工具,使得分析比在电子表格上布置参数操作更抽象。然而,代码块和已开发函数的可重用性,以及更复杂模型和函数的增量开发的潜力所带来的优势,超过了编程环境的更抽象的本质。使用 R 作为一种编程语言来开发这些不同的分析非常适合渔业和生态建模工作。
QUINN, TERRANCE J. 2003. 《RUMINATIONS ON THE DEVELOPMENT AND FUTURE OF POPULATION DYNAMICS MODELS IN FISHERIES》. Natural Resource Modeling 16 (4): 341–92. https://doi.org/10.1111/j.1939-7445.2003.tb00119.x.
Quinn, Terrance J., 和 Richard B. Deriso. 1999. Quantitative Fish Dynamics. Biological Resource Management. Oxford, New York: Oxford University Press.
Russell, E. S. 1942. The Overfishing Problem. CUP Archive.
Schaefer, M. 1991. 《Some Aspects of the Dynamics of Populations Important to the Management of the Commercial Marine Fisheries》. Bulletin of Mathematical Biology 53 (1-2): 253–79. https://doi.org/10.1016/s0092-8240(05)80049-7.