Demographic interpretation of reconstructed profiles

library(StablePopulation)

Purpose

This vignette explains how to interpret the demographic quantities produced by StablePopulation. The package reconstructs survivorship profiles at the species level and derives quantities that can later be connected to ecological or paleobiological questions such as age structure, mortality structure, and potential biomass flow.

The package does not itself calculate ecosystem biomass or predator resources. It provides the demographic layer needed before those later steps.

From fertility to constrained survivorship

The constrained reconstruction starts from a fertility schedule mx. For a given Weibull shape parameter beta, the corresponding scale parameter alpha is solved so that

\[ \sum_x l_x m_x = 1. \]

mx <- c(0, 0, 0.30, 0.75, 0.60, 0.20)

reconstruction <- reconstruct_population(
  fertility_rates = mx,
  beta = 1.20
)

knitr::kable(reconstruction$table, digits = 4)
age fertility_rates lx lxmx
0 0.00 1.0000 0.0000
1 0.00 0.8642 0.0000
2 0.30 0.7151 0.2145
3 0.75 0.5796 0.4347
4 0.60 0.4629 0.2777
5 0.20 0.3654 0.0731

The reconstructed lx is cumulative survivorship to each class. It starts at one and declines with age.

Stable structure R

The stable structure is the normalised age distribution implied by the survivorship profile:

\[ R_x = \frac{l_x}{\sum_x l_x}. \]

profile <- derive_demographic_profile(
  lx = reconstruction$lx,
  fertility_rates = mx
)

knitr::kable(
  profile$table[, c("age", "lx", "stable_structure")],
  digits = 4
)
age lx stable_structure
0 1.0000 0.2508
1 0.8642 0.2167
2 0.7151 0.1794
3 0.5796 0.1454
4 0.4629 0.1161
5 0.3654 0.0916

R sums to one and can be read as the expected proportion of individuals in each class under the reconstructed stationary structure.

sum(profile$stable_structure)
#> [1] 1

Biologically, classes with high R are classes expected to be abundant in the standing population, not necessarily classes producing the most deaths or the most births.

Mortality profile D

The raw mortality profile is derived from the stable structure:

\[ D_x = R_x - R_{x+1}, \]

with the final class closed by setting D_last = R_last.

knitr::kable(
  profile$table[, c("age", "stable_structure", "mortality_profile")],
  digits = 4
)
age stable_structure mortality_profile
0 0.2508 0.0341
1 0.2167 0.0374
2 0.1794 0.0340
3 0.1454 0.0293
4 0.1161 0.0245
5 0.0916 0.0916

D describes the exit-by-death profile from the stable structure. It is not simply 1 - lx, and it is not the same as the relative death distribution. Its sum equals the first value of R.

sum(profile$mortality_profile)
#> [1] 0.250809
profile$stable_structure[1]
#> [1] 0.250809

This distinction matters when moving from demographic reconstruction to resource-flow questions. The raw D keeps the scale implied by the stable structure.

Relative mortality profile D_relative

The relative mortality profile is a separately normalised version of D:

\[ D^{rel}_x = \frac{D_x}{\sum_x D_x}. \]

knitr::kable(
  profile$table[, c("age", "mortality_profile", "mortality_profile_relative")],
  digits = 4
)
age mortality_profile mortality_profile_relative
0 0.0341 0.1358
1 0.0374 0.1491
2 0.0340 0.1355
3 0.0293 0.1167
4 0.0245 0.0975
5 0.0916 0.3654

D_relative sums to one.

sum(profile$mortality_profile_relative)
#> [1] 1

It is useful when the question concerns the relative age distribution of deaths, independently of the scale of the stable age structure.

Conditional survival B

Conditional survival between consecutive classes is

\[ B_x = \frac{l_{x+1}}{l_x}. \]

knitr::kable(
  profile$table[, c("age", "lx", "conditional_survival")],
  digits = 4
)
age lx conditional_survival
0 1.0000 0.8642
1 0.8642 0.8275
2 0.7151 0.8105
3 0.5796 0.7986
4 0.4629 0.7894
5 0.3654 NA

