---
title: "The *Lepanthes eltoroensis* analysis"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{The Lepanthes eltoroensis analysis}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 4.5,
                      eval = requireNamespace("dplyr", quietly = TRUE))
```

```{r, message = FALSE}
library(recruitlag)
library(dplyr)
```

This vignette reproduces the analysis of the companion paper on the
epiphytic orchid *Lepanthes eltoroensis* (Luquillo Mountains, Puerto Rico),
in which reproduction is continuous in time and recruitment is episodic. The
package's tests check the numbers below against the archived outputs of the
paper's pipeline; here they are simply computed.

## The two records

```{r}
lepanthes_monthly
lepanthes_census |> select(period, census, adults, inflorescences, recruits)
lepanthes_hosts
```

The monthly survey (1994 to 1996) counts inflorescence-bearing adults and new
seedlings at 23 monthly censuses of four populations; recruits are defined by
first appearance, so they are scored from census 2. The six-monthly survey
(1999 to 2004) counts every plant on 23 host trees at 12 censuses; recruits
are scored at censuses 3 to 12. `lepanthes_census` holds its system totals
and `lepanthes_hosts` one row per host tree and census, which is the input
of the host-level test.

The lag conventions differ, and the package makes them explicit. The
monthly lag model of the paper reads lag *k* at month *t - k* over a
six-month horizon (`K = 6`, `lag0 = 0`). The six-monthly model reads lag
*k* at census *t - 1 - k* over five six-month bins (`K = 4`, `lag0 = 1`).
The bins are labelled 0-6, 6-12, 12-18, 18-24 and 24-30 months, counted
from the first lagged census, which is the convention of the kernel labels
throughout.

## Concentration and the ceiling

```{r}
k_m <- lag_kernels(6, bin = 1, unit = "mo")
k_6 <- lag_kernels(4, bin = 6, unit = "mo")
c_m <- lag_ceiling(lepanthes_monthly, reproduction = "reproductive_adults", K = 6, lag0 = 0, kernels = k_m)
c_6 <- lag_ceiling(lepanthes_census, reproduction = "inflorescences", K = 4, lag0 = 1, kernels = k_6)
c_m
c_6
```

At both time scales the ceiling is low because the input is flat, the
searched maximum is attained by a pure delay and lies within 0.01 of the
theorem bound (the small difference arises from backfilling pre-record
lags), and observed recruitment exceeds the ceiling by a factor of about
`r round(c_m$exceedance["gini"])` (monthly) and
`r round(c_6$exceedance["gini"], 1)` (six-monthly) on the Gini.

```{r}
rain_strip(`monthly 1994` = c_m, `six-monthly 1999-2004` = c_6)
```

## What any lag profile predicts

```{r}
profiles <- list(
  `one interval only (projection-matrix null)` = c(1, 0, 0, 0, 0),
  `concentrated early (0 to 6 months)` = c(.7, .2, .07, .03, 0),
  `evenly spread (0 to 30 months)` = rep(.2, 5),
  `concentrated late (24 to 30 months)` = c(0, .03, .07, .2, .7),
  `fitted posterior mean` = lepanthes_profile$weight)
rain_profiles(profiles, bin = 6, unit = "months")
rain_expected(c_6, profiles)
```

The expected series all but coincide and follow the slow decline of
reproduction; none resembles the recruits counted (62 at census 5, none at
censuses 7, 8 and 12, 32 at census 10). The fitted posterior-mean profile
from the paper's distributed-lag model is among them and is
indistinguishable from the rest, which is the identification problem the
paper describes: with a flat input the lag cannot be recovered, however long
the series.

## How firm the exceedance is

The ceiling is arithmetic and carries no uncertainty. The two concentration
indices it is compared with are estimates from ten censuses, and so is their
ratio, and a reader is entitled to an interval on them. The package gives
three, because they answer differently worded questions and a user should
take the one whose logic they accept.

The first resamples the 23 host trees with replacement, rebuilds both series
from the resampled trees, and recomputes the ceiling and the exceedance in
each replicate. It is a confidence interval, BCa by default, and is read in
the usual way. The share of replicates at or below 1 is reported as what it
is, a one-sided bootstrap p-value, and not as a probability that the
exceedance is at most 1.

```{r}
set.seed(20260913)
k4 <- lag_kernels(4, bin = 6, unit = "mo")
lag_ceiling_boot(lepanthes_hosts, unit = "host", reproduction = "inflorescences",
                 K = 4, kernels = k4, R = 2000)
