Learn / Inside the pipeline / 05 · baseline

The early-window baseline: what 70% is 70% of

This is the pivot of the entire workflow. For each accession: collapse to the maximum germination per year, take the earliest tested year, open a window of [earliest, earliest + 5] years, and the baseline is the highest germination observed anywhere in that window (tie goes to the earlier year). The threshold — the "P70" everything downstream compares against — is 0.70 × that baseline.

Why a window and not just the first test? Because first tests under-read: dormancy hasn't been fully cracked, protocols get refined, and germination often peaks a year or two into storage. Taking the early-window maximum reads the accession at its demonstrated best, so the threshold measures decline from what the seeds could actually do — not from an unlucky first assay.

One guard: an accession with exactly two test years, the earliest at year 0 or 1, forces that earliest test as the baseline. Otherwise a lone later test could become its own reference and the accession could never register decline.

The window width is an assumption — so test it. Rerun the species intervals at 3, 5, and 7-year windows and flag taxa whose interval changes. In the dataset behind the catalog, the 5-year window is stable for roughly 96% of taxa; the flagged remainder are exactly the ones to interpret carefully.

Build it yourself
library(dplyr)

early_baseline <- function(df, window_years = 5) {
  df %>%
    filter(!is.na(YearsDifference), !is.na(PctGermin)) %>%
    group_by(Family, TaxonName, AcquisitionNum) %>%
    group_modify(~{
      d <- .x %>%
        group_by(YearsDifference) %>%
        summarise(max_germ = max(PctGermin), .groups = "drop") %>%
        arrange(YearsDifference)
      yrs <- d$YearsDifference
      # guard: exactly two test years starting at 0/1 -> force the earliest
      if (length(yrs) == 2 && min(yrs) <= 1) {
        pick <- d %>% filter(YearsDifference == min(yrs))
      } else {
        pick <- d %>%
          filter(YearsDifference <= min(yrs) + window_years) %>%
          arrange(desc(max_germ), YearsDifference) %>% slice(1)
      }
      tibble::tibble(baseline_year = pick$YearsDifference,
                     baseline_viab = pick$max_germ,
                     threshold     = 0.7 * pick$max_germ)
    }) %>% ungroup()
}
The math behind this step: The baseline question · Three different P70s
Many tests, one year: max or poolThe observed interval: no model, just the latest year that cleared the bar