B is returned as NA where the ratio is undefined. It should be interpreted as survival from one class to the next, not as cumulative survival from birth. Cumulative survival is lx.

Fertility contribution lxmx

When fertility is supplied, the table also contains lxmx, the contribution of each class to net replacement.

knitr::kable(
  profile$table[, c("age", "fertility_rates", "lx", "lxmx")],
  digits = 4
)
age fertility_rates lx lxmx
0 0.00 1.0000 0.0000
1 0.00 0.8642 0.0000
2 0.30 0.7151 0.2145
3 0.75 0.5796 0.4347
4 0.60 0.4629 0.2777
5 0.20 0.3654 0.0731

profile$R0
#> [1] 1

In the constrained reconstruction, the total of lxmx is numerically one. This is the stationary-population condition imposed by the reconstruction.

Observed survivorship and model selection

When observed survivorship is available, select_beta() chooses the constrained candidate closest to it.

lx_observed <- c(1.0000000, 0.8480619, 0.7023945, 0.5759026, 0.4689639, 0.3798816)

selection <- select_beta(
  fertility_rates = mx,
  lx_observed = lx_observed,
  beta_values = seq(0.50, 1.80, by = 0.05)
)

selection$best_beta
#> [1] 1.1
selection$best_RMSE
#> [1] 2.901736e-08
knitr::kable(selection$best_profile, digits = 4)
age fertility_rates lx_observed lx_reconstructed residual squared_error lxmx
0 0.00 1.0000 1.0000 0 0 0.0000
1 0.00 0.8481 0.8481 0 0 0.0000
2 0.30 0.7024 0.7024 0 0 0.2107
3 0.75 0.5759 0.5759 0 0 0.4319
4 0.60 0.4690 0.4690 0 0 0.2814
5 0.20 0.3799 0.3799 0 0 0.0760

The selected profile is constrained by R0 = 1. It is the best grid candidate among the supplied beta values, not a free two-parameter optimum.

Scenario interpretation without observed survivorship

When observed lx is unavailable, scan_beta() should be interpreted as a scenario generator.

scan <- scan_beta(
  fertility_rates = mx,
  beta_values = seq(0.50, 1.80, by = 0.10),
  terminal_window = c(0.10, 0.40)
)

knitr::kable(scan$admissible_summary, digits = 4)
beta alpha R0 residual lx_terminal stable terminal_admissible admissible status
6 1.0 5.3754 1 0 0.3945 TRUE TRUE TRUE converged
7 1.1 5.1505 1 0 0.3799 TRUE TRUE TRUE converged
8 1.2 4.9715 1 0 0.3654 TRUE TRUE TRUE converged
9 1.3 4.8259 1 0 0.3509 TRUE TRUE TRUE converged
10 1.4 4.7053 1 0 0.3366 TRUE TRUE TRUE converged
11 1.5 4.6040 1 0 0.3225 TRUE TRUE TRUE converged
12 1.6 4.5179 1 0 0.3085 TRUE TRUE TRUE converged
13 1.7 4.4439 1 0 0.2947 TRUE TRUE TRUE converged
14 1.8 4.3796 1 0 0.2810 TRUE TRUE TRUE converged

A terminal window defines an admissible set of scenarios. It does not provide a single fitted curve. Downstream ecological interpretations should therefore preserve the scenario logic rather than reporting one arbitrary beta as if it had been estimated from observed survival data.

Reading the output biologically

The main columns answer different biological questions.

Quantity Biological reading
lx Cumulative survivorship to each class.
R / stable_structure Expected standing age structure under the reconstructed stationary profile.
D / mortality_profile Raw age-specific exits by death from that structure.
D_relative Relative distribution of deaths across age classes.
B / conditional_survival Survival from one class to the next.
lxmx Class contribution to net replacement.

For paleobiological applications, the most important point is that the package separates the reconstruction of a survivorship profile from its demographic interpretation. The Weibull model supplies a smooth survivorship structure; the derived quantities translate that structure into age distribution, death profile, and fertility contribution.