```

The second is Rubin's Bayesian bootstrap: Dirichlet weights over the hosts
in place of resampling, and a posterior in place of a sampling distribution.
The numbers are close to the first, which is expected; the difference is
that `P(exceedance > 1)` is now a posterior probability under a stated
prior and may be written as one.

```{r}
lag_ceiling_bayesboot(lepanthes_hosts, unit = "host", reproduction = "inflorescences",
                      K = 4, kernels = k4, draws = 4000)
```

The third fits a negative-binomial model to the counts on every host at
every census, with host and census effects, and puts the posterior of the
**expected** series through the same ceiling arithmetic. That is the object
the bound applies to, and its exceedance is lower than the raw one, because
part of the concentration of small counts is sampling variation about a
smoother mean. It needs `cmdstanr` and CmdStan, so it is not run here.

```{r, eval = FALSE}
lag_ceiling_stan(lepanthes_hosts, unit = "host", reproduction = "inflorescences",
                 K = 4, kernels = k4)
```

## Why the pooled comparison cannot decide

```{r}
set.seed(20260903)
cal <- ceiling_calibration(c_6)
cal
```

About a mean that obeys the ceiling, negative-binomial counts with the
observed variance-to-mean ratio produce a series as concentrated as the
observed one roughly one time in
`r round(1 / cal$p_gini[cal$null == "N1 best-kernel mean"])`. The exceedance of the ceiling by the
pooled counts is therefore not evidence; the test has to be made across
host trees.

## The host-level test

```{r}
lepanthes_hosts |>
  filter(!is.na(recruits)) |>
  group_by(period) |>
  summarise(recruits = sum(recruits), hosts_recruiting = sum(recruits > 0))
```

Three of the ten censuses are silent (no recruit on any of the 23 hosts) and
one carries 62. Under the lag hypothesis, expected recruitment on each host
is that host's own lagged inflorescence record, scaled to its own total.
The kernels are the pure delays, every uniform window, geometric decay and
the fitted profile; the paper adds 800 Dirichlet draws, which take longer
and change nothing.

```{r}
set.seed(20260910)
ht <- host_lag_test(lepanthes_hosts, unit = "host", reproduction = "inflorescences",
                    K = 4, nsim = 4000,
                    kernels = lag_kernels(4, bin = 6, unit = "mo",
                                          extra = list(`fitted posterior mean` = lepanthes_profile$weight)),
                    phi = 0.089)
ht
```

The moment estimate of the clumping parameter under the flat reference is
about `r signif(ht$table$phi[ht$table$family == "flat" & ht$table$phi_source == "moment"], 2)`;
the paper also
evaluates every profile at 0.089, the estimate obtained about a single
pooled mean, which attributes the between-host spread of the means to
clumping and is about twice the clumping the counts support. Even at that
value the most favourable profile does not make three silent censuses
ordinary.

```{r}
plot(ht, statistic = "silent")
plot(ht, statistic = "gini")
```

## The verdict, keyed on labels

```{r}
ht$verdict
ht$table |>
  filter(phi_source == "moment",
         label %in% c("flat reference", "pure delay 24-30 mo", "uniform 0-30 mo", "fitted posterior mean")) |>
  select(label, phi, gini_expected, p_silent, p_gini, p_cv)
```

No lag profile, of any shape over the 30-month window, makes the observed record
ordinary. Recruitment in this population is not the delayed consequence of
its own reproduction; something else opens and closes.
