Restoration
Survival
Plant records compared at equal plant age.
Follow-up at 24 months
Follow-up states, not a pooled survival result.
What the four states mean
- Latest status recorded
- Read at 24 months of age or later, or recorded dead before then.
- Never read
- Planted, with no read of the plant on file.
- Not found
- Looked for and not found, with no status at 24 months.
- Monitoring ended
- Read at least once, then no read by 24 months and no death on file.
Alive at 24 months
Each range runs from every plant without a status counted dead (the solid part) to every such plant counted alive (the hatched part).
79 planted · 40 read at least once · no status for 39 never read
194 planted · 138 read at least once · no status for 56 never read
69 planted · 69 read at least once · every plant has a status
15 planted · 15 read at least once · every plant has a status
Follow-up of the whole cohort ends at 9.7 months. 86 planted · 78 read at least once · no status for 8 never read.
22 planted · 22 read at least once · every plant has a status
Reading this result
- One row is
- One planting cohort: the plants set out in one planting year.
- Counted over
- Every plant of the cohort, read or not: 465 plants in 6 cohorts.
- Left out
- No plant. A plant with no status widens its cohort's range instead of leaving the count.
- Bear in mind
- Cohorts differ in species, planting method and site, so a gap between two cohorts is not the effect of any one of them.
Cohort by cohort, 0 to 24 months
- Range: plants without a status counted dead to counted alive
- Share of the read plants still alive
- Follow-up of the whole cohort ends
6 cohort charts in one row: scroll sideways for the rest.
Each milestone carries the latest read at or before that age. Drawn to 24 months; the values table lists every read inside each cohort's follow-up.
The first year
By 17 Aug 2026 every one of the 6 cohorts had a full year of plant age behind it. Both milestones in a row come from that cohort's own plants, so they share one denominator.
The 6- and 12-month table, 6 cohorts
| Cohort | Planted | Alive at 6 months | Alive at 12 months |
|---|---|---|---|
| 2019 | 79 | 48% to 97% | 42% to 91% |
| 2020 | 194 | 69% to 98% | 57% to 86% |
| 2021 | 69 | 100% | 90% |
| 2022 | 15 | 13% | 0% |
| 2023 | 86 | 84% to 93% | Not followed to 12 months (follow-up ends at 9.7 months) |
| 2024 | 22 | 82% | 32% |
What was planted
The cohorts differ before any plant is read: species and planting method change from year to year.
Species and method for 6 cohorts, 465 plants
2019 cohort 79 planted
- Species
- Foothill palo verde 20 · Ironwood 18 · Saguaro 16 · Velvet mesquite 13 · Blue palo verde 12
- Method
- Nursery stock 44 · Salvaged transplant 25 · Direct seeding 10
2020 cohort 194 planted
- Species
- Foothill palo verde 59 · Ironwood 42 · Blue palo verde 34 · Velvet mesquite 31 · Saguaro 28
- Method
- Nursery stock 126 · Salvaged transplant 52 · Direct seeding 16
2021 cohort 69 planted
- Species
- Foothill palo verde 17 · Blue palo verde 16 · Ironwood 12 · Saguaro 12 · Velvet mesquite 12
- Method
- Nursery stock 41 · Salvaged transplant 17 · Direct seeding 11
2022 cohort 15 planted
- Species
- Foothill palo verde 7 · Velvet mesquite 4 · Blue palo verde 2 · Saguaro 2
- Method
- Nursery stock 9 · Salvaged transplant 4 · Direct seeding 2
2023 cohort 86 planted
- Species
- Ironwood 21 · Foothill palo verde 18 · Velvet mesquite 17 · Blue palo verde 16 · Saguaro 14
- Method
- Nursery stock 54 · Salvaged transplant 28 · Direct seeding 4
2024 cohort 22 planted
- Species
- Foothill palo verde 6 · Ironwood 6 · Velvet mesquite 6 · Blue palo verde 3 · Saguaro 1
- Method
- Nursery stock 13 · Salvaged transplant 6 · Direct seeding 3
What changed between visits
| Earlier → later | Alive | Stressed | Dead |
|---|---|---|---|
| Alive | 375 | 57 | 199 |
| Stressed | · | 106 | 67 |
| Dead | 13 | · | 1,169 |
Counts per pair of consecutive reads of one plant on different days, not per year; 0 same-day pairs excluded. Status can reverse: the amber cell opens the 13 plant records the dead-then-alive check lists. No time-to-event curve or transition probability is reported.
Survival values
How the ranges are counted
- Plants are grouped by planting year. Cohorts are never pooled into one number.
- Plant age comes from each read. A plant counts dead from the first read that records it dead.
- Each range has two ends: plants without a status all counted dead, then all counted alive.
- A cohort is drawn only up to the age at which its first unresolved plant stops being read.
Behind this number
5 functions compute it. Each is shown exactly as this build runs it.
.rstp_survival_frame
apps/hub/R/view_restoration_product.R
.rstp_survival_frame <- function (loaded)
{
rst_require_claim("survival_bounds", "FOLLOWED", ".rstp_survival_frame()")
pool <- rst_pool(.rstp_public_analysis_loaded(loaded))
if (!isTRUE(pool$cohort_nested_in_plot) || !identical(as.integer(sort(pool$cohorts_spanned)),
2019:2024))
return(NULL)
out <- do.call(rbind, lapply(pool$cohorts_spanned, function(cohort) {
d <- .rst_survival_series(pool, cohort)
d$horizon_mo <- unname(pool$horizon_months[[as.character(cohort)]])
d
}))
rownames(out) <- NULL
attr(out, "pool") <- pool
out
}
.o1r_milestones
apps/hub/R/rst_survival_revamp.R
.o1r_milestones <- function (frame, milestones = O1R_MILESTONES)
{
cohorts <- sort(unique(frame$cohort_year))
rows <- lapply(cohorts, function(co) {
one <- frame[frame$cohort_year == co & frame$in_horizon, , drop = FALSE]
one <- one[order(one$age_mo), , drop = FALSE]
horizon <- unique(frame$horizon_mo[frame$cohort_year == co])[1]
lapply(milestones, function(m) {
at <- one[one$age_mo <= m, , drop = FALSE]
state <- if (m > horizon)
"past_horizon"
else if (!nrow(at) || !any(at$observed_line))
"no_read"
else "reached"
last <- if (nrow(at))
at[nrow(at), , drop = FALSE]
else NULL
ok <- identical(state, "reached")
data.frame(cohort_year = as.integer(co), milestone = m, state = state,
read_age_mo = if (ok)
last$age_mo
else NA, lower = if (ok)
last$lower
else NA, upper = if (ok)
last$upper
else NA, line = if (ok)
last$line
else NA, n_planted = one$n_planted[1], n_roster = one$n_roster[1],
horizon_mo = horizon, stringsAsFactors = FALSE)
})
})
out <- do.call(rbind, unlist(rows, recursive = FALSE))
rownames(out) <- NULL
out
}
.rst_survival_series
apps/hub/R/fig_restoration.R
.rst_survival_series <- function (pool, cohort = NULL)
{
if (isTRUE(pool$mixture) && is.null(cohort))
stop(paste0(".rst_survival_series() was handed a pool spanning ",
paste(pool$cohorts_spanned, collapse = ", "), " with no cohort. ",
.rst_cfg()$pool$mixture_rule), call. = FALSE)
co <- as.character(cohort %||% pool$cohorts_spanned[1])
st <- pool$state[as.character(pool$state$cohort_year) == co, , drop = FALSE]
ros <- pool$roster[[co]]
planted <- nrow(st)
n_ros <- length(ros)
reads <- pool$plant_reads[pool$plant_reads$plant_key %in% ros, , drop = FALSE]
read_ages <- sort(unique(reads$age_mo[is.finite(reads$age_mo)]))
if (!length(read_ages))
stop(sprintf("Cohort %s has a non-empty roster but no finite read ages.",
co), call. = FALSE)
ages <- sort(unique(c(0, read_ages)))
first_read_mo <- min(read_ages)
horizon <- pool$horizon_months[[co]]
out <- do.call(rbind, lapply(ages, function(a) {
known_dead <- sum(st$first_dead_mo <= a)
followed <- st$ever_read & (st$first_dead_mo <= a | (!is.na(st$last_read_mo) &
st$last_read_mo >= a))
known_alive <- sum(followed & st$first_dead_mo > a)
data.frame(cohort_year = as.integer(co), age_mo = a, n_planted = planted,
n_roster = n_ros, n_followed = sum(followed), known_alive = known_alive,
known_dead = known_dead, line = known_alive/n_ros, lower = known_alive/planted,
upper = (planted - known_dead)/planted, stringsAsFactors = FALSE)
}))
out$in_horizon <- out$age_mo <= horizon
out$first_read_mo <- first_read_mo
out$observed_line <- out$age_mo %in% read_ages
bad <- out$in_horizon & out$n_followed != n_ros
if (any(bad))
stop(sprintf(paste0("Cohort %s: %d age(s) at or below the horizon where the followed set ",
"(%s) is not the whole roster (%d). The horizon is meant to be exactly ",
"the age at which that stops being true."), co, sum(bad), paste(unique(out$n_followed[bad]),
collapse = "/"), n_ros), call. = FALSE)
attr(out, "horizon") <- horizon
out
}
rst_pool
apps/hub/R/fig_restoration.R
rst_pool <- function (loaded, cohorts = NULL, units = NULL, plots = NULL, taxa = NULL)
{
cfg <- .rst_cfg()
st <- .rst_plant_state(loaded)
plots_all <- .rst_plots(loaded)
preads <- .rst_plot_reads(loaded)
reads <- .rst_plant_reads(loaded)
bits <- character(0)
if (!is.null(cohorts)) {
st <- st[st$cohort_year %in% cohorts, , drop = FALSE]
bits <- c(bits, sprintf("the %s planting%s", paste(sort(unique(cohorts)),
collapse = " and "), if (length(unique(cohorts)) == 1L) "" else "s"))
}
if (!is.null(units)) {
st <- st[st$unit_code %in% units, , drop = FALSE]
bits <- c(bits, paste(vapply(units, function(u) cfg$site_labels[[u]] %||%
u, character(1)), collapse = " and "))
}
if (!is.null(plots)) {
st <- st[st$plot_id %in% plots, , drop = FALSE]
bits <- c(bits, paste(plots, collapse = " and "))
}
if (!is.null(taxa)) {
st <- st[st$taxon_code %in% taxa, , drop = FALSE]
bits <- c(bits, paste(taxa, collapse = " and "))
}
keep_plots <- unique(st$plot_id)
preads <- preads[preads$plot_id %in% keep_plots, , drop = FALSE]
reads <- reads[reads$plant_key %in% st$plant_key, , drop = FALSE]
cohorts_spanned <- sort(unique(st$cohort_year))
roster <- lapply(cohorts_spanned, function(co) st$plant_key[st$cohort_year ==
co & st$ever_read])
names(roster) <- as.character(cohorts_spanned)
horizon <- numeric(0)
binder <- list()
for (co in as.character(cohorts_spanned)) {
s <- st[st$plant_key %in% roster[[co]], , drop = FALSE]
un <- s[!is.finite(s$first_dead_mo), , drop = FALSE]
if (nrow(un)) {
h <- min(un$last_read_mo)
b <- un[un$last_read_mo == h, , drop = FALSE]
b <- b[.order_by(b$plant_tag), , drop = FALSE][1, ]
pl <- plots_all[plots_all$plot_id == b$plot_id, , drop = FALSE]
binder[[co]] <- list(plant_key = b$plant_key, plant_tag = b$plant_tag,
plot_id = b$plot_id, n_tied = sum(un$last_read_mo == h),
last_read_on = b$last_read_on, last_status = b$last_status,
plot_state = pl$plot_state[1], retired_reason = pl$retired_reason[1],
why = if (nzchar(trimws(pl$retired_reason[1] %||% ""))) pl$retired_reason[1] else "the record simply ends here")
}
else {
h <- max(s$last_read_mo)
binder[[co]] <- list(plant_key = NA, plant_tag = NA, plot_id = NA,
n_tied = 0L, last_read_on = as.Date(NA), last_status = NA,
plot_state = NA, retired_reason = NA, why = "nothing binds it: every plant of this planting is resolved dead")
}
horizon[[co]] <- h
}
label <- if (!length(bits))
"all plots"
else paste(bits, collapse = ", ")
mixture <- length(cohorts_spanned) > 1L
list(state = st, plants = st, plot_reads = preads, plant_reads = reads,
plots = plots_all[plots_all$plot_id %in% keep_plots, , drop = FALSE],
label = label, label_slug = gsub("(^-|-$)", "", gsub("[^a-z0-9]+",
"-", tolower(label))), n_planted = nrow(st), n_plots = length(keep_plots),
n_plots_read = length(unique(preads$plot_id)), n_plot_reads = nrow(preads),
n_plant_reads = nrow(reads), n_duplicates_removed = length(.rst_dup_keys(loaded)),
roster = roster, roster_n = vapply(roster, length, integer(1)), horizon_months = horizon,
horizon_binder = binder, never_read_plants = st$plant_key[!st$ever_read],
never_read_plots = sort(unique(st$plot_id[st$plot_never_read])),
not_found_plants = st$plant_key[st$not_found], revivals = st$plant_key[st$revival],
cohorts_spanned = cohorts_spanned, mixture = mixture, cohort_nested_in_plot = all(vapply(split(st$cohort_year,
st$plot_id), function(x) length(unique(x)) == 1L, logical(1))))
}
rst_arm_at
apps/hub/R/fig_restoration.R
rst_arm_at <- function (state, age_mo)
{
arm <- rep("STOPPED", nrow(state))
followed <- state$ever_read & (state$first_dead_mo <= age_mo | (!is.na(state$last_read_mo) &
state$last_read_mo >= age_mo))
arm[state$not_found] <- "NOT_FOUND"
arm[followed] <- "FOLLOWED"
arm[!state$ever_read] <- "NEVER_READ"
arm
}
Read-only excerpts of this build's calculation code (hash ffa57b6e8a59). No record appears in them, and this page displays them without running them.