This document was built in Markdown in R 4.0.4 and compiled on 25 February 2021. It covers package lefko3
version 3.3.2.
In this vignette, we use the cypdata
dataset to illustrate the estimation of function-based MPMs. Please see the other vignettes included in package lefko3
, as well as further vignettes posted online on the projects page of the Shefferson lab website, for further demonstrations of raw MPMs, function-based MPMs, IPMs, and age-by-stage MPMs.
The white lady’s slipper, Cypripedium candidum, is a North American perennial herb in the family Orchidaceae. It is long-lived and of conservation concern. The population from which the dataset originates is located within a state nature preserve located in northeastern Illinois, USA. The population was monitored annually from 2004 to 2009, with two monitoring sessions per year. Monitoring sessions took roughly 2 weeks each, and included complete censuses of the population divided into sections referred to as patches. Each monitoring session consisted of searches for previously recorded individuals, which were located according to coordinates relative to fixed stakes at the site, followed by a search for new individuals. Data recorded per individual included: the location, the number of non-flowering sprouts, the number of flowering sprouts, the number of flowers per flowering sprout, and the number of fruit pods per sprout (only in the second monitoring session per year, once fruiting had occurred). Location was used to infer individual identity. More information about this population and its characteristics is given in Shefferson et al. (2001) and Shefferson et al. (2017).
Our goal in this exercise will be to produce ahistorical and historical function-based matrices, and to compare these to see if we can spot the influence of individual history. We will also illustrate the use of multiple demographic data formats via comparison with the cypvert
dataset, which presents the same information as cypdata
but in ahistorical vertical format. Where appropriate, we will provide commentary to illustrate where analyses may be shortened or altered for more typical analyses.
Population matrix projection modeling requires an appropriate life history model showing how all stages and transitions are related. The figure below shows a very general life history model detailing these relationships in Cypripedium candidum. The first stage of life is a dormant seed stage, although an individual may germinate in the year following seed production. The first germinated stage is a protocorm, which is an underground, mycoheterotrophic stage unique to the families Orchidaceae and Pyrolaceae. There are three years of protocorm stages, followed by a seedling stage, and finally a set of stages that comprise the size-classified adult portion of life. The figure shows 49 such stages, each for a different number of stems (including 0 for vegetative dormancy) and one of two reproductive statuses. These stages may be reorganized for different circumstances (more on this later).
Figure 5.1. Life history model of Cypripedium candidum.
We can see a variety of transitions within this figure. The juvenile stages have fairly simple transitions. New recruits may enter the population directly from germination of a seed produced the previous year, in which case they start in the protocorm 1 stage, or they may begin as dormant seed. Dormant seed may remain dormant, die, or germinate into the protocorm 1 stage. Protocorms exist for up to 3 years, yielding the protocorm 1, 2, and 3 stages, without any possibility of staying within each of these stages for more than a single year. Protocorm 3 leads to a seedling stage, in which the plant may persist for many years before becoming mature. Here, maturity does not really refer to reproduction per se, but rather to a morphology indistinguishable from a reproductive plant except for the lack of a flower. The first mature stage is usually either vegetative dormancy (dorm), during which time the plant does not sprout, or a small, non-flowering adult (1V). Once in this portion of the life history, the plant may transition among 49 mature stages, including vegetative dormancy, 1-24 shoots without flowers, or 1-24 shoots with at least one flower (theoretically, individual plants may be bigger than 24 sprouts, but we have never observed a plant bigger than this in the population).
The horizontal dataset cypdata
, and the ahistorical vertical dataset cypvert
which is the same as cypdata
but is structured differently, both include only data for the adult stages, and so later we will need to set juvenile transitions to constants. Let’s begin by clearing the memory, loading the package, and loading the horizontal dataset.
rm(list=ls(all=TRUE))
library(lefko3)
data(cypdata)
We will first need to describe the life history characterizing the dataset, matching it to our analyses properly with a stageframe
for our Cypripedium candidum dataset. Since this analysis will be function-based, we will include all possible size classes here. If constructing raw matrices, all sizes that occur in the dataset need to be accounted for in a way that is both natural and parsimonious with respect to the numbers of individuals moving through actual transitions. If constructing function-based matrices, such as IPMs, then representative sizes at systematic increments will be satisfactory. Since size is count-based in the Cypripedium candidum case, we will use all numbers of stems that might occur from 0 to the maximum in the dataset, representing the life history diagram shown in the beginning of this chapter.
<- c(0, 0, 0, 0, 0, seq(from = 0, t = 24), seq(from = 1, to = 24))
sizevector <- c("SD", "P1", "P2", "P3", "SL", "D", "V1", "V2", "V3", "V4", "V5",
stagevector "V6", "V7", "V8", "V9", "V10", "V11", "V12", "V13", "V14", "V15", "V16", "V17",
"V18", "V19", "V20", "V21", "V22", "V23", "V24", "F1", "F2", "F3", "F4", "F5",
"F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16", "F17",
"F18", "F19", "F20", "F21", "F22", "F23", "F24")
<- c(0, 0, 0, 0, 0, rep(0, 25), rep(1, 24))
repvector <- c(0, 0, 0, 0, 0, 0, rep(1, 48))
obsvector <- c(0, 0, 0, 0, 0, rep(1, 49))
matvector <- c(0, 1, 1, 1, 1, rep(0, 49))
immvector <- c(1, rep(0, 53))
propvector <- c(0, 0, 0, 0, 0, rep(1, 49))
indataset
<- sf_create(sizes = sizevector, stagenames = stagevector,
cypframe repstatus = repvector, obsstatus = obsvector, matstatus = matvector,
propstatus = propvector, immstatus = immvector, indataset = indataset)
A close look at the resulting object, cypframe
, shows a data frame that includes in order for each stage: the stage’s name, the associated size, its reproductive status, its status as an observable stage, its status as a propagule stage, its status as an immature stage, its status as a mature stage, whether it occurs in the dataset, the half-width of its size class bin, the minimum and maximum ages associated with the stage, the minimum and maximum of its size class bin, the centroid of its size class bin, its full size class bin width, and some text about the stage in a comments field. Stage names and combinations of characteristics must be unique to prevent estimation errors, and the comments field may be edited to include any information deemed pertinent. For example, we may edit the comments field as below to help us remember what each stage represents.
$comments[(cypframe$stage == "SD")] <- "Dormant seed"
cypframe$comments[(cypframe$stage == "P1")] <- "1st yr protocorm"
cypframe$comments[(cypframe$stage == "P2")] <- "2nd yr protocorm"
cypframe$comments[(cypframe$stage == "P3")] <- "3rd yr protocorm"
cypframe$comments[(cypframe$stage == "SL")] <- "Seedling"
cypframe$comments[(cypframe$stage == "D")] <- "Dormant mature"
cypframe$comments[(cypframe$stage == "V1")] <- "Non-reproductive mature with 1 stem"
cypframe$comments[(cypframe$stage == "V2")] <- "Non-reproductive mature with 2 stems"
cypframe$comments[(cypframe$stage == "V3")] <- "Non-reproductive mature with 3 stems"
cypframe$comments[(cypframe$stage == "V4")] <- "Non-reproductive mature with 4 stems"
cypframe$comments[(cypframe$stage == "V5")] <- "Non-reproductive mature with 5 stems"
cypframe$comments[(cypframe$stage == "V6")] <- "Non-reproductive mature with 6 stems"
cypframe$comments[(cypframe$stage == "V7")] <- "Non-reproductive mature with 7 stems"
cypframe$comments[(cypframe$stage == "V8")] <- "Non-reproductive mature with 8 stems"
cypframe$comments[(cypframe$stage == "V9")] <- "Non-reproductive mature with 9 stems"
cypframe$comments[(cypframe$stage == "V10")] <- "Non-reproductive mature with 10 stems"
cypframe$comments[(cypframe$stage == "V11")] <- "Non-reproductive mature with 11 stems"
cypframe$comments[(cypframe$stage == "V12")] <- "Non-reproductive mature with 12 stems"
cypframe$comments[(cypframe$stage == "V13")] <- "Non-reproductive mature with 13 stems"
cypframe$comments[(cypframe$stage == "V14")] <- "Non-reproductive mature with 14 stems"
cypframe$comments[(cypframe$stage == "V15")] <- "Non-reproductive mature with 15 stems"
cypframe$comments[(cypframe$stage == "V16")] <- "Non-reproductive mature with 16 stems"
cypframe$comments[(cypframe$stage == "V17")] <- "Non-reproductive mature with 17 stems"
cypframe$comments[(cypframe$stage == "V18")] <- "Non-reproductive mature with 18 stems"
cypframe$comments[(cypframe$stage == "V19")] <- "Non-reproductive mature with 19 stems"
cypframe$comments[(cypframe$stage == "V20")] <- "Non-reproductive mature with 20 stems"
cypframe$comments[(cypframe$stage == "V21")] <- "Non-reproductive mature with 21 stems"
cypframe$comments[(cypframe$stage == "V22")] <- "Non-reproductive mature with 22 stems"
cypframe$comments[(cypframe$stage == "V23")] <- "Non-reproductive mature with 23 stems"
cypframe$comments[(cypframe$stage == "V24")] <- "Non-reproductive mature with 24 stems"
cypframe$comments[(cypframe$stage == "F1")] <- "Flowering mature with 1 stem"
cypframe$comments[(cypframe$stage == "F2")] <- "Flowering mature with 2 stems"
cypframe$comments[(cypframe$stage == "F3")] <- "Flowering mature with 3 stems"
cypframe$comments[(cypframe$stage == "F4")] <- "Flowering mature with 4 stems"
cypframe$comments[(cypframe$stage == "F5")] <- "Flowering mature with 5 stems"
cypframe$comments[(cypframe$stage == "F6")] <- "Flowering mature with 6 stems"
cypframe$comments[(cypframe$stage == "F7")] <- "Flowering mature with 7 stems"
cypframe$comments[(cypframe$stage == "F8")] <- "Flowering mature with 8 stems"
cypframe$comments[(cypframe$stage == "F9")] <- "Flowering mature with 9 stems"
cypframe$comments[(cypframe$stage == "F10")] <- "Flowering mature with 10 stems"
cypframe$comments[(cypframe$stage == "F11")] <- "Flowering mature with 11 stems"
cypframe$comments[(cypframe$stage == "F12")] <- "Flowering mature with 12 stems"
cypframe$comments[(cypframe$stage == "F13")] <- "Flowering mature with 13 stems"
cypframe$comments[(cypframe$stage == "F14")] <- "Flowering mature with 14 stems"
cypframe$comments[(cypframe$stage == "F15")] <- "Flowering mature with 15 stems"
cypframe$comments[(cypframe$stage == "F16")] <- "Flowering mature with 16 stems"
cypframe$comments[(cypframe$stage == "F17")] <- "Flowering mature with 17 stems"
cypframe$comments[(cypframe$stage == "F18")] <- "Flowering mature with 18 stems"
cypframe$comments[(cypframe$stage == "F19")] <- "Flowering mature with 19 stems"
cypframe$comments[(cypframe$stage == "F20")] <- "Flowering mature with 20 stems"
cypframe$comments[(cypframe$stage == "F21")] <- "Flowering mature with 21 stems"
cypframe$comments[(cypframe$stage == "F22")] <- "Flowering mature with 22 stems"
cypframe$comments[(cypframe$stage == "F23")] <- "Flowering mature with 23 stems"
cypframe$comments[(cypframe$stage == "F24")] <- "Flowering mature with 24 stems" cypframe
This object is quite large, so we do not show what it looks like here. Type cypframe
at the prompt to see the full object.
Now we will transform our vertical dataset into a historically-formatted vertical file. The resulting dataset will have each individual’s observed life history broken up into states corresponding to three consecutive years per row, with plant identity marked in each row. To handle this, we use the verticalize3()
function, as below.
<- verticalize3(data = cypdata, noyears = 6, firstyear = 2004,
vertdata patchidcol = "patch", individcol = "plantid", blocksize = 4,
sizeacol = "Inf2.04", sizebcol = "Inf.04", sizeccol = "Veg.04",
repstracol = "Inf.04", repstrbcol = "Inf2.04", fecacol = "Pod.04",
stageassign = cypframe, stagesize = "sizeadded", NAas0 = TRUE)
In the above code, we described the input dataset in a way that allows R to reorganize it appropriately. For the reorganization to proceed properly, the input dataset needs to be arranged in blocks of columns for each year, with variables in the same order every year. The output dataset includes a number of summary variables, but the data is essentially broken down into groups of three consecutive monitoring occasions each (time t+1, t, and t-1, corresponding to year3
, year2
, and year1
in the output, respectively), with individuals spread across multiple rows. The output dataset is further limited to those entries in which the individual is alive in time t (year2
), meaning that all rows in which an individual is dead or not yet recruited in time t are dropped. Thus, we have 320 rows of data and 54 variables.
This reorganized dataset includes a set of interesting terms, the sizeadded
group of three variables. These are sums of the size variables for each time, such that size1added
is calculated as sizea1 + sizeb1 + sizec1
. This may or may not make sense depending on the dataset. In this particular dataset, the full size of the individual in each time is this sum, because size is determined as the number of stems per plant, and these columns give the numbers of 3 different kinds of stems: Veg
gives the number of non-reproductive stems, Inf
gives the number of single-flowered inflorescences, and Inf2
gives the number of double-flowered inflorescences per plant per time-step (an inflorescence takes a single stem, and no inflorescence has more than two flowers). Since size is given by the total number of stems in this example, we will use the sizeadded
group of variables to code individual size in our analyses.
Size and fecundity are both count variables here, and we need to determine the appropriate distribution to use in both cases. Let’s take a look at a histogram of size in time t.
hist(vertdata$size2added, main = "Size in time t", xlab = "Number of stems in time t")
Figure 5.2. Histogram of Cypripedium candidum size in time t
We can see here that the count has a very low mean, and the curve declines fairly smoothly. The Poisson or negative binomial distribution might work for size. Before determining whether this is indeed the case, let’s also see if similar patterns hold for fecundity.
hist(subset(vertdata, repstatus2 == 1)$feca2, main = "Fecundity in time t",
xlab = "Number of fruit pods in time t")
Figure 5.3. Histogram of Cypripedium candidum fecundity in time t
Fecundity appears to have many 0s, suggesting the possibility that we will need to use a zero-inflated distribution.
Let’s conduct a formal test to determine the appropriate distributions for size and fecundity.
sf_distrib(vertdata, size = "size2added", fec = "feca2", repst = "repstatus2")
#> The mean size is 3.322
#>
#> The variance in size is 12.02
#>
#> The probability of this dispersion level by chance assuming the true mean size = variance in size is 0
#>
#> Size is significantly overdispersed.
#>
#> Mean lambda is 0.03609
#> The actual number of 0s in size is 15
#> The expected number of 0s in size under the null hypothesis is 11.55
#> The probability of this deviation in 0s from expectation by chance is 0.2687
#>
#> Size is not significantly zero-inflated.
#>
#>
#> --------------------------------------------------
#>
#>
#> Mean fecundity is 0.7881
#> The variance in fecundity is 1.536
#> The probability of this dispersion level by chance assuming the true mean fecundity = variance in fecundity is 0
#>
#> Fecundity is significantly overdispersed.
#>
#>
#> Mean lambda is 0.4547
#> The actual number of 0s in fecundity is 68
#> The expected number of 0s in fecundity under the null hypothesis is 53.65
#> The probability of this deviation in 0s is 5.904e-06
#>
#> Fecundity is significantly zero-inflated.
#> NULL
The results of these tests show us that size is overdispersed but does not have excess 0s, while fecundity is overdispersed with excess 0s. Thus, we should assume the negative binomial distribution for size, and the zero-inflated negative binomial distribution for fecundity.
We may also wish to see how to proceed if our original dataset is already in vertical, but ahistorical, format. This package also includes dataset cypvert
, which is the same dataset as cypdata
but set in ahistorical vertical format. The structure of the dataset can be seen below. Note that individual histories are split across multiple rows.
Figure 5.4. Organization of the ahistorical vertical version of the Cypripedium dataset, as viewed in Microsoft Excel.
Here, we use the historicalize3()
function to deal with this dataset. First, let’s load the ahistorical vertical raw data file.
data(cypvert)
And let’s also look at its dimensions, relative to the original horizontal dataset.
dim(cypdata)
#> [1] 77 27
dim(cypvert)
#> [1] 331 12
This dataset is longer and narrower, with more rows and fewer columns. This is because we now split data for each individual across multiple columns. After three columns of identifying information (plantid
, patch
, and censor
), a single column designates time in year t, given as year2
. This dataset then includes columns showing individual state in pairs of consecutive years corresponding to times t and t+1. State in time t-1 is not presented because this is an ahistorical dataset. Fortunately, this dataset includes the plantid
variable, which is an individual identity term and must be supplied for conversion. The historicalize3()
function uses individual identity to reorganize datasets into historical vertical format. After the next block runs, type summary(vertdata2)
to get a summary of this dataset.
<- historicalize3(data = cypvert, patchidcol = "patch",
vertdata2 individcol = "plantid", year2col = "year2", sizea2col = "Inf2.2",
sizea3col = "Inf2.3", sizeb2col = "Inf.2", sizeb3col = "Inf.3",
sizec2col = "Veg.2", sizec3col = "Veg.3", repstra2col = "Inf2.2",
repstra3col = "Inf2.3", repstrb2col = "Inf.2", repstrb3col = "Inf.3",
feca2col = "Pod.2", feca3col = "Pod.3", repstrrel = 2, stageassign = cypframe,
stagesize = "sizeadded", censorcol = "censor", censorkeep = 1, censor = FALSE,
NAas0 = TRUE, reduce = TRUE)
We can compare the dimensions of the new vertical datasets.
dim(vertdata)
#> [1] 320 54
dim(vertdata2)
#> [1] 320 54
The lengths of the datasets are the same in terms of rows and columns, and the variables and data are the same although the order of the columns and rows might not match (see the summaries for comparison).
One important consideration is the use of censor variables. Censoring a demographic dataset refers to the elimination of suspect data points from analysis. It is typically accomplished by including a binary variable in the dataset denoting whether an individual datum is to be kept or excluded. The objects vertdata
and vertdata2
were both created without using censoring variables.However, because the datasets actually included censor variables (all data were set to be included, with no suspect data), we wished to incorporate those variables in the final datasets. Hence, although censor = FALSE
in both the call to verticalize3()
and the call to verticalize3()
, we also noted censorcol = "censor"
and censorkeep = 1
in the call to historicalize3()
. Failing to add these options to the call to historicalize3()
will produce the same dataset but with some zeroes entering variables censor1
, censor2
, and censor3
.
The next steps involve adding some external data to help parameterize the matrices properly. There are two approaches for this:
supplemental()
function, oroverwrite()
function.
We advise the use of the first approach, the supplemental()
function, which is more holistic and flexible than the other approach. However, we describe both methods below.
The supplemental()
function provides a means of inputting three kinds of data into MPM construction:
Here, we create two supplement tables taking all of these sorts of data. The first is the supplement table for the historical analysis, and the second table covers the ahistorical analysis. Each row refers to a specific transition, and in the historical case, there are codes for 19 given transitions (12 for the ahistorical case). The first 9 of these transitions are set to specific probabilities, and the next 8 are transitions that will be set to other, estimated transitions (these are the non-NA transitions in eststage
set below). The final 2 terms of fecundity multipliers. Based on the literature, the proxies for entry into the adult classes are transitions from dormancy, as below. However, in the raw dataset, dormancy is not common enough to use as an effective proxy in raw matrix creation. Hence, we can use different proxies for function-based matrix estimation than for raw matrix estimation. Where necessary, we also use rep
and mat
as shorthand to code for all reproductive stages and all mature stages, respectively.
<- supplemental(stage3 = c("SD", "SD", "P1", "P1", "P2", "P3", "SL", "SL",
cypsupp3 "SL", "D", "V1", "V2", "V3", "D", "V1", "V2", "V3", "SD", "P1"),
stage2 = c("SD", "SD", "SD", "SD", "P1", "P2", "P3", "SL", "SL", "SL", "SL",
"SL", "SL", "SL", "SL", "SL", "SL", "rep", "rep"),
stage1 = c("SD", "rep", "SD", "rep", "SD", "P1", "P2", "P3", "SL", "P3", "P3",
"P3", "P3", "SL", "SL", "SL", "SL", "mat", "mat"),
eststage3 = c(NA, NA, NA, NA, NA, NA, NA, NA, NA, "D", "V1", "V2", "V3", "D",
"V1", "V2", "V3", NA, NA),
eststage2 = c(NA, NA, NA, NA, NA, NA, NA, NA, NA, "D", "D", "D", "D", "D",
"D", "D", "D", NA, NA),
eststage1 = c(NA, NA, NA, NA, NA, NA, NA, NA, NA, "D", "D", "D", "D", "D",
"D", "D", "D", NA, NA),
givenrate = c(0.1, 0.1, 0.2, 0.2, 0.2, 0.2, 0.25, 0.4, 0.4, NA, NA, NA, NA,
NA, NA, NA, NA, 0.5, 0.5),
type = c("S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S",
"S", "S", "S", "R", "R"),
stageframe = cypframe)
<- supplemental(stage3 = c("SD", "P1", "P2", "P3", "SL", "SL", "D", "V1",
cypsupp2 "V2", "V3", "SD", "P1"),
stage2 = c("SD", "SD", "P1", "P2", "P3", "SL", "SL", "SL", "SL", "SL", "rep",
"rep"),
eststage3 = c(NA, NA, NA, NA, NA, NA, "D", "V1", "V2", "V3", NA, NA),
eststage2 = c(NA, NA, NA, NA, NA, NA, "D", "D", "D", "D", NA, NA),
givenrate = c(0.1, 0.2, 0.2, 0.2, 0.25, 0.4, NA, NA, NA, NA, 0.5, 0.5),
type = c("S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "R", "R"),
stageframe = cypframe, historical = FALSE)
The supplement tables that we just created are the best means of adding external data to our MPMs, mostly because they allow both specific transitions to be isolated, and because they allow the use of shorthand to identify large groups of transitions (e.g. using mat
, rep
, immat
, prop
, or all
to signify all mature stages, reproductive stages, immature stages, propagule stages, or simply all stages, respectively). However, an alternative means is through the creation of a reproduction matrix and an overwrite
data frame. The reproduction matrix is an ahistorical matrix that tells where where fecundity rates need to be set, and at what level. Cypripedium candidum produces seeds that germinate by the following growing season (stage P1, or a first year protocorm), or that remain dormant for the next year (stage SD). In the following matrix, we detail that the fecundity of each reproductive stage needs to be split into two between each of these output stages. The actual split places 50% of the fecundity of a reproductive stage into each category of recruit, where the full fecundity is estimated by linear models that we will create. This reproduction matrix can be used in historical MPM construction, and it is assumed that stage at time t-1 is set to all
.
<- matrix(0, 54, 54)
rep.assumptions 1:2,31:54] <- 0.5 rep.assumptions[
Next we will create an overwrite data frame, which outlines transitions that cannot be estimated from the data set and need to be set by other means. Here is an example of two overwrite tables for the Cypripedium candidum analysis. Note that they are fundamentally similar to supplement tables, but simply neither allow the addition of fecundity multipliers nor check entered stages against the stage frame.
<- overwrite(stage3 = c("SD", "SD", "P1", "P1", "P2", "P3", "SL", "SL",
cypover3 "SL", "D", "V1", "V2", "V3", "D", "V1", "V2", "V3"),
stage2 = c("SD", "SD", "SD", "SD", "P1", "P2", "P3", "SL", "SL", "SL", "SL",
"SL", "SL", "SL", "SL", "SL", "SL"),
stage1 = c("SD", "rep", "SD", "rep", "SD", "P1", "P2", "P3", "SL", "P3", "P3",
"P3", "P3", "SL", "SL", "SL", "SL"),
eststage3 = c(NA, NA, NA, NA, NA, NA, NA, NA, NA, "D", "V1", "V2", "V3", "D",
"V1", "V2", "V3"),
eststage2 = c(NA, NA, NA, NA, NA, NA, NA, NA, NA, "D", "D", "D", "D", "D",
"D", "D", "D"),
eststage1 = c(NA, NA, NA, NA, NA, NA, NA, NA, NA, "D", "D", "D", "D", "D",
"D", "D", "D"),
givenrate = c(0.1, 0.1, 0.2, 0.2, 0.2, 0.2, 0.25, 0.4, 0.4, NA, NA, NA, NA,
NA, NA, NA, NA),
type = c("S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S",
"S", "S", "S"))
<- overwrite(stage3 = c("SD", "P1", "P2", "P3", "SL", "SL", "D", "V1",
cypover2 "V2", "V3"),
stage2 = c("SD", "SD", "P1", "P2", "P3", "SL", "SL", "SL", "SL", "SL"),
eststage3 = c(NA, NA, NA, NA, NA, NA, "D", "V1", "V2", "V3"),
eststage2 = c(NA, NA, NA, NA, NA, NA, "D", "D", "D", "D"),
givenrate = c(0.1, 0.2, 0.2, 0.2, 0.25, 0.4, NA, NA, NA, NA),
type = c("S", "S", "S", "S", "S", "S", "S", "S", "S", "S"))
We can now proceed with matrix estimation.
Matrix creation can proceed either as raw matrix creation, as initially outlined in Ehrlén (2000), or via the creation of function-based matrices, in many ways equivalent to complex integral projection models per Ellner and Rees (2006) and as further described in the non-Gaussian case in Shefferson et al. (2014). In the raw MPM case, no vital rate models are estimated. In the function-based MPM case, vital rates are first analyzed via linear or non-linear models of the raw demographic dataset, and functions are created that estimate these vital rates according the inputs given. Matrices are then estimated using these functions, as opposed to the raw data.
Prior to vital rate estimation, a number of key decisions need to be made regarding the assumptions underlying the vital rates, and their relationships with the factors under investigation. These decisions include the general modeling strategy, and the size and fecundity distributions.
Most function-based matrices, whether integral projection models or otherwise, use either a generalized linear modeling (GLM) strategy, or a generalized linear mixed modeling (GLMM) strategy. The former is more common because of its simplicity, but the latter is theoretically more correct because it allows the user to correct for the lack of independence inherent in datasets incorporating repeat sampling from the same individuals. The difference between the two with regards to vital rate modeling is strongly related to assumptions regarding the individual and the nature of spatiotemporal variation in vital rates.
In both cases, the underlying dataset utilized is a vertical dataset. Here, each row of data gives the state of the individual in either two consecutive times (the ahistorical case), or three consecutive times (the historical case). Under a GLM framework, time is a fixed categorical variable, and individual identity is ignored. Using time as a fixed categorical variable implies that the monitoring occasions worked with are the only times for which inference is wanted. Thus, it would not be correct to infer future population dynamics after 2009 for a dataset collected between 2004 and 2009, if year is treated as fixed. Ignoring individual identity treats all transitions as independent, even though data originating from the same sampled individual is clearly not independent. This may be interpreted as a form of pseudoreplication because strongly related data is used to create matrices that are assumed to be statistically independent. In the case of linear modeling of vital rates, some of the data being used to estimate specific vital rates would originate from the same individual and so be related, even though all data points are assumed to be independent in the construction of linear models. This might impact demographic modeling by inflating Type 1 error in the linear modeling, yielding more significant terms in the chosen best-fit model.
Under a GLMM (generalized linear mixed model) framework, both time and individual identity can be treated as random categorical terms. This has two major implications. First, both time and individuals can be assumed to be random samples from a broader population of times and individuals for which inference is sought. Thus, sampled years represent a greater universe of years for which inference can be made, and so their associated coefficients can be assumed to come from a normal distribution with \(mean = 0\). Second, treating individual as a random categorical term eliminates the pseudoreplication that is inherent in the GLM approach to vital rate estimation when individuals are monitored potentially many times, because now each individual is assumed randomly drawn and associated with its own response distribution. Additionally, patch may be considered random, in which case it is assumed to have been sampled from all possible spaces that the species might occupy. We encourage researchers to use the GLMM approach in their demographic work, but we have also included easy-to-use GLM functionality, since many will find the GLM approach particularly useful in cases where mixed modeling breaks down.
Once a general approach is decided upon, the next step is to decide the underlying distributions. The probabilities of survival, observation, and reproductive status are automatically set to the binomial distribution, and this cannot be altered. However, the probability of size transition and fecundity rate can be set to the Gaussian, Poisson, or negative binomial distributions, with zero-inflated versions of the Poisson and negative binomial also available. In general, if size or fecundity rate is a continuous variable (i.e., not an integer or count variable), then it should be set to the Gaussian distribution. In contrast, if size or fecundity rate is a count, then it should be set to the Poisson distribution. The negative binomial distribution is also provided in cases where the Poisson distribution’s assumptions, such as the mean equaling the variance, are clearly broken. We do not encourage the use of the negative binomial except in such cases, as the extra parameters estimated for the negative binomial distribution reduce the power of the modeling exercises conducted.
The Poisson and the negative binomial distributions both predict specific numbers of 0s in the response variable. If excess 0s occur within the dataset even after including the observation status and reproductive status as vital rates to absorb 0s, then a zero-inflated Poisson or negative binomial distribution may be used. These modeling approaches work by parameterizing a binomial model, typically with a logit link, to predict 0 responses. The Poisson or negative binomial is then used to predict non-zero responses. This conditional model ends up really acting as two separate models in which 0s are assumed to be predicted under potentially different processes than the remaining counts. Users should be aware that, because an extra model is built to cover 0s, zero-inflated models are much more complex and can include many more parameters than their non-inflated counterparts. The principle of parsimony suggests that they should only be used when there are significantly more 0s than expected.
Package lefko3
includes a function that can help in determining which distributions to use: sf_distrib()
. Earlier in this vignette, we used it to determine that we should use the negative binomial for size, and the zero-inflated negative binomial for fecundity.
In lefko3, the modelsearch
function is the workhorse that conducts vital rate model estimation. Here, we will create a full suite of vital rate models for the Cypripedium candidum dataset. Before proceeding, we need to decide on the linear model building strategy, the correct vital rates to model, the proper statistical distributions for estimated vital rates, the proper parameterizations for each vital rate, and the strategy for determination of the best-fit models.
First, we must determine the model building strategy. In most cases, the best procedure will be through mixed linear models in which monitoring occasion and individual identity are random terms. We will set monitoring occasion as random because we wish to make inferences for the population as a whole and do not wish to restrict ourselves to inference only for the years monitored (i.e. our distribution of years sampled is itself a sample of the population in time). We will set individual identity as random because many or most of the individuals that we have sampled to produce our dataset yield multiple observation data points across time. Thus, we will use approach = "mixed"
in the parameterization for modelsearch
. To make sure that time and individual identity are treated as random, we just need to set the proper variable names for indiv
and year
, which correspond to individual identity (individ
by default), and to time t (year2
by default). The year.as.random
option is set to random by default, and leaving it this way also means that R will randomly draw coefficient values for years with inestimable coefficients. Setting year.as.random
to FALSE would make time a fixed variable.
The mixed modeling approach is usually preferable to the GLM approach, particularly because it allows us to handle data points originating from the same individual as related. However, a mixed modeling strategy results in lower statistical power and a greater time used in estimating models. Users of package lefko3
wishing to use a standard generalized linear modeling strategy can set approach = "glm"
. In this case, individual identity is not used, time is a fixed factor, and all observed transitions are treated as independent.
Next, we must determine which vital rates to model. Function modelsearch()
estimates up to 9 vital rate models:
survival probability from time t to time t+1,
observation probability in time t+1 assuming survival until that time,
size in time t+1 assuming survival and observation in that time,
reproduction status in time t+1 assuming survival and observation until that time,
fecundity rate assuming survival in time t and observation and reproduction in time t+1 (mature only),
juvenile survival probability from time t to time t+1,
juvenile observation probability in time t+1 assuming survival until that time,
juveile size in time t+1 assuming survival and observation in that time, and
reproduction status in time t+1 assuming survival and observation until that time of a juvenile in time t that is becoming mature in time t+1.
The default settings for modelsearch
estimate 1) survival probability, 3) size distribution, and 5) fecundity, which are the minimum 3 vital rates required for a full projection matrix. Observation probability (option obs
in vitalrates
) should only be included when a life history stage or size exists that cannot be observed. For example, in the case of a plant with vegetative dormancy, the observation probability can be thought of as the sprouting probability, which is a biologically meaningful vital rate (Shefferson et al. 2001). Further, reproduction status (option repst
in vitalrates
) should only be modeled if size classification needs to be stratified by the ability to reproduce, as when 0 fecundity occurs within stages that also produce offspring. In this latter case, we can imagine that reproductive and non-reproductive individuals of each size class might theoretically exist, and we may wish to parameterize transitions allowing individuals to be reproductive or non-reproductive. Since Cypripedium candidum is capable of long bouts of vegetative dormancy, since we wish to stratify the population into reproductive and non-reproductive adults of the same size classes, and since we have no data derived from juvenile individuals, we will set vitalrates = c("surv", "obs", "size", "repst", "fec")
.
Third, we need to set the proper statistical distribution for each parameter. Survival probability, observation probability, and reproductive status are all modeled as binomial variables, and this cannot be changed. In the case of this population of Cypripedium candidum, size was measured as the number of stems and so is a count variable. Likewise, fecundity is actually estimated as the number of fruits produced per plant, and so is also a count variable. We have already performed tests for overdispersion and zero-inflation, and so will set size to the negative binomial distribution and fecundity to the zero-inflated negative binomial distribution.
Fourth, we need the proper model parameterizations for each vital rate, using the suite
option. The default, suite = "main"
, under the mixed model setting (approach = "mixed"
) starts with the estimation of global models that include size in times t and t-1, and reproductive status in times t and t-1, as fixed factors, with individual identity and time t set as random terms in a mixed model framework. Other terms can be included when specified, such as individual covariates and age. Setting suite = "full"
will yield global models that also include all two-way interactions. We will set it to the latter. The default under the GLM setting (approach = "glm"
) makes time t a fixed term and drops individual identity from consideration. The global model under suite = "full"
then includes all fixed factors noted before, plus time t and all two-way interactions with it. If the population is not stratified by reproductive status, then suite = "size"
will eliminate reproductive status terms and use all others in the global model. If size is not important, then suite = "rep"
will eliminate size but keep reproductive status and all other terms. Finally, suite = "cons"
will result in a global model in which neither reproductive status nor size are considered.
Fifth, and finally, we need to determine the proper strategy for the determination of the best-fit model. Model building proceeds through the dredge
function in package MuMIn
(Barton 2014), and each model has an associated AICc value. The default setting in lefko3
(bestfit = "AICc&k"
) will compare all models within 2.0 AICc units of the model with \(\Delta AICc = 0\), and choose the one with the lowest degrees of freedom. This approach is generally better than the alternative, which simply uses the model with \(\Delta AICc = 0\) (bestfit = "AICc"
), as all models within 2.0 AICc units of that model are equally parsimonious and so fewer degrees of freedom result from fewer parameters estimated (Burnham and Anderson 2002).
In the model building exercise below, we will use the suite = "full"
option to run all main effects and their two-way interactions.
<- modelsearch(vertdata, historical = TRUE, approach = "mixed",
cypmodels3 vitalrates = c("surv", "obs", "size", "repst", "fec"), sizedist = "negbin",
fecdist = "negbin", fec.zero = TRUE, suite = "full",
size = c("size3added", "size2added", "size1added"), quiet = TRUE)
#> boundary (singular) fit: see ?isSingular
#> Warning in checkConv(attr(opt, "derivs"), opt$par, ctrl = control$checkConv, : Model failed to converge with max|grad| = 0.00275551 (tol =
#> 0.002, component 1)
#> Warning in Matrix::sparseMatrix(dims = c(0, 0), i = integer(0), j = integer(0), : 'giveCsparse' has been deprecated; setting 'repr = "T"'
#> for you
#> Warning in Matrix::sparseMatrix(dims = c(0, 0), i = integer(0), j = integer(0), : 'giveCsparse' has been deprecated; setting 'repr = "T"'
#> for you
#> Warning in Matrix::sparseMatrix(dims = c(0, 0), i = integer(0), j = integer(0), : 'giveCsparse' has been deprecated; setting 'repr = "T"'
#> for you
#> Warning in checkConv(attr(opt, "derivs"), opt$par, ctrl = control$checkConv, : Model failed to converge with max|grad| = 0.0099624 (tol =
#> 0.002, component 1)
#> Warning in Matrix::sparseMatrix(dims = c(0, 0), i = integer(0), j = integer(0), : 'giveCsparse' has been deprecated; setting 'repr = "T"'
#> for you
#> Warning in Matrix::sparseMatrix(dims = c(0, 0), i = integer(0), j = integer(0), : 'giveCsparse' has been deprecated; setting 'repr = "T"'
#> for you
#> Warning in Matrix::sparseMatrix(dims = c(0, 0), i = integer(0), j = integer(0), : 'giveCsparse' has been deprecated; setting 'repr = "T"'
#> for you
#> Warning in fitTMB(TMBStruc): Model convergence problem; false convergence (8). See vignette('troubleshooting')
As modelsearch
works, it produces a good amount of text to allow the user to understand what is going on. It is entirely possible, and actually quite likely, that many warning messages will appear, and these may be of use to users in understanding their data and how well it conforms to their analytical assumptions. We have silenced most of this with the quiet = TRUE
option, but we encourage users to allow the function to run unsilenced, in case of modeling problems. Please read the documentation for functions lm
, glm
, glm.nb
, lmer
(lme4 package), glmer
(lme4 package), zeroinfl
(pscl package), glmmTMB
(glmmTMB package), and dredge
(MuMIn package) for further information on the sources of problems in such models. Note, however, that some warnings are expected, and so are not cause for alarm.
Once done, we can summarize the output with the summary()
function.
summary(cypmodels3)
#> This LefkoMod object includes 5 linear models.
#> Best-fit model criterion used: AICc&k
#>
#> ────────────────────────────────────────────────────────────────────────────────
#> Survival model:
#> Generalized linear mixed model fit by maximum likelihood (Laplace Approximation) ['glmerMod']
#> Family: binomial ( logit )
#> Formula: alive3 ~ size2added + (1 | year2) + (1 | individ)
#> Data: surv.data
#> AIC BIC logLik deviance df.resid
#> 128.1324 143.2057 -60.0662 120.1324 316
#> Random effects:
#> Groups Name Std.Dev.
#> individ (Intercept) 1.198361
#> year2 (Intercept) 0.008826
#> Number of obs: 320, groups: individ, 74; year2, 5
#> Fixed Effects:
#> (Intercept) size2added
#> 2.0352 0.6344
#> optimizer (Nelder_Mead) convergence code: 0 (OK) ; 0 optimizer warnings; 1 lme4 warnings
#>
#> ────────────────────────────────────────
#>
#> Observation model:
#> Generalized linear mixed model fit by maximum likelihood (Laplace Approximation) ['glmerMod']
#> Family: binomial ( logit )
#> Formula: obsstatus3 ~ size2added + (1 | year2) + (1 | individ)
#> Data: obs.data
#> AIC BIC logLik deviance df.resid
#> 118.2567 133.1117 -55.1284 110.2567 299
#> Random effects:
#> Groups Name Std.Dev.
#> individ (Intercept) 1.078e-05
#> year2 (Intercept) 8.776e-01
#> Number of obs: 303, groups: individ, 70; year2, 5
#> Fixed Effects:
#> (Intercept) size2added
#> 2.4904 0.3134
#> optimizer (Nelder_Mead) convergence code: 0 (OK) ; 0 optimizer warnings; 1 lme4 warnings
#>
#> ────────────────────────────────────────
#>
#> Size model:
#> Formula: size3added ~ repstatus1 + repstatus2 + size1added + size2added +
#> (1 | year2) + (1 | individ) + repstatus1:repstatus2 + size1added:size2added
#> Data: size.data
#> AIC BIC logLik df.resid
#> 1083.5943 1120.2239 -531.7971 278
#> Random-effects (co)variances:
#>
#> Conditional model:
#> Groups Name Std.Dev.
#> year2 (Intercept) 0.19588
#> individ (Intercept) 0.05703
#>
#> Number of obs: 288 / Conditional model: year2, 5; individ, 70
#>
#> Overdispersion parameter for nbinom2 family (): 651
#>
#> Fixed Effects:
#>
#> Conditional model:
#> (Intercept) repstatus1 repstatus2 size1added size2added repstatus1:repstatus2
#> 0.212264 0.279521 0.255923 0.131124 0.153940 -0.369427
#> size1added:size2added
#> -0.008694
#>
#> ────────────────────────────────────────
#>
#> Reproductive status model:
#> Generalized linear mixed model fit by maximum likelihood (Laplace Approximation) ['glmerMod']
#> Family: binomial ( logit )
#> Formula: repstatus3 ~ repstatus2 + size2added + (1 | year2) + (1 | individ)
#> Data: repst.data
#> AIC BIC logLik deviance df.resid
#> 333.6176 351.9324 -161.8088 323.6176 283
#> Random effects:
#> Groups Name Std.Dev.
#> individ (Intercept) 0.1829
#> year2 (Intercept) 0.6250
#> Number of obs: 288, groups: individ, 70; year2, 5
#> Fixed Effects:
#> (Intercept) repstatus2 size2added
#> -1.4630 1.6457 0.1715
#>
#> ────────────────────────────────────────
#>
#> Fecundity model:
#> Formula: feca2 ~ size2added + (1 | year2) + (1 | individ)
#> Zero inflation: ~size2added + (1 | year2) + (1 | individ)
#> Data: fec.data
#> AIC BIC logLik df.resid
#> 250.8609 275.7971 -116.4305 109
#> Random-effects (co)variances:
#>
#> Conditional model:
#> Groups Name Std.Dev.
#> year2 (Intercept) 0.5761
#> individ (Intercept) 0.1640
#>
#> Zero-inflation model:
#> Groups Name Std.Dev.
#> year2 (Intercept) 1.278e-06
#> individ (Intercept) 2.123e-04
#>
#> Number of obs: 118 / Conditional model: year2, 5; individ, 51 / Zero-inflation model: year2, 5; individ, 51
#>
#> Overdispersion parameter for nbinom2 family (): 1.11e+07
#>
#> Fixed Effects:
#>
#> Conditional model:
#> (Intercept) size2added
#> -0.54014 0.06174
#>
#> Zero-inflation model:
#> (Intercept) size2added
#> 3.865 -1.574
#>
#> ────────────────────────────────────────────────────────────────────────────────
#> Juvenile survival model:
#> [1] 1
#>
#> ────────────────────────────────────────
#>
#> Juvenile observation model:
#> [1] 1
#>
#> ────────────────────────────────────────
#>
#> Juvenile size model:
#> [1] 1
#>
#> ────────────────────────────────────────
#>
#> Juvenile reproduction model:
#> [1] 0
#>
#>
#> ────────────────────────────────────────────────────────────────────────────────
#>
#> Number of models in survival table:72
#>
#> Number of models in observation table:72
#>
#> Number of models in size table:72
#>
#> Number of models in reproduction status table:72
#>
#> Number of models in fecundity table:151
#>
#> Number of models in juvenile survival table: 1
#>
#> Number of models in juvenile observation table: 1
#>
#> Number of models in juvenile size table: 1
#>
#> Number of models in juvenile reproduction table: 1
#>
#>
#> ────────────────────────────────────────────────────────────────────────────────
#>
#> General model parameter names (column 1), and specific names used in these models (column 2):
#> parameter_names mainparams
#> 1 time t year2
#> 2 individual individ
#> 3 patch patch
#> 4 alive in time t+1 surv3
#> 5 observed in time t+1 obs3
#> 6 size in time t+1 size3
#> 7 reproductive status in time t+1 repst3
#> 8 fecundity in time t+1 fec3
#> 9 fecundity in time t fec2
#> 10 size in time t size2
#> 11 size in time t-1 size1
#> 12 reproductive status in time t repst2
#> 13 reprodutive status in time t-1 repst1
#> 14 age in time t age
#> 15 individual covariate a in time t indcova2
#> 16 individual covariate a in time t-1 indcova1
#> 17 individual covariate b in time t indcovb2
#> 18 individual covariate b in time t-1 indcovb1
#> 19 individual covariate c in time t indcovc2
#> 20 individual covariate c in time t-1 indcovc1
#>
#>
#> ────────────────────────────────────────────────────────────────────────────────
#>
#> Quality control:
#>
#> Survival estimated with 74 individuals and 320 individual transitions.
#> Observation estimated with 70 individuals and 303 individual transitions.
#> Size estimated with 70 individuals and 288 individual transitions.
#> Reproductive status estimated with 70 individuals and 288 individual transitions.
#> Fecundity estimated with 51 individuals and 118 individual transitions.
#> Juvenile survival not estimated.
#> Juvenile observation probability not estimated.
#> Juvenile size transition not estimated.
#> Juvenile reproduction probability not estimated.
#> NULL
Here we see historical terms determining size, suggesting that we cannot ignore history. The summary
function gives us a great deal of information about all of the models, but also hides a number of more technical details. For example, the complete model selection tables are actually included in the modelsearch
output, and users can see these by calling them directly. For example:
$survival_table
cypmodels3#> Global model call: lme4::glmer(formula = alive3 ~ size2added + repstatus2 + size1added +
#> repstatus1 + size2added:repstatus2 + size2added:size1added +
#> repstatus2:repstatus1 + size2added:repstatus1 + repstatus2:size1added +
#> (1 | year2) + (1 | individ), data = surv.data, family = "binomial")
#> ---
#> Model selection table
#> (Int) rp1 rp2 sz1 sz2 rp1:rp2 rp1:sz2 rp2:sz1 rp2:sz2 sz1:sz2 df logLik used.criterion delta weight
#> 9 2.035 0.6344 4 -60.066 128.3 0.00 0.165
#> 11 2.006 0.54530 0.5899 5 -59.773 129.7 1.48 0.079
#> 13 1.538 0.1862 0.5185 5 -59.781 129.8 1.49 0.078
#> 10 1.984 0.31570 0.6240 5 -59.967 130.1 1.87 0.065
#> 28 2.063 -0.42760 -0.13110 0.5712 46.86 7 -58.039 130.4 2.18 0.056
#> 42 2.149 -0.77080 0.5004 0.8144 6 -59.407 131.1 2.82 0.040
#> 15 1.505 0.41530 0.1828 0.4824 6 -59.578 131.4 3.17 0.034
#> 12 1.972 0.25460 0.51660 0.5866 6 -59.712 131.7 3.43 0.030
#> 269 1.453 0.2393 0.5678 -0.0242500 6 -59.745 131.8 3.50 0.029
#> 139 1.995 0.78840 0.6308 -1.287e-01 6 -59.748 131.8 3.50 0.029
#> 14 1.535 0.04577 0.1820 0.5183 6 -59.779 131.8 3.57 0.028
#> 60 2.161 -0.97690 -0.09133 0.5073 66.09 0.4323 8 -57.864 132.2 3.93 0.023
#> 32 1.642 -0.62310 -0.13560 0.1672 0.4722 32.63 8 -57.910 132.3 4.02 0.022
#> 156 1.999 -0.42880 0.16980 0.6194 78.38 -1.665e-01 8 -57.991 132.4 4.18 0.020
#> 96 1.446 -0.80970 0.49640 0.3565 0.4784 29.27 -0.5133 9 -57.022 132.6 4.37 0.019
#> 44 2.126 -0.74500 0.43350 0.4772 0.7549 7 -59.228 132.8 4.56 0.017
#> 79 1.381 0.81620 0.2759 0.4952 -0.2702 7 -59.333 133.0 4.76 0.015
#> 46 1.696 -0.78060 0.1570 0.4225 0.6639 7 -59.333 133.0 4.77 0.015
#> 271 1.425 0.41160 0.2332 0.5288 -0.0227300 7 -59.546 133.5 5.19 0.012
#> 143 1.467 0.58300 0.1844 0.5106 -9.563e-02 7 -59.561 133.5 5.22 0.012
#> 16 1.507 -0.05451 0.42620 0.1876 0.4815 7 -59.575 133.5 5.25 0.012
#> 140 1.950 0.27090 0.78510 0.6327 -1.432e-01 7 -59.680 133.7 5.46 0.011
#> 270 1.451 0.04333 0.2350 0.5673 -0.0240900 7 -59.743 133.8 5.59 0.010
#> 64 1.988 -1.04200 -0.09154 0.1302 0.4754 67.43 0.3721 9 -57.649 133.9 5.62 0.010
#> 288 1.524 -0.63140 -0.14680 0.2443 0.5409 28.76 -0.0345200 9 -57.852 134.3 6.02 0.008
#> 188 2.123 -0.93650 0.04907 0.5338 48.18 0.4045 -7.679e-02 9 -57.855 134.3 6.03 0.008
#> 160 1.596 -0.62030 0.08667 0.1643 0.5111 40.43 -1.275e-01 9 -57.877 134.3 6.08 0.008
#> 48 1.951 -0.83530 0.42180 0.1351 0.4415 0.7060 8 -59.007 134.5 6.22 0.007
#> 5 2.203 0.4232 4 -63.249 134.6 6.36 0.007
#> 128 1.483 -0.98140 0.49340 0.3452 0.4569 33.53 0.1606 -0.4970 10 -56.997 134.7 6.45 0.007
#> 352 1.444 -0.80970 0.49510 0.3575 0.4797 30.36 -0.5125 -0.0005682 10 -57.022 134.8 6.50 0.006
#> 224 1.445 -0.80970 0.49780 0.3565 0.4788 34.47 -0.5131 -9.739e-04 10 -57.022 134.8 6.50 0.006
#> 172 2.060 -0.73950 0.75970 0.5324 0.7524 -1.818e-01 8 -59.169 134.8 6.54 0.006
#> 302 1.546 -0.84910 0.2643 0.5043 0.6923 -0.0454200 8 -59.216 134.9 6.64 0.006
#> 7 2.034 0.80290 0.4000 5 -62.393 135.0 6.72 0.006
#> 335 1.362 0.80260 0.2870 0.5080 -0.2618 -0.0063460 8 -59.330 135.1 6.86 0.005
#> 80 1.382 -0.02074 0.82000 0.2775 0.4949 -0.2698 8 -59.332 135.1 6.87 0.005
#> 207 1.376 0.83470 0.2757 0.4990 -0.2679 -1.284e-02 8 -59.332 135.1 6.87 0.005
#> 399 1.401 0.55540 0.2294 0.5477 -8.203e-02 -0.0200800 8 -59.533 135.5 7.27 0.004
#> 272 1.427 -0.05600 0.42280 0.2385 0.5282 -0.0228700 8 -59.543 135.5 7.29 0.004
#> 144 1.470 -0.04976 0.59090 0.1888 0.5094 -9.440e-02 8 -59.558 135.6 7.32 0.004
#> 88 1.913 -0.93410 0.95620 0.6509 20.14 -0.6027 8 -59.560 135.6 7.32 0.004
#> 24 2.154 -0.69120 0.25110 0.3991 18.80 7 -60.686 135.7 7.47 0.004
#> 320 1.834 -1.11300 -0.09706 0.2406 0.5457 23.35 0.4029 -0.0405500 10 -57.568 135.8 7.59 0.004
#> 192 1.945 -0.97650 0.08750 0.1360 0.5024 25.30 0.3255 -8.932e-02 10 -57.637 136.0 7.73 0.003
#> 416 1.684 -0.61010 0.16500 0.2256 0.6463 227.90 -1.686e-01 -0.0345100 10 -57.679 136.1 7.81 0.003
#> 112 1.531 -0.87890 0.83730 0.2757 0.3991 0.7075 -0.3417 9 -58.813 136.2 7.95 0.003
#> 3 3.318 1.13000 4 -64.113 136.4 8.09 0.003
#> 20 3.291 -0.29670 0.35580 29.65 6 -62.076 136.4 8.16 0.003
#> 304 1.768 -0.97290 0.37300 0.2656 0.5016 0.7453 -0.0418000 9 -58.935 136.5 8.19 0.003
#> 71 1.890 1.23900 0.5294 -0.2978 6 -62.098 136.5 8.20 0.003
#> 176 1.887 -0.82500 0.73360 0.1317 0.4991 0.7012 -1.755e-01 9 -58.951 136.5 8.22 0.003
#> 6 2.193 0.12520 0.4129 5 -63.233 136.7 8.40 0.002
#> 256 1.499 -0.99750 0.44370 0.3452 0.4446 33.30 0.1730 -0.5019 3.323e-02 11 -56.995 136.8 8.59 0.002
#> 384 1.475 -0.98370 0.48650 0.3499 0.4626 29.11 0.1630 -0.4920 -0.0028670 11 -56.996 136.8 8.59 0.002
#> 480 1.444 -0.80970 0.49500 0.3575 0.4796 28.55 -0.5125 8.388e-05 -0.0005727 11 -57.022 136.9 8.64 0.002
#> 8 2.039 -0.11950 0.82790 0.4090 6 -62.380 137.0 8.77 0.002
#> 1 3.724 3 -65.517 137.1 8.85 0.002
#> 336 1.363 -0.02181 0.80640 0.2888 0.5078 -0.2612 -0.0064190 9 -59.330 137.2 8.98 0.002
#> 463 1.361 0.80780 0.2867 0.5087 -0.2613 -3.446e-03 -0.0061910 9 -59.330 137.2 8.98 0.002
#> 208 1.378 -0.02024 0.83770 0.2772 0.4985 -0.2676 -1.241e-02 9 -59.332 137.2 8.98 0.002
#> 400 1.403 -0.05133 0.56330 0.2343 0.5467 -8.058e-02 -0.0202400 9 -59.531 137.6 9.38 0.002
#> 448 1.792 -1.06400 0.04248 0.2272 0.5779 13.39 0.3693 -8.065e-02 -0.0393600 11 -57.560 138.0 9.72 0.001
#> 4 3.227 0.42560 1.06400 5 -63.937 138.1 9.80 0.001
#> 368 1.461 -0.90630 0.78600 0.3220 0.4437 0.7117 -0.3090 -0.0240700 10 -58.776 138.3 10.00 0.001
#> 240 1.500 -0.88080 0.96170 0.2756 0.4238 0.7059 -0.3320 -8.134e-02 10 -58.799 138.3 10.05 0.001
#> [ reached getOption("max.print") -- omitted 6 rows ]
#> Models ranked by used.criterion(x)
#> Random terms (all models):
#> '1 | year2', '1 | individ'
Looking over the model table shows that our best-fit model was the model with the lowest \(\Delta AICc = 0\). This is not always the case, because sometimes models with \(\Delta AICc \le 2.0\) have fewer parameters, in which case those models are considered more parsimonious. If such a model existed here, then that model would have been chosen as the best-fit model. This reflects current best practice in model selection, where the most parsimonious model is chosen.
Before moving on, we note that the models created above are actually only usable for the construction of historical models. For comparison, we may wish to estimate ahistorical models. In that case, we also need linear models in which the global models tested do not include state at time t-1. Here, we produce these models.
<- modelsearch(vertdata, historical = FALSE, approach = "mixed",
cypmodels2 vitalrates = c("surv", "obs", "size", "repst", "fec"), sizedist = "negbin",
fecdist = "negbin", fec.zero = TRUE, suite = "full",
size = c("size3added", "size2added"), quiet = TRUE)
#> Warning in checkConv(attr(opt, "derivs"), opt$par, ctrl = control$checkConv, : Model failed to converge with max|grad| = 0.0330464 (tol =
#> 0.002, component 1)
#> Warning in Matrix::sparseMatrix(dims = c(0, 0), i = integer(0), j = integer(0), : 'giveCsparse' has been deprecated; setting 'repr = "T"'
#> for you
#> Warning in Matrix::sparseMatrix(dims = c(0, 0), i = integer(0), j = integer(0), : 'giveCsparse' has been deprecated; setting 'repr = "T"'
#> for you
#> Warning in Matrix::sparseMatrix(dims = c(0, 0), i = integer(0), j = integer(0), : 'giveCsparse' has been deprecated; setting 'repr = "T"'
#> for you
#> Warning in fitTMB(TMBStruc): Model convergence problem; false convergence (8). See vignette('troubleshooting')
#> Warning in Matrix::sparseMatrix(dims = c(0, 0), i = integer(0), j = integer(0), : 'giveCsparse' has been deprecated; setting 'repr = "T"'
#> for you
#> Warning in Matrix::sparseMatrix(dims = c(0, 0), i = integer(0), j = integer(0), : 'giveCsparse' has been deprecated; setting 'repr = "T"'
#> for you
#> Warning in Matrix::sparseMatrix(dims = c(0, 0), i = integer(0), j = integer(0), : 'giveCsparse' has been deprecated; setting 'repr = "T"'
#> for you
#> Warning in fitTMB(TMBStruc): Model convergence problem; false convergence (8). See vignette('troubleshooting')
summary(cypmodels2)
#> This LefkoMod object includes 5 linear models.
#> Best-fit model criterion used: AICc&k
#>
#> ────────────────────────────────────────────────────────────────────────────────
#> Survival model:
#> Generalized linear mixed model fit by maximum likelihood (Laplace Approximation) ['glmerMod']
#> Family: binomial ( logit )
#> Formula: alive3 ~ size2added + (1 | year2) + (1 | individ)
#> Data: surv.data
#> AIC BIC logLik deviance df.resid
#> 128.1324 143.2057 -60.0662 120.1324 316
#> Random effects:
#> Groups Name Std.Dev.
#> individ (Intercept) 1.198361
#> year2 (Intercept) 0.008826
#> Number of obs: 320, groups: individ, 74; year2, 5
#> Fixed Effects:
#> (Intercept) size2added
#> 2.0352 0.6344
#> optimizer (Nelder_Mead) convergence code: 0 (OK) ; 0 optimizer warnings; 1 lme4 warnings
#>
#> ────────────────────────────────────────
#>
#> Observation model:
#> Generalized linear mixed model fit by maximum likelihood (Laplace Approximation) ['glmerMod']
#> Family: binomial ( logit )
#> Formula: obsstatus3 ~ size2added + (1 | year2) + (1 | individ)
#> Data: obs.data
#> AIC BIC logLik deviance df.resid
#> 118.2567 133.1117 -55.1284 110.2567 299
#> Random effects:
#> Groups Name Std.Dev.
#> individ (Intercept) 1.078e-05
#> year2 (Intercept) 8.776e-01
#> Number of obs: 303, groups: individ, 70; year2, 5
#> Fixed Effects:
#> (Intercept) size2added
#> 2.4904 0.3134
#> optimizer (Nelder_Mead) convergence code: 0 (OK) ; 0 optimizer warnings; 1 lme4 warnings
#>
#> ────────────────────────────────────────
#>
#> Size model:
#> Formula: size3added ~ repstatus2 + size2added + (1 | year2) + (1 | individ) + repstatus2:size2added
#> Data: size.data
#> AIC BIC logLik df.resid
#> 1117.7882 1143.4289 -551.8941 281
#> Random-effects (co)variances:
#>
#> Conditional model:
#> Groups Name Std.Dev.
#> year2 (Intercept) 0.09953
#> individ (Intercept) 0.51799
#>
#> Number of obs: 288 / Conditional model: year2, 5; individ, 70
#>
#> Overdispersion parameter for nbinom2 family (): 6.07e+07
#>
#> Fixed Effects:
#>
#> Conditional model:
#> (Intercept) repstatus2 size2added repstatus2:size2added
#> 0.71946 0.25222 0.06979 -0.03505
#>
#> ────────────────────────────────────────
#>
#> Reproductive status model:
#> Generalized linear mixed model fit by maximum likelihood (Laplace Approximation) ['glmerMod']
#> Family: binomial ( logit )
#> Formula: repstatus3 ~ repstatus2 + size2added + (1 | year2) + (1 | individ)
#> Data: repst.data
#> AIC BIC logLik deviance df.resid
#> 333.6176 351.9324 -161.8088 323.6176 283
#> Random effects:
#> Groups Name Std.Dev.
#> individ (Intercept) 0.1829
#> year2 (Intercept) 0.6250
#> Number of obs: 288, groups: individ, 70; year2, 5
#> Fixed Effects:
#> (Intercept) repstatus2 size2added
#> -1.4630 1.6457 0.1715
#>
#> ────────────────────────────────────────
#>
#> Fecundity model:
#> Formula: feca2 ~ size2added + (1 | year2) + (1 | individ)
#> Zero inflation: ~size2added + (1 | year2) + (1 | individ)
#> Data: fec.data
#> AIC BIC logLik df.resid
#> 250.8609 275.7971 -116.4305 109
#> Random-effects (co)variances:
#>
#> Conditional model:
#> Groups Name Std.Dev.
#> year2 (Intercept) 0.5761
#> individ (Intercept) 0.1640
#>
#> Zero-inflation model:
#> Groups Name Std.Dev.
#> year2 (Intercept) 1.278e-06
#> individ (Intercept) 2.123e-04
#>
#> Number of obs: 118 / Conditional model: year2, 5; individ, 51 / Zero-inflation model: year2, 5; individ, 51
#>
#> Overdispersion parameter for nbinom2 family (): 1.11e+07
#>
#> Fixed Effects:
#>
#> Conditional model:
#> (Intercept) size2added
#> -0.54014 0.06174
#>
#> Zero-inflation model:
#> (Intercept) size2added
#> 3.865 -1.574
#>
#> ────────────────────────────────────────────────────────────────────────────────
#> Juvenile survival model:
#> [1] 1
#>
#> ────────────────────────────────────────
#>
#> Juvenile observation model:
#> [1] 1
#>
#> ────────────────────────────────────────
#>
#> Juvenile size model:
#> [1] 1
#>
#> ────────────────────────────────────────
#>
#> Juvenile reproduction model:
#> [1] 0
#>
#>
#> ────────────────────────────────────────────────────────────────────────────────
#>
#> Number of models in survival table:5
#>
#> Number of models in observation table:5
#>
#> Number of models in size table:5
#>
#> Number of models in reproduction status table:5
#>
#> Number of models in fecundity table:3
#>
#> Number of models in juvenile survival table: 1
#>
#> Number of models in juvenile observation table: 1
#>
#> Number of models in juvenile size table: 1
#>
#> Number of models in juvenile reproduction table: 1
#>
#>
#> ────────────────────────────────────────────────────────────────────────────────
#>
#> General model parameter names (column 1), and specific names used in these models (column 2):
#> parameter_names mainparams
#> 1 time t year2
#> 2 individual individ
#> 3 patch patch
#> 4 alive in time t+1 surv3
#> 5 observed in time t+1 obs3
#> 6 size in time t+1 size3
#> 7 reproductive status in time t+1 repst3
#> 8 fecundity in time t+1 fec3
#> 9 fecundity in time t fec2
#> 10 size in time t size2
#> 11 size in time t-1 size1
#> 12 reproductive status in time t repst2
#> 13 reprodutive status in time t-1 repst1
#> 14 age in time t age
#> 15 individual covariate a in time t indcova2
#> 16 individual covariate a in time t-1 indcova1
#> 17 individual covariate b in time t indcovb2
#> 18 individual covariate b in time t-1 indcovb1
#> 19 individual covariate c in time t indcovc2
#> 20 individual covariate c in time t-1 indcovc1
#>
#>
#> ────────────────────────────────────────────────────────────────────────────────
#>
#> Quality control:
#>
#> Survival estimated with 74 individuals and 320 individual transitions.
#> Observation estimated with 70 individuals and 303 individual transitions.
#> Size estimated with 70 individuals and 288 individual transitions.
#> Reproductive status estimated with 70 individuals and 288 individual transitions.
#> Fecundity estimated with 51 individuals and 118 individual transitions.
#> Juvenile survival not estimated.
#> Juvenile observation probability not estimated.
#> Juvenile size transition not estimated.
#> Juvenile reproduction probability not estimated.
#> NULL
Fewer models were estimated per dredge, since fewer parameters were tested in the global models (size and reproductive status in time t-1 were not included). So, the best-fit models should look a little bit different. However, a more thorough comparison will show that many of the best-fit models are similar between historical and ahistorical analysis. This is not guaranteed - in this case, it may be that the relatively small number of years and small overall sample size leaves too little power to find an impact of historical status on most vital rates. However, with size impacted by history, the choice of a historical model is better here.
Now we will create function-based MPMs. Function-based matrices have quickly been taking over in population ecology, probably because of their ability to parse out interesting trends and influential factors picked up by the linear modeling of vital rates. Let’s first create a set of ahistorical matrices.
<- flefko2(stageframe = cypframe, repmatrix = rep.assumptions,
cypmatrix2 modelsuite = cypmodels2, overwrite = cypover2, data = vertdata,
year.as.random = TRUE)
summary(cypmatrix2)
#>
#> This ahistorical lefkoMat object contains 5 matrices.
#>
#> Each matrix is a square matrix with 54 rows and columns, and a total of 2916 elements.
#> A total of 12055 survival transitions were estimated, with 2411 per matrix.
#> A total of 240 fecundity transitions were estimated, with 48 per matrix.
#>
#> Vital rate modeling quality control:
#>
#> Survival estimated with 74 individuals and 320 individual transitions.
#> Observation estimated with 70 individuals and 303 individual transitions.
#> Size estimated with 70 individuals and 288 individual transitions.
#> Reproductive status estimated with 70 individuals and 288 individual transitions.
#> Fecundity estimated with 51 individuals and 118 individual transitions.
#> Juvenile survival not estimated.
#> Juvenile observation probability not estimated.
#> Juvenile size transition not estimated.
#> Juvenile reproduction probability not estimated.
#> NULL
A quick glance at the summary output will highlight that many more elements are estimated for function-based matrices than for raw matrices - this time 2,459 out of 2,916 total elements (84.3%). In raw matrices, elements associated with transitions from specific stages are only estimated when individuals actually exist within those particular stages. In function-based matrices, in contrast, the linear models estimated allow the estimation of all elements that are theoretically possible (i.e. only structural 0s are not estimated). Let’s take a look at an example matrix, but only on the top corner to deal with its size.
print(cypmatrix2$A[[1]][1:25, 1:8], digits = 3)
#> [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
#> [1,] 0.1 0.0 0.0 0.00 0.000 0.00e+00 0.00e+00 0.00e+00
#> [2,] 0.2 0.0 0.0 0.00 0.000 0.00e+00 0.00e+00 0.00e+00
#> [3,] 0.0 0.2 0.0 0.00 0.000 0.00e+00 0.00e+00 0.00e+00
#> [4,] 0.0 0.0 0.2 0.00 0.000 0.00e+00 0.00e+00 0.00e+00
#> [5,] 0.0 0.0 0.0 0.25 0.400 0.00e+00 0.00e+00 0.00e+00
#> [6,] 0.0 0.0 0.0 0.00 0.047 4.70e-02 3.69e-02 2.81e-02
#> [7,] 0.0 0.0 0.0 0.00 0.158 1.58e-01 1.48e-01 1.33e-01
#> [8,] 0.0 0.0 0.0 0.00 0.176 1.76e-01 1.77e-01 1.71e-01
#> [9,] 0.0 0.0 0.0 0.00 0.131 1.31e-01 1.42e-01 1.46e-01
#> [10,] 0.0 0.0 0.0 0.00 0.000 7.31e-02 8.48e-02 9.40e-02
#> [11,] 0.0 0.0 0.0 0.00 0.000 3.26e-02 4.06e-02 4.82e-02
#> [12,] 0.0 0.0 0.0 0.00 0.000 1.21e-02 1.62e-02 2.06e-02
#> [13,] 0.0 0.0 0.0 0.00 0.000 3.87e-03 5.54e-03 7.57e-03
#> [14,] 0.0 0.0 0.0 0.00 0.000 1.08e-03 1.66e-03 2.43e-03
#> [15,] 0.0 0.0 0.0 0.00 0.000 2.68e-04 4.41e-04 6.93e-04
#> [16,] 0.0 0.0 0.0 0.00 0.000 5.98e-05 1.06e-04 1.78e-04
#> [17,] 0.0 0.0 0.0 0.00 0.000 1.21e-05 2.30e-05 4.15e-05
#> [18,] 0.0 0.0 0.0 0.00 0.000 2.26e-06 4.58e-06 8.88e-06
#> [19,] 0.0 0.0 0.0 0.00 0.000 3.88e-07 8.44e-07 1.75e-06
#> [20,] 0.0 0.0 0.0 0.00 0.000 6.19e-08 1.44e-07 3.21e-07
#> [21,] 0.0 0.0 0.0 0.00 0.000 9.21e-09 2.30e-08 5.50e-08
#> [22,] 0.0 0.0 0.0 0.00 0.000 1.29e-09 3.45e-09 8.82e-09
#> [23,] 0.0 0.0 0.0 0.00 0.000 1.69e-10 4.85e-10 1.33e-09
#> [24,] 0.0 0.0 0.0 0.00 0.000 2.09e-11 6.45e-11 1.90e-10
#> [25,] 0.0 0.0 0.0 0.00 0.000 2.46e-12 8.13e-12 2.57e-11
The matrix is overwhelmingly composed of non-zero elements, unlike in the raw matrix case.
Next, we will create a set of historical Lefkovitch matrices.
<- flefko3(stageframe = cypframe, repmatrix = rep.assumptions,
cypmatrix3 modelsuite = cypmodels3, overwrite = cypover3, data = vertdata,
yearcol = "year2", year.as.random = TRUE)
summary(cypmatrix3)
#>
#> This historical lefkoMat object contains 5 matrices.
#>
#> Each matrix is a square matrix with 2916 rows and columns, and a total of 8503056 elements.
#> A total of 588560 survival transitions were estimated, with 117712 per matrix.
#> A total of 12960 fecundity transitions were estimated, with 2592 per matrix.
#>
#> Vital rate modeling quality control:
#>
#> Survival estimated with 74 individuals and 320 individual transitions.
#> Observation estimated with 70 individuals and 303 individual transitions.
#> Size estimated with 70 individuals and 288 individual transitions.
#> Reproductive status estimated with 70 individuals and 288 individual transitions.
#> Fecundity estimated with 51 individuals and 118 individual transitions.
#> Juvenile survival not estimated.
#> Juvenile observation probability not estimated.
#> Juvenile size transition not estimated.
#> Juvenile reproduction probability not estimated.
#> NULL
Once again, we see many more elements estimated (over 8.5 million, in comparison to 2,916 in the ahistorical case), and many more rows and columns (54 rows and columns in the ahistorical case, and 542 = 2,916 rows and columns in the historical case). However, the dominance of structural 0s in historical matrices still yields matrices that are mostly composed of 0s. In this case, with only 120,304 elements estimated per matrix, only 1.4% of elements are non-zero (the equivalent percentage for the ahistorical case is 84.3%). A quick glance at a portion of one matrix will show that.
print(cypmatrix3$A[[1]][2001:2030,2036:2045], digits = 3)
#> [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#> [1,] 0.00e+00 0 0 0 0 0 0 0 0 0
#> [2,] 0.00e+00 0 0 0 0 0 0 0 0 0
#> [3,] 0.00e+00 0 0 0 0 0 0 0 0 0
#> [4,] 4.55e-03 0 0 0 0 0 0 0 0 0
#> [5,] 4.26e-05 0 0 0 0 0 0 0 0 0
#> [6,] 2.23e-04 0 0 0 0 0 0 0 0 0
#> [7,] 7.77e-04 0 0 0 0 0 0 0 0 0
#> [8,] 2.04e-03 0 0 0 0 0 0 0 0 0
#> [9,] 4.28e-03 0 0 0 0 0 0 0 0 0
#> [10,] 7.49e-03 0 0 0 0 0 0 0 0 0
#> [11,] 1.13e-02 0 0 0 0 0 0 0 0 0
#> [12,] 1.49e-02 0 0 0 0 0 0 0 0 0
#> [13,] 1.74e-02 0 0 0 0 0 0 0 0 0
#> [14,] 1.85e-02 0 0 0 0 0 0 0 0 0
#> [15,] 1.78e-02 0 0 0 0 0 0 0 0 0
#> [16,] 1.57e-02 0 0 0 0 0 0 0 0 0
#> [17,] 1.29e-02 0 0 0 0 0 0 0 0 0
#> [18,] 9.77e-03 0 0 0 0 0 0 0 0 0
#> [19,] 6.94e-03 0 0 0 0 0 0 0 0 0
#> [20,] 4.63e-03 0 0 0 0 0 0 0 0 0
#> [21,] 2.91e-03 0 0 0 0 0 0 0 0 0
#> [22,] 1.73e-03 0 0 0 0 0 0 0 0 0
#> [23,] 9.78e-04 0 0 0 0 0 0 0 0 0
#> [24,] 5.25e-04 0 0 0 0 0 0 0 0 0
#> [25,] 2.69e-04 0 0 0 0 0 0 0 0 0
#> [26,] 1.32e-04 0 0 0 0 0 0 0 0 0
#> [27,] 6.18e-05 0 0 0 0 0 0 0 0 0
#> [28,] 2.78e-05 0 0 0 0 0 0 0 0 0
#> [29,] 2.38e-04 0 0 0 0 0 0 0 0 0
#> [30,] 1.24e-03 0 0 0 0 0 0 0 0 0
Now let’s estimate some mean matrices. First, we will create the ahistorical matrices. We will also very quickly calculate the column sums, which represent the actual survival of stages from time t to time t+1.
<- lmean(cypmatrix2)
tmeans2r summary(colSums(tmeans2r$U[[1]]))
#> Min. 1st Qu. Median Mean 3rd Qu. Max.
#> 0.2000 0.9427 0.9828 0.9133 0.9957 0.9999
Next we will estimate the historical element-wise mean matrix, along with its column sums (these represent the surivival of stage pairs in timet t-1 and time t).
<- lmean(cypmatrix3)
tmeans3r summary(colSums(tmeans3r$U[[1]]))
#> Min. 1st Qu. Median Mean 3rd Qu. Max.
#> 0.0000 0.4245 0.9224 0.6997 0.9859 0.9996
It might help to do a more holistic comparison of the ahistorical vs. historical MPMS. Let’s do matrix plots of the ahistorical and historical means. For this purpose, we will use the image2()
function from package popbio
for the ahistorical mean, and the image()
function from the SparseM
package for the historical mean. Please make sure that both packages are installed. Also, do not use the popbio
function for the historical matrix - the function was not designed for large, sparse matrices, and will likely take up significant time and energy with the possibility of crashing in the end.
::image2(tmeans2r$A[[1]], col = c("white", rev(heat.colors(1000)), "black"), border = "black", text.col = NA)
popbiotitle("Ahistorical")
Figure 5.5. Images of ahistorical vs. historical matrix means
::image(SparseM::as.matrix.csr(tmeans3r$A[[1]]))
SparseMtitle("Historical")
Figure 5.5. Images of ahistorical vs. historical matrix means
Historical matrices are often huge and always sparse, just as our historical matrix is in the plot above. Although there are roughly 8.5 million elements, very few are non-zero and those are identified as the gray streaks in the matrix image above. In contrast, the ahistorical matrix is large for an ahistorical matrix, but tiny relative to the historical matrix. It is also quite dense.
For further comparison, let’s decompose the mean historical matrix into conditional historical matrices. We’ll take a look at the matrix conditional on being vegetatively dormant in time t-1.
<- cond_hmpm(tmeans3r)
condm3r print(condm3r$Acond[[1]]$D, digits = 3)
#> [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14] [,15] [,16]
#> [1,] 0 0 0 0 0 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [2,] 0 0 0 0 0 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [3,] 0 0 0 0 0 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [4,] 0 0 0 0 0 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [5,] 0 0 0 0 0 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [6,] 0 0 0 0 0 8.61e-02 6.91e-02 5.37e-02 4.08e-02 3.06e-02 2.28e-02 1.69e-02 1.24e-02 9.13e-03 6.70e-03 4.92e-03
#> [7,] 0 0 0 0 0 2.23e-01 2.23e-01 2.08e-01 1.82e-01 1.51e-01 1.19e-01 8.74e-02 6.03e-02 3.86e-02 2.28e-02 1.23e-02
#> [8,] 0 0 0 0 0 1.38e-01 1.59e-01 1.71e-01 1.73e-01 1.65e-01 1.49e-01 1.25e-01 9.87e-02 7.22e-02 4.85e-02 2.98e-02
#> [9,] 0 0 0 0 0 5.91e-02 7.86e-02 9.75e-02 1.14e-01 1.25e-01 1.29e-01 1.24e-01 1.12e-01 9.30e-02 7.11e-02 4.96e-02
#> [10,] 0 0 0 0 0 1.96e-02 3.01e-02 4.31e-02 5.79e-02 7.31e-02 8.66e-02 9.57e-02 9.82e-02 9.31e-02 8.09e-02 6.39e-02
#> [11,] 0 0 0 0 0 5.36e-03 9.53e-03 1.58e-02 2.44e-02 3.55e-02 4.83e-02 6.11e-02 7.17e-02 7.73e-02 7.61e-02 6.80e-02
#> [12,] 0 0 0 0 0 1.26e-03 2.58e-03 4.94e-03 8.85e-03 1.48e-02 2.32e-02 3.36e-02 4.51e-02 5.54e-02 6.19e-02 6.25e-02
#> [13,] 0 0 0 0 0 2.58e-04 6.15e-04 1.36e-03 2.82e-03 5.45e-03 9.82e-03 1.64e-02 2.51e-02 3.52e-02 4.47e-02 5.10e-02
#> [14,] 0 0 0 0 0 4.73e-05 1.31e-04 3.36e-04 8.05e-04 1.80e-03 3.74e-03 7.18e-03 1.26e-02 2.02e-02 2.92e-02 3.77e-02
#> [15,] 0 0 0 0 0 7.85e-06 2.52e-05 7.51e-05 2.08e-04 5.39e-04 1.29e-03 2.86e-03 5.80e-03 1.06e-02 1.75e-02 2.56e-02
#> [16,] 0 0 0 0 0 1.19e-06 4.44e-06 1.53e-05 4.94e-05 1.48e-04 4.11e-04 1.05e-03 2.45e-03 5.15e-03 9.69e-03 1.61e-02
#> [17,] 0 0 0 0 0 1.66e-07 7.20e-07 2.89e-06 1.08e-05 3.76e-05 1.21e-04 3.57e-04 9.60e-04 2.32e-03 5.01e-03 9.48e-03
#> [18,] 0 0 0 0 0 2.15e-08 1.08e-07 5.05e-07 2.19e-06 8.86e-06 3.30e-05 1.13e-04 3.51e-04 9.79e-04 2.42e-03 5.24e-03
#> [,17] [,18] [,19] [,20] [,21] [,22] [,23] [,24] [,25] [,26] [,27] [,28] [,29] [,30]
#> [1,] 0.00e+00 0.00e+00 0.00e+00 0.000000 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [2,] 0.00e+00 0.00e+00 0.00e+00 0.000000 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [3,] 0.00e+00 0.00e+00 0.00e+00 0.000000 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [4,] 0.00e+00 0.00e+00 0.00e+00 0.000000 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [5,] 0.00e+00 0.00e+00 0.00e+00 0.000000 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [6,] 3.60e-03 2.64e-03 1.93e-03 0.001411 1.03e-03 7.55e-04 5.52e-04 4.03e-04 2.95e-04 2.16e-04 1.58e-04 1.15e-04 8.42e-05 6.16e-05
#> [7,] 5.96e-03 2.57e-03 9.71e-04 0.000314 8.45e-05 1.85e-05 3.15e-06 4.04e-07 3.71e-08 2.31e-09 9.17e-11 2.16e-12 2.80e-14 1.81e-16
#> [8,] 1.65e-02 8.13e-03 3.51e-03 0.001297 4.01e-04 1.01e-04 1.99e-05 2.95e-06 3.13e-07 2.26e-08 1.04e-09 2.84e-11 4.26e-13 3.19e-15
#> [9,] 3.12e-02 1.75e-02 8.57e-03 0.003620 1.28e-03 3.70e-04 8.40e-05 1.44e-05 1.77e-06 1.48e-07 7.88e-09 2.49e-10 4.33e-12 3.75e-14
#> [10,] 4.54e-02 2.88e-02 1.60e-02 0.007691 3.11e-03 1.03e-03 2.68e-04 5.28e-05 7.51e-06 7.26e-07 4.48e-08 1.64e-09 3.30e-11 3.31e-13
#> [11,] 5.46e-02 3.90e-02 2.45e-02 0.013302 6.10e-03 2.30e-03 6.88e-04 1.56e-04 2.56e-05 2.86e-06 2.04e-07 8.69e-09 2.02e-10 2.35e-12
#> [12,] 5.65e-02 4.54e-02 3.20e-02 0.019564 1.01e-02 4.34e-03 1.48e-03 3.86e-04 7.29e-05 9.41e-06 7.79e-07 3.83e-08 1.03e-09 1.39e-11
#> [13,] 5.18e-02 4.67e-02 3.69e-02 0.025243 1.47e-02 7.12e-03 2.76e-03 8.23e-04 1.79e-04 2.66e-05 2.55e-06 1.45e-07 4.52e-09 7.03e-11
#> [14,] 4.31e-02 4.34e-02 3.83e-02 0.029252 1.91e-02 1.04e-02 4.56e-03 1.55e-03 3.85e-04 6.61e-05 7.31e-06 4.82e-07 1.74e-08 3.13e-10
#> [15,] 3.30e-02 3.72e-02 3.65e-02 0.031010 2.25e-02 1.37e-02 6.77e-03 2.61e-03 7.42e-04 1.47e-04 1.87e-05 1.42e-06 5.94e-08 1.24e-09
#> [16,] 2.35e-02 2.97e-02 3.24e-02 0.030512 2.45e-02 1.66e-02 9.17e-03 3.99e-03 1.29e-03 2.93e-04 4.31e-05 3.80e-06 1.83e-07 4.41e-09
#> [17,] 1.56e-02 2.22e-02 2.71e-02 0.028187 2.50e-02 1.87e-02 1.15e-02 5.62e-03 2.07e-03 5.36e-04 9.07e-05 9.22e-06 5.15e-07 1.43e-08
#> [18,] 9.82e-03 1.57e-02 2.14e-02 0.024663 2.41e-02 1.98e-02 1.35e-02 7.35e-03 3.05e-03 9.03e-04 1.75e-04 2.06e-05 1.33e-06 4.27e-08
#> [,31] [,32] [,33] [,34] [,35] [,36] [,37] [,38] [,39] [,40] [,41] [,42] [,43] [,44]
#> [1,] 4.54e-01 3.36e-01 1.49e-01 4.04e-02 8.96e-03 1.88e-03 3.92e-04 8.12e-05 1.68e-05 3.49e-06 7.24e-07 1.50e-07 3.11e-08 6.44e-09
#> [2,] 4.54e-01 3.36e-01 1.49e-01 4.04e-02 8.96e-03 1.88e-03 3.92e-04 8.12e-05 1.68e-05 3.49e-06 7.24e-07 1.50e-07 3.11e-08 6.44e-09
#> [3,] 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [4,] 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [5,] 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [6,] 6.91e-02 5.37e-02 4.08e-02 3.06e-02 2.28e-02 1.69e-02 1.24e-02 9.13e-03 6.70e-03 4.92e-03 3.60e-03 2.64e-03 1.93e-03 1.41e-03
#> [7,] 1.05e-01 8.82e-02 6.88e-02 5.03e-02 3.43e-02 2.18e-02 1.28e-02 6.84e-03 3.32e-03 1.44e-03 5.47e-04 1.80e-04 4.97e-05 1.13e-05
#> [8,] 9.32e-02 8.95e-02 8.02e-02 6.72e-02 5.25e-02 3.81e-02 2.55e-02 1.56e-02 8.66e-03 4.29e-03 1.87e-03 7.05e-04 2.24e-04 5.85e-05
#> [9,] 5.70e-02 6.29e-02 6.46e-02 6.20e-02 5.54e-02 4.59e-02 3.50e-02 2.44e-02 1.54e-02 8.72e-03 4.34e-03 1.87e-03 6.81e-04 2.04e-04
#> [10,] 2.72e-02 3.44e-02 4.06e-02 4.45e-02 4.54e-02 4.29e-02 3.72e-02 2.95e-02 2.12e-02 1.36e-02 7.68e-03 3.77e-03 1.57e-03 5.39e-04
#> [11,] 1.08e-02 1.57e-02 2.12e-02 2.66e-02 3.10e-02 3.33e-02 3.28e-02 2.95e-02 2.39e-02 1.74e-02 1.11e-02 6.19e-03 2.93e-03 1.15e-03
#> [12,] 3.67e-03 6.14e-03 9.54e-03 1.37e-02 1.83e-02 2.24e-02 2.50e-02 2.54e-02 2.33e-02 1.90e-02 1.37e-02 8.64e-03 4.63e-03 2.07e-03
#> [13,] 1.10e-03 2.13e-03 3.81e-03 6.29e-03 9.57e-03 1.34e-02 1.70e-02 1.95e-02 2.01e-02 1.85e-02 1.50e-02 1.06e-02 6.39e-03 3.23e-03
#> [14,] 2.98e-04 6.65e-04 1.37e-03 2.60e-03 4.53e-03 7.22e-03 1.04e-02 1.36e-02 1.58e-02 1.63e-02 1.47e-02 1.16e-02 7.88e-03 4.48e-03
#> [15,] 7.33e-05 1.89e-04 4.49e-04 9.81e-04 1.97e-03 3.58e-03 5.90e-03 8.70e-03 1.14e-02 1.32e-02 1.33e-02 1.17e-02 8.85e-03 5.64e-03
#> [16,] 1.65e-05 4.93e-05 1.35e-04 3.41e-04 7.87e-04 1.65e-03 3.09e-03 5.19e-03 7.67e-03 9.95e-03 1.12e-02 1.10e-02 9.19e-03 6.52e-03
#> [17,] 3.43e-06 1.19e-05 3.78e-05 1.10e-04 2.93e-04 7.04e-04 1.52e-03 2.89e-03 4.85e-03 7.07e-03 8.90e-03 9.64e-03 8.93e-03 7.01e-03
#> [18,] 6.64e-07 2.67e-06 9.82e-06 3.31e-05 1.02e-04 2.82e-04 6.97e-04 1.52e-03 2.89e-03 4.75e-03 6.69e-03 8.03e-03 8.21e-03 7.10e-03
#> [,45] [,46] [,47] [,48] [,49] [,50] [,51] [,52] [,53] [,54]
#> [1,] 1.34e-09 2.77e-10 5.74e-11 1.19e-11 2.47e-12 5.11e-13 1.06e-13 2.20e-14 4.55e-15 9.43e-16
#> [2,] 1.34e-09 2.77e-10 5.74e-11 1.19e-11 2.47e-12 5.11e-13 1.06e-13 2.20e-14 4.55e-15 9.43e-16
#> [3,] 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [4,] 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [5,] 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [6,] 1.03e-03 7.55e-04 5.52e-04 4.03e-04 2.95e-04 2.16e-04 1.58e-04 1.15e-04 8.42e-05 6.16e-05
#> [7,] 2.02e-06 2.77e-07 2.78e-08 1.93e-09 8.74e-11 2.42e-12 3.79e-14 3.08e-16 1.17e-18 1.88e-21
#> [8,] 1.21e-05 1.92e-06 2.23e-07 1.79e-08 9.43e-10 3.03e-11 5.49e-13 5.16e-15 2.28e-17 4.22e-20
#> [9,] 4.88e-05 8.93e-06 1.20e-06 1.12e-07 6.79e-09 2.53e-10 5.31e-12 5.78e-14 2.95e-16 6.31e-19
#> [10,] 1.48e-04 3.12e-05 4.84e-06 5.21e-07 3.68e-08 1.59e-09 3.86e-11 4.86e-13 2.87e-15 7.09e-18
#> [11,] 3.62e-04 8.78e-05 1.57e-05 1.95e-06 1.60e-07 7.97e-09 2.25e-10 3.28e-12 2.24e-14 6.38e-17
#> [12,] 7.43e-04 2.07e-04 4.26e-05 6.12e-06 5.79e-07 3.35e-08 1.09e-09 1.84e-11 1.45e-13 4.79e-16
#> [13,] 1.32e-03 4.21e-04 9.94e-05 1.65e-05 1.80e-06 1.21e-07 4.56e-09 8.90e-11 8.12e-13 3.09e-15
#> [14,] 2.08e-03 7.54e-04 2.04e-04 3.90e-05 4.92e-06 3.81e-07 1.67e-08 3.77e-10 3.97e-12 1.75e-14
#> [15,] 2.94e-03 1.21e-03 3.75e-04 8.24e-05 1.20e-05 1.07e-06 5.43e-08 1.42e-09 1.73e-11 8.79e-14
#> [16,] 3.81e-03 1.77e-03 6.25e-04 1.57e-04 2.63e-05 2.72e-06 1.59e-07 4.82e-09 6.80e-11 3.99e-13
#> [17,] 4.57e-03 2.39e-03 9.53e-04 2.74e-04 5.28e-05 6.29e-06 4.26e-07 1.49e-08 2.43e-10 1.65e-12
#> [18,] 5.12e-03 2.99e-03 1.35e-03 4.40e-04 9.73e-05 1.34e-05 1.05e-06 4.23e-08 7.98e-10 6.24e-12
#> [ reached getOption("max.print") -- omitted 36 rows ]
Everything looks fine at first glance, and we can assess the impacts of history by comparing to the ahistorical mean. Below, we produce a difference matrix, subtracting the mean ahistorical matrix from the conditional matrix for dormancy in time t-1.
print(condm3r$Acond[[1]]$D - tmeans2r$A[[1]], digits = 3)
#> [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14] [,15]
#> [1,] -0.1 0.0 0.0 0.00 0.0000 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [2,] -0.2 0.0 0.0 0.00 0.0000 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [3,] 0.0 -0.2 0.0 0.00 0.0000 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [4,] 0.0 0.0 -0.2 0.00 0.0000 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [5,] 0.0 0.0 0.0 -0.25 -0.4000 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [6,] 0.0 0.0 0.0 0.00 -0.0861 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [7,] 0.0 0.0 0.0 0.00 -0.1662 5.72e-02 6.14e-02 5.82e-02 4.87e-02 3.51e-02 1.98e-02 5.44e-03 -6.23e-03 -1.41e-02 -1.80e-02
#> [8,] 0.0 0.0 0.0 0.00 -0.1706 -3.23e-02 -1.80e-02 -4.48e-03 5.00e-03 8.73e-03 6.44e-03 -7.10e-04 -1.05e-02 -2.04e-02 -2.80e-02
#> [9,] 0.0 0.0 0.0 0.00 -0.1175 -5.83e-02 -5.19e-02 -4.09e-02 -2.81e-02 -1.65e-02 -8.49e-03 -5.83e-03 -8.79e-03 -1.61e-02 -2.53e-02
#> [10,] 0.0 0.0 0.0 0.00 0.0000 -4.14e-02 -4.24e-02 -3.92e-02 -3.23e-02 -2.29e-02 -1.32e-02 -5.51e-03 -2.03e-03 -3.95e-03 -1.09e-02
#> [11,] 0.0 0.0 0.0 0.00 0.0000 -2.01e-02 -2.29e-02 -2.36e-02 -2.18e-02 -1.71e-02 -1.02e-02 -2.28e-03 4.50e-03 7.79e-03 5.93e-03
#> [12,] 0.0 0.0 0.0 0.00 0.0000 -7.68e-03 -9.57e-03 -1.09e-02 -1.10e-02 -9.34e-03 -5.53e-03 3.59e-04 7.39e-03 1.37e-02 1.69e-02
#> [13,] 0.0 0.0 0.0 0.00 0.0000 -2.44e-03 -3.31e-03 -4.10e-03 -4.51e-03 -4.10e-03 -2.33e-03 1.31e-03 6.88e-03 1.36e-02 1.98e-02
#> [14,] 0.0 0.0 0.0 0.00 0.0000 -6.68e-04 -9.83e-04 -1.32e-03 -1.58e-03 -1.53e-03 -7.84e-04 1.17e-03 4.86e-03 1.04e-02 1.71e-02
#> [15,] 0.0 0.0 0.0 0.00 0.0000 -1.61e-04 -2.57e-04 -3.75e-04 -4.84e-04 -4.94e-04 -2.10e-04 7.29e-04 2.84e-03 6.62e-03 1.22e-02
#> [16,] 0.0 0.0 0.0 0.00 0.0000 -3.50e-05 -6.03e-05 -9.50e-05 -1.32e-04 -1.42e-04 -4.08e-05 3.64e-04 1.43e-03 3.68e-03 7.61e-03
#> [17,] 0.0 0.0 0.0 0.00 0.0000 -6.91e-06 -1.28e-05 -2.18e-05 -3.27e-05 -3.69e-05 -3.23e-06 1.55e-04 6.40e-04 1.83e-03 4.26e-03
#> [18,] 0.0 0.0 0.0 0.00 0.0000 -1.25e-06 -2.49e-06 -4.58e-06 -7.40e-06 -8.71e-06 1.69e-06 5.85e-05 2.58e-04 8.26e-04 2.17e-03
#> [,16] [,17] [,18] [,19] [,20] [,21] [,22] [,23] [,24] [,25] [,26] [,27] [,28]
#> [1,] 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.000000 0.000000 0.00000 0.00000 0.00000 0.000000 0.000000 0.00e+00 0.000000
#> [2,] 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.000000 0.000000 0.00000 0.00000 0.00000 0.000000 0.000000 0.00e+00 0.000000
#> [3,] 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.000000 0.000000 0.00000 0.00000 0.00000 0.000000 0.000000 0.00e+00 0.000000
#> [4,] 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.000000 0.000000 0.00000 0.00000 0.00000 0.000000 0.000000 0.00e+00 0.000000
#> [5,] 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.000000 0.000000 0.00000 0.00000 0.00000 0.000000 0.000000 0.00e+00 0.000000
#> [6,] 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.000000 0.000000 0.00000 0.00000 0.00000 0.000000 0.000000 0.00e+00 0.000000
#> [7,] -1.85e-02 -1.67e-02 -1.37e-02 -1.04e-02 -0.007379 -0.004975 -0.00321 -0.00198 -0.00118 -0.000680 -0.000376 -1.99e-04 -0.000102
#> [8,] -3.20e-02 -3.21e-02 -2.92e-02 -2.43e-02 -0.018834 -0.013746 -0.00953 -0.00633 -0.00404 -0.002478 -0.001464 -8.30e-04 -0.000452
#> [9,] -3.36e-02 -3.88e-02 -3.99e-02 -3.71e-02 -0.031716 -0.025250 -0.01893 -0.01350 -0.00922 -0.006053 -0.003821 -2.32e-03 -0.001346
#> [10,] -2.07e-02 -3.06e-02 -3.77e-02 -4.06e-02 -0.039120 -0.034443 -0.02816 -0.02167 -0.01588 -0.011152 -0.007521 -4.87e-03 -0.003023
#> [11,] -1.19e-03 -1.19e-02 -2.31e-02 -3.20e-02 -0.036622 -0.036678 -0.03322 -0.02784 -0.02197 -0.016524 -0.011911 -8.24e-03 -0.005461
#> [12,] 1.50e-02 7.76e-03 -3.33e-03 -1.53e-02 -0.025087 -0.030729 -0.03191 -0.02961 -0.02538 -0.020504 -0.015809 -1.17e-02 -0.008268
#> [13,] 2.29e-02 2.10e-02 1.38e-02 2.70e-03 -0.009203 -0.018968 -0.02479 -0.02647 -0.02505 -0.021888 -0.018087 -1.43e-02 -0.010791
#> [14,] 2.30e-02 2.60e-02 2.39e-02 1.66e-02 0.005857 -0.005370 -0.01436 -0.01965 -0.02133 -0.020450 -0.018195 -1.54e-02 -0.012398
#> [15,] 1.88e-02 2.44e-02 2.68e-02 2.42e-02 0.016800 0.006632 -0.00347 -0.01116 -0.01548 -0.016856 -0.016322 -1.48e-02 -0.012740
#> [16,] 1.32e-02 1.96e-02 2.47e-02 2.61e-02 0.022699 0.015190 0.00580 -0.00285 -0.00897 -0.012166 -0.013157 -1.29e-02 -0.011853
#> [17,] 8.38e-03 1.40e-02 2.00e-02 2.41e-02 0.024259 0.019975 0.01248 0.00412 -0.00287 -0.007350 -0.009513 -1.02e-02 -0.010086
#> [18,] 4.85e-03 9.22e-03 1.48e-02 2.01e-02 0.022843 0.021581 0.01650 0.00930 0.00222 -0.003023 -0.006022 -7.40e-03 -0.007907
#> [,29] [,30] [,31] [,32] [,33] [,34] [,35] [,36] [,37] [,38] [,39] [,40] [,41]
#> [1,] 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [2,] 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [3,] 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [4,] 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [5,] 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [6,] 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [7,] -4.94e-05 -2.30e-05 4.01e-02 2.91e-02 1.71e-02 5.82e-03 -3.25e-03 -9.54e-03 -1.30e-02 -1.42e-02 -1.36e-02 -1.21e-02 -1.02e-02
#> [8,] -2.35e-04 -1.17e-04 5.48e-03 7.69e-03 6.05e-03 1.37e-03 -4.95e-03 -1.14e-02 -1.66e-02 -1.98e-02 -2.09e-02 -2.01e-02 -1.81e-02
#> [9,] -7.48e-04 -3.97e-04 -2.19e-02 -1.31e-02 -6.57e-03 -3.30e-03 -3.52e-03 -6.54e-03 -1.11e-02 -1.57e-02 -1.91e-02 -2.08e-02 -2.06e-02
#> [10,] -1.79e-03 -1.02e-03 -2.64e-02 -1.89e-02 -1.10e-02 -4.35e-03 -1.33e-04 9.86e-04 -8.44e-04 -4.73e-03 -9.33e-03 -1.33e-02 -1.59e-02
#> [11,] -3.46e-03 -2.09e-03 -1.85e-02 -1.45e-02 -8.94e-03 -2.88e-03 2.59e-03 6.32e-03 7.50e-03 5.94e-03 2.25e-03 -2.41e-03 -6.76e-03
#> [12,] -5.60e-03 -3.61e-03 -9.77e-03 -8.13e-03 -5.19e-03 -1.17e-03 3.44e-03 7.80e-03 1.09e-02 1.19e-02 1.04e-02 6.87e-03 2.36e-03
#> [13,] -7.80e-03 -5.38e-03 -4.21e-03 -3.70e-03 -2.41e-03 -2.09e-04 2.89e-03 6.58e-03 1.02e-02 1.28e-02 1.35e-02 1.20e-02 8.73e-03
#> [14,] -9.56e-03 -7.04e-03 -1.55e-03 -1.43e-03 -9.40e-04 1.06e-04 1.88e-03 4.45e-03 7.56e-03 1.06e-02 1.28e-02 1.32e-02 1.17e-02
#> [15,] -1.05e-02 -8.23e-03 -5.01e-04 -4.84e-04 -3.17e-04 1.26e-04 1.03e-03 2.57e-03 4.81e-03 7.55e-03 1.02e-02 1.19e-02 1.20e-02
#> [16,] -1.04e-02 -8.72e-03 -1.45e-04 -1.46e-04 -9.47e-05 7.60e-05 4.86e-04 1.31e-03 2.72e-03 4.78e-03 7.23e-03 9.47e-03 1.07e-02
#> [17,] -9.46e-03 -8.46e-03 -3.80e-05 -4.00e-05 -2.53e-05 3.49e-05 2.04e-04 6.02e-04 1.40e-03 2.76e-03 4.70e-03 6.90e-03 8.71e-03
#> [18,] -7.93e-03 -7.56e-03 -9.13e-06 -1.00e-05 -6.11e-06 1.35e-05 7.79e-05 2.53e-04 6.63e-04 1.48e-03 2.85e-03 4.70e-03 6.63e-03
#> [,42] [,43] [,44] [,45] [,46] [,47] [,48] [,49] [,50] [,51] [,52] [,53] [,54]
#> [1,] 0.00e+00 0.000000 0.000000 0.00000 0.000000 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [2,] 0.00e+00 0.000000 0.000000 0.00000 0.000000 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [3,] 0.00e+00 0.000000 0.000000 0.00000 0.000000 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [4,] 0.00e+00 0.000000 0.000000 0.00000 0.000000 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [5,] 0.00e+00 0.000000 0.000000 0.00000 0.000000 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [6,] 0.00e+00 0.000000 0.000000 0.00000 0.000000 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
#> [7,] -8.24e-03 -0.006511 -0.005060 -0.00389 -0.002962 -2.24e-03 -1.68e-03 -1.25e-03 -9.28e-04 -6.82e-04 -4.98e-04 -3.61e-04 -2.59e-04
#> [8,] -1.55e-02 -0.012839 -0.010382 -0.00827 -0.006520 -5.10e-03 -3.96e-03 -3.05e-03 -2.34e-03 -1.77e-03 -1.34e-03 -1.00e-03 -7.47e-04
#> [9,] -1.91e-02 -0.016767 -0.014214 -0.01178 -0.009619 -7.78e-03 -6.25e-03 -4.98e-03 -3.94e-03 -3.10e-03 -2.42e-03 -1.87e-03 -1.44e-03
#> [10,] -1.67e-02 -0.016019 -0.014487 -0.01259 -0.010696 -8.96e-03 -7.44e-03 -6.13e-03 -5.02e-03 -4.08e-03 -3.29e-03 -2.64e-03 -2.10e-03
#> [11,] -9.85e-03 -0.011343 -0.011457 -0.01069 -0.009533 -8.30e-03 -7.13e-03 -6.08e-03 -5.15e-03 -4.32e-03 -3.61e-03 -2.99e-03 -2.46e-03
#> [12,] -1.92e-03 -0.005082 -0.006803 -0.00730 -0.007029 -6.42e-03 -5.73e-03 -5.06e-03 -4.42e-03 -3.84e-03 -3.32e-03 -2.84e-03 -2.42e-03
#> [13,] 4.58e-03 0.000689 -0.002155 -0.00372 -0.004274 -4.24e-03 -3.96e-03 -3.62e-03 -3.28e-03 -2.95e-03 -2.63e-03 -2.33e-03 -2.05e-03
#> [14,] 8.64e-03 0.004935 0.001606 -0.00071 -0.001928 -2.36e-03 -2.39e-03 -2.28e-03 -2.14e-03 -1.99e-03 -1.83e-03 -1.68e-03 -1.53e-03
#> [15,] 1.04e-02 0.007489 0.004262 0.00157 -0.000158 -9.78e-04 -1.25e-03 -1.28e-03 -1.25e-03 -1.20e-03 -1.15e-03 -1.08e-03 -1.02e-03
#> [16,] 1.04e-02 0.008620 0.005921 0.00319 0.001139 -2.27e-05 -5.00e-04 -6.35e-04 -6.59e-04 -6.57e-04 -6.48e-04 -6.34e-04 -6.16e-04
#> [17,] 9.44e-03 0.008714 0.006777 0.00432 0.002118 6.70e-04 -2.31e-05 -2.57e-04 -3.14e-04 -3.28e-04 -3.35e-04 -3.39e-04 -3.41e-04
#> [18,] 7.97e-03 0.008136 0.007018 0.00503 0.002883 1.23e-03 3.16e-04 -3.61e-05 -1.29e-04 -1.50e-04 -1.60e-04 -1.67e-04 -1.74e-04
#> [ reached getOption("max.print") -- omitted 36 rows ]
The first five columns reflect the fact that the conditional matrix does not include juvenile transitions. The remaining columns diverge from the ahistorical matrix widely.
Let’s move on to some of the analyses that are currently possible with lefko3
.
Let’s now conduct some quick population analyses. First, we can estimate the deterministic growth rate for these means.
lambda3(tmeans2r)
#> pop patch lambda
#> 1 1 1 0.9029831
lambda3(tmeans3r)
#> pop patch lambda
#> 1 1 1 0.9437312
These are very similar \(\lambda\) values, although the historical is slightly larger than the ahistorical. Let’s take a look at a plot of annual matrices.
<- lambda3(cypmatrix3)
cypann3 <- lambda3(cypmatrix2)
cypann2
plot(lambda ~ year2, data = cypann2, xlab = "Year", ylab = "Lambda", ylim = c(0.80, 1.00), type = "l", lty= 1, lwd = 2, bty = "n")
lines(lambda ~ year2, data = cypann3, lty = 2, lwd= 2, col = "red")
legend("bottomright", c("ahistorical", "historical"), lty = c(1, 2), col = c("black", "red"), lwd = 2, bty = "n")
Figure 5.6. Ahistorical vs. historical lambda
We find that annual \(\lambda\) differs between ahistorical and historical analyses. It is likely that the historical approach is more accurate, given the ability of this style of analysis to pick out the underlying statistical patterns occurring in a dataset, suggesting that the earlier years were worse for the population than we might have initially thought given the ahistorical analysis.
We may also compare the stochastic population growth rate, \(a = \text{log} \lambda _{S}\), as below.
slambda3(cypmatrix2)
#> pop patch a var sd se
#> 1 1 1 -0.1028182 0.001783574 0.04223238 0.0004223238
slambda3(cypmatrix3)
#> pop patch a var sd se
#> 1 1 1 -0.05924337 0.001548299 0.03934844 0.0003934844
We find that historical \(a\) is higher than ahistorical \(a\), as was the case with \(\lambda\), although both show declining populations over time.
Now let’s take a look at the stable stage distributions. First the ahistorical case, with summary.
<- stablestage3(tmeans2r)
tm2ss <- stablestage3(tmeans3r)
tm3ss
<- cbind.data.frame(tm2ss$ss_prop, tm3ss$ahist$ss_prop)
ss_put_together names(ss_put_together) <- c("ahist", "hist")
rownames(ss_put_together) <- tm2ss$stage_id
barplot(t(ss_put_together), beside=T, ylab = "Proportion", xlab = "Stage", ylim = c(0, 0.15),
col = c("black", "red"), bty = "n")
legend("topright", c("ahistorical", "historical"), col = c("black", "red"), pch = 15, bty = "n")
Figure 5.7. Ahistorical vs. historically-corrected stable stage distribution
Overall, these look like fairly large shifts. Ahistorical analysis suggests that small non-reproductive adults take up the greatest share of the stable stage structure, with dormant seed and 1st-year protocorms coming next. In contrast, historical analysis suggests that the greatest share of the stable stage structure comes from reproductive individuals.
Finally, let’s take a peek at the reproductive values associated with both ahistorical and historical approaches.
<- repvalue3(tmeans2r)
tm2rv <- repvalue3(tmeans3r)$ahist
tm3rv
<- cbind.data.frame(tm2rv$rep_value, tm3rv$rep_value)
rv_put_together names(rv_put_together) <- c("ahist", "hist")
$ahist <- rv_put_together$ahist / max(rv_put_together$ahist)
rv_put_together$hist <- rv_put_together$hist / max(rv_put_together$hist)
rv_put_togetherrownames(rv_put_together) <- tm2rv$stage_id
barplot(t(rv_put_together), beside=T, ylab = "Relative rep value", xlab = "Stage",
col = c("black", "red"), bty = "n")
legend("topleft", c("ahistorical", "historical"), col = c("black", "red"), pch = 15, bty = "n")
Figure 5.8. Ahistorical vs. historically-corrected reproductive values
Note that the historical case predicts a greater importance of small adults and lesser importance of large adults than the ahistorical matrix does. Interesting results in need of further study!
Let’s now do a sensitivity analysis.
<- sensitivity3(tmeans2r)
tm2sens
writeLines("\nThe highest sensitivity value: ")
#>
#> The highest sensitivity value:
max(tm2sens$ah_sensmats[[1]][which(tmeans2r$A[[1]] > 0)])
#> [1] 0.1761273
writeLines("\nThis value is associated with element: ")
#>
#> This value is associated with element:
which(tm2sens$ah_sensmats[[1]] == max(tm2sens$ah_sensmats[[1]][which(tmeans2r$A[[1]] > 0)]))
#> [1] 408
The highest sensitivity value appears to be associated with the transition from 2-sprouted non-flowering adult to the largest non-flowering adult. Inspecting the sensitivity matrix (type tm2sens$ah_sensmats[[1]]
to inspect the full matrix) also shows that transitions near that element in the matrix are also associated with rather high sensitivities.
Now we can compare the historical case, particularly focusing on the historically-corrected sensitivities.
<- sensitivity3(tmeans3r)
tm3sens
writeLines("\nThe highest sensitivity value: ")
#>
#> The highest sensitivity value:
max(tm3sens$ah_sensmats[[1]][which(tmeans2r$A[[1]] > 0)])
#> [1] 0.1142779
writeLines("\nThis value is associated with element: ")
#>
#> This value is associated with element:
which(tm3sens$ah_sensmats[[1]] == max(tm3sens$ah_sensmats[[1]][which(tmeans2r$A[[1]] > 0)]))
#> [1] 2266
Here, the highest sensitivity value is associated with transition from 12-sprouted flowering adult (Sz5nr) to 22-sprouted flowering adults, making it rather different the ahistorical case. The sensitivity associated with transitions from smaller-sized individuals to larger flowering individuals appears to be similarly large relative to other elements.
Let’s now assess the elasticity of \(\lambda\) to matrix elements, comparing the ahistorical to the historically-corrected case.
<- elasticity3(tmeans2r)
tm2elas <- elasticity3(tmeans3r)
tm3elas
writeLines("\nThe largest ahistorical elasticity is associated with element: ")
#>
#> The largest ahistorical elasticity is associated with element:
which(tm2elas$ah_elasmats[[1]] == max(tm2elas$ah_elasmats[[1]]))
#> [1] 386
writeLines("\nThe largest historically-corrected elasticity is associated with element: ")
#>
#> The largest historically-corrected elasticity is associated with element:
which(tm3elas$ah_elasmats[[1]] == max(tm3elas$ah_elasmats[[1]]))
#> [1] 2256
Ahistorical analysis suggests that \(\lambda\) is most elastic in response to the transition from 2-sprouted to 2-sprouted non-flowering status. In the historically-corrected case, \(\lambda\) is most elastic to stasis as a 12-sprouted flowering individual. Users examining the elasticity matrices will also see further differences, with different transitions exerting different magnitudes of elasticity. The overall patterns look rather different between ahistorical and historical MPMs.
Now let’s compare the elasticity of \(\lambda\) in relation to the core life history stages, via a barplot comparison.
<- cbind.data.frame(colSums(tm2elas$ah_elasmats[[1]]), colSums(tm3elas$ah_elasmats[[1]]))
elas_put_together names(elas_put_together) <- c("ahist", "hist")
rownames(elas_put_together) <- tm2elas$stages$stage_id
barplot(t(elas_put_together), beside=T, ylab = "Elasticity of lambda", xlab = "Stage",
col = c("black", "red"), bty = "n")
legend("topright", c("ahistorical", "historical"), col = c("black", "red"), pch = 15, bty = "n")
Figure 5.9. Ahistorical vs. historically-corrected elasticity of lambda to stage
The historical barplots show that incorporating individual history leads to greater elasticity values in larger adults and in flowering adults, while in the ahistorical case we see greater elasticity in smaller individuals, with greater elasticity in non-flowering stages. These patterns look somewhat reminiscent of the stable stage profiles that we saw in Figure 5.7. Further analysis and exploration will be required to flesh out why we see the patterns that we do here.
Finally, let’s take a look at how the importance of different kinds of transitions changes, by looking at elasticity sums.
<- summary(tm2elas)
tm2elas_sums <- summary(tm3elas)
tm3elas_sums
<- cbind.data.frame(tm2elas_sums$ahist[,2], tm3elas_sums$ahist[,2])
elas_sums_together names(elas_sums_together) <- c("ahist", "hist")
rownames(elas_sums_together) <- tm2elas_sums$ahist$category
barplot(t(elas_sums_together), beside=T, ylab = "Elasticity of lambda", xlab = "Transition",
col = c("black", "red"), bty = "n")
legend("topright", c("ahistorical", "historical"), col = c("black", "red"), pch = 15, bty = "n")
Figure 5.10. Ahistorical vs. historically-corrected elasticity of lambda to transitions
Fecundity makes little difference in both cases, but the importance of stasis transitions drops in historical analysis, while growth and shrinkage transitions both increase in importance. Next, we can see which historical transitions are most important.
<- as.data.frame(tm3elas_sums$hist[,2])
elas_hist2plot names(elas_hist2plot) <- c("hist")
rownames(elas_hist2plot) <- tm3elas_sums$hist$category
barplot(t(elas_hist2plot), ylab = "Elasticity of lambda", xlab = "Transition",
col = c("red"), bty = "n")
We can see that shrinkage from time t-1 to t followed by growth to time t+1 is associated with the greatest summed elasticities, while the inverse, growth from t-1 to time t followed by shrinkage to time t+1 is the next most important. Transitions associated with fecundity are associated with the lowest summed elasticities.
Package lefko3
also has the ability to estimate stochastic sensitivities and elasticities. The same functions are used to conduct these analyses, but we set the stochastic
option to TRUE
. Here, we illustrate the elasticity case.
<- elasticity3(cypmatrix2, stochastic = TRUE)
tm2elas_s <- elasticity3(cypmatrix3, stochastic = TRUE) tm3elas_s
We encourage users to compare these sensitivity and elasticity matrices with their deterministic equivalents.
Further analytical tools are being planned for lefko3
, but packages that handle projection matrices can typically handle the individual matrices produced and saved in lefkoMat
objects in this package. Differences, obscure results, and errors sometimes arise when packages are not made to handle large and/or sparse matrices - historical matrices are both, and so care must be taken with their analysis.
We are grateful to two anonymous reviewers whose scrutiny improved the quality of this vignette. The project resulting in this package and this tutorial was funded by Grant-In-Aid 19H03298 from the Japan Society for the Promotion of Science.