中文 | Español | English | português | Turkish
source("single_populations_app_setup.R")
本文介绍单个物种的种群动态。为了方便起见,我们只考虑无个体迁入或迁出的封闭种群。该种群大小因个体出生而增长,因个体死亡而缩小。若种群增长不受抑制,则其增长呈指数型。无论种群大小变化,其出生率和死亡率均不变。已知人均出生率(per-capita birth rate)为$b$, 人均死亡率(per-capita death rate)为$d$, 则种群大小$N$的变化为:
$$\frac{dN}{dT} = bN - dN = (b-d)N$$
$(b-d)$ 是一个常数,可合并写为自然增长率(intrinsic growth rate)$r$。则指数型增长可写为:
$$\frac{dN}{dT} = rN$$
对于一个指数型增长的种群,其种群大小随时间变化的轨迹呈J型曲线。而J的形状由$r$的数值决定。👇请使用文末的app探索$r$值如何改变种群的轨迹。
上文的指数增长模型(exponential growth model)预测,该类种群的长期规模会趋于无穷大。但现实中,没有种群的数量能够无限制增长,因此指数增长模型可以被改进。
指数增长模型的前提条件是人均出生率$b$和人均死亡率$d$均不变。我们可以放宽这个条件,并假设当种群的增长趋于环境承载力(carrying capacity $K$)时,其总增长率(net growth rate $rD$)呈线性递减。此类种群的动态变化可用逻辑斯谛增长(logistic growth)模型描述:
$$\frac{dN}{dt} = rN \left(1-\frac{N}{K}\right)$$
呈逻辑斯谛增长的种群的会持续增长,直到种群规模与环境承载力相等($N = K$)。当种群规模超出环境承载力时,种群出现负增长,直到其规模减少至环境承载力。
在某些情况下,一个种群的增长率会取决于该种群在过去某个时间点的规模。比如,生物的繁殖产量会与其未成年时资源竞争的强度相关。对于这种情况,某时间点$t$时的种群增长率可以用过去$t-\tau$时的种群大小表示:
$$ \frac{dN}{dt} = rN_{t} \left(1-\frac{N_{t-\tau}}{K}\right)$$ 在app中可选择密度制约,并请勾选“Lagged density dependence”(滞后的密度依赖)选项,来模拟滞后逻辑斯谛增长。
pars_vars <- c("$r_i$", "$K_i$", "$N_i$", "$\\tau$") descriptions <- c("物种$i$的自然增长率", "物种$i$的环境承载力(carrying capacity)", "物种$i$的种群大小", "滞后逻辑斯谛增长的滞后时间") param_df <- data.frame(pars_vars, descriptions) kable(x = param_df, format = "html", col.names = c("参数/变量", "描述")) %>% kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "condensed"), position = "center")
sidebarLayout( sidebarPanel( # User defined density dependent or independent growth ---- radioButtons("densdep", "Type of population growth", choices = c(`Density Independent` = "indep", `Density Dependent` = "dep")), # User defined intrinsic growth rate r ----- sliderInput("r_val", label = "Intrinsic growth rate (r):", min = -0.25, max = 1, value = .25), # If Density Dependent growth requested, ask for K ----- conditionalPanel(condition = "input.densdep == 'dep'", sliderInput("K_val", label = "Carrying capacity (K)", min = 1, max = 10000, value = 500, step = 10)), # If Density Dependent growth requested, ask if time lag requested ----- conditionalPanel(condition = "input.densdep == 'dep'", radioButtons("densdepLag", label = "Delayed density dependence?", choices = c(`No lag` = "nolag",`Lagged density dependence` = "lag"))), # If timelag requested in density-dependent growth, ask for tau ----- conditionalPanel(condition = "input.densdepLag == 'lag'", sliderInput("tau", label = "Time lag (tau)", min = 0, max = 4, value = 1, step = .1)), # User defined initial population size ----- sliderInput("N1_val", label = "Initial population size:", min = 1, max = 1000, value = 50, step = 10), hr(), # User can add a second species ---- checkboxInput("secondsp", "Add a second species?", value = F), # If second species requested, add the following... ---- conditionalPanel(condition = "input.secondsp == true", # User defined density dependent or independent growth for sp2 ---- radioButtons("densdep2", "Type of population growth for Species 2", choices = c(`Density Independent` = "indep", `Density Dependent` = "dep")), # User defined intrinsic growth rate r2 ----- sliderInput("r_val2", label = "Intrinsic growth rate for species 2 (r2):", min = -0.25, max = 1, value = .25), # If Density Dependent growth requested for sp2, ask for K2 ----- conditionalPanel(condition = "input.densdep2 == 'dep'", sliderInput("K_val2", label = "Carrying capacity for species 2 (K2)", min = 1, max = 10000, value = 500, step = 10), ), # If Density Dependent growth requested, ask if time lag requested ----- conditionalPanel(condition = "input.densdep2 == 'dep'", radioButtons("densdepLag2", label = "Delayed density dependence in Species 2?", choices = c(`No lag` = "nolag",`Lagged density dependence` = "lag"))), # If timelag requested in density-dependent growth, ask for tau ----- conditionalPanel(condition = "input.densdepLag2 == 'lag'", sliderInput("tau2", label = "Time lag sp 2 (tau2)", min = 0, max = 4, value = 1, step = .1)), sliderInput("N2_val", label = "Initial population size of species 2:", min = 1, max = 1000, value = 50, step = 10), hr()), # User can select which plots to show ---- checkboxGroupInput("plots_to_show", "Select which plots to show", c("N vs. Time" = "Nvtime", "ln(N) vs. Time" = "lnNvtime", "dN/dt vs. N" = "n1overn", "dN/Ndt vs. N" = "n1novern"), selected = c("Nvtime", "lnNvtime")), hr(), # User defined length of simulation ----- numericInput("max_time", label = "Length of simulation:", value = 100), # User defined initial population size ----- ), # Panel of plots ----- mainPanel( renderPlot({plots_to_print()}, width = 600, height = 600) ) ) # Get user defined parameters for sp 1 ------ params <- reactive({ if(input$densdep == "indep") { c(r = input$r_val) } else if(input$densdepLag == 'lag') { c(r = input$r_val, K = input$K_val, tau = input$tau) } else { c(r = input$r_val, K = input$K_val) } }) init <- reactive({c(N1 = input$N1_val)}) time <- reactive({seq(from = 0, to = input$max_time, by=1)}) params2 <- reactive({ if(input$densdep2 == "indep") { c(r = input$r_val2) } else if(input$densdepLag2 == 'lag') { c(r = input$r_val2, K = input$K_val2, tau = input$tau2) } else { c(r = input$r_val2, K = input$K_val2) } }) init2 <- reactive({c(N1 = input$N2_val)}) # Generate trajectories for sp 1 -------- pop_growth <- reactive({ if(input$densdep == 'indep') { data.frame(ecoevoapps::run_exponential_model(time= time(), init = init(), params = params())) } else { data.frame(ecoevoapps::run_logistic_model(time= time(), init = init(), params = params())) } }) # Generate trajectories for sp 2 -------- pop_growth2 <- reactive({ if(input$secondsp) { if(input$densdep2 == 'indep') { data.frame(ecoevoapps::run_exponential_model(time= time(), init = init2(), params = params2())) } else { data.frame(ecoevoapps::run_logistic_model(time= time(), init = init2(), params = params2())) } } }) pops_combined <- reactive({ if(input$secondsp) { dplyr::bind_rows(pop_growth(), pop_growth2(), .id = "which_pop") } else { pg <- pop_growth() pg$which_pop = "1" pg } }) # Make plots ----------- # 0. Make caption ------ plot_caption1 <- reactive({ if(input$densdep == 'indep') { paste0("Species 1 parameter values: r1 = ", input$r_val) } else if (input$densdepLag == "nolag") { paste0("Species 1 parameter values: r1 = ", input$r_val, ", K1 = ", input$K_val) } else { paste0("Species 1 parameter values: r1 = ", input$r_val, ", K1 = ", input$K_val, ", Tau1 = ", input$tau) } }) plot_caption2 <- reactive({ if(input$secondsp) { if(input$densdep2 == 'indep') { paste0("Species 2 parameter values: r2 = ", input$r_val2) } else if (input$densdepLag2 == "nolag") { paste0("Species 2 parameter values: r2 = ", input$r_val2, ", K2 = ", input$K_val2) } else { paste0("Species 2 parameter values: r2 = ", input$r_val2, ", K2 = ", input$K_val2, ", Tau2 = ", input$tau2) } } }) plot_caption <- reactive ({ if(input$secondsp) { paste0(plot_caption1(), "\n", plot_caption2()) } else { plot_caption1() } }) # 1. Trajectory of N vs. time ----------- trajectory <- reactive({ if("Nvtime" %in% input$plots_to_show) { ggplot(pops_combined()) + geom_line(aes(x = time, y = N1, color = which_pop), linewidth = 2) + ecoevoapps::theme_apps() + scale_x_continuous(expand = c(0, 0, .1, 0)) + scale_y_continuous(expand = c(0, 0, .1, 0)) + annotate("text", x = 0, y = 0, label = "") + scale_color_brewer("Species", palette = "Set1") + ylab("Population size") } }) # 2. Trjectory of ln(N) vs. time -------- trajectory_log <- reactive({ if("lnNvtime" %in% input$plots_to_show) { ggplot(pops_combined()) + geom_line(aes(x = time, y = log(N1), color = which_pop), linewidth = 2) + scale_color_brewer("Species", palette = "Set1") + ecoevoapps::theme_apps() + scale_x_continuous(expand = c(0, 0, .1, 0)) + scale_y_continuous(expand = c(0, 0, .1, 0)) + annotate("text", x = 0, y = 0, label = "") + ylab("ln(Population size)") } }) # 3. dN/dT vs. N ----------- n1_over_n <- reactive({ if("n1overn" %in% input$plots_to_show) { dndt_out <- data.frame(dndt = dndt(params()), which_pop = "1") dndt_out$x <- 1:nrow(dndt_out) dndt_out_for_plot <- if(input$secondsp) { dn2dt_out <- data.frame(dndt = dndt(params2()), which_pop = "2") dn2dt_out$x <- 1:nrow(dn2dt_out) dplyr::bind_rows(dndt_out, dn2dt_out) } else { dndt_out } ggplot(dndt_out_for_plot) + geom_line(aes(x= x, y = dndt, color = which_pop), linewidth = 2) + xlab("Population Size") + ylab("Population growth rate (dN/dt)") + scale_color_brewer("Species", palette = "Set1") + scale_x_continuous(expand = c(0, 0, .1, 0)) + scale_y_continuous(expand = c(0, 0, .1, 0)) + theme_apps() } }) # 4. dN/Ndt vs. N ----------- n1n_over_n <- reactive({ if("n1novern" %in% input$plots_to_show) { dnNdt_out <- data.frame(dnNdt = dnNdt(params()), which_pop = "1") dnNdt_out$x <- 1:nrow(dnNdt_out) dnNdt_out_for_plot <- if(input$secondsp) { dnN2dt_out <- data.frame(dnNdt = dnNdt(params2()), which_pop = "2") dnN2dt_out$x <- 1:nrow(dnN2dt_out) dplyr::bind_rows(dnNdt_out, dnN2dt_out) } else { dnNdt_out } ggplot(dnNdt_out_for_plot) + geom_line(aes(x = x, y = dnNdt, color = which_pop), linewidth = 2) + xlab("Population Size") + ylab("per-capita population growth rate\n(dN/Ndt)") + scale_color_brewer("Species", palette = "Set1") + scale_x_continuous(expand = c(0, 0, .1, 0)) + # limits = c(0, min(max(pop_growth()$N1)), max(pop_growth2()$N1) scale_y_continuous(limits = c(-0.26, 1), expand = c(0, 0, .1, 0)) + theme_apps() } }) # Make a list of plots and print out plots based on which ones were requested ---- plot_list <- reactive({ list(trajectory(), trajectory_log(), n1_over_n(), n1n_over_n()) %>% discard(is.null) }) plots_to_print <- reactive({ wrap_plots(plot_list(), ncol = 2) + labs(caption = plot_caption()) + plot_layout(guides = "collect") & theme(legend.position = 'bottom') })
"How populations grow: the Exponential and the logistic", John Vandermeer, Nature Education Knowledge.
"Exponential & logistic growth" at Khan Academy.
suppressWarnings(ecoevoapps::print_app_footer(language = "ch"))
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.