scripts/0297's scoring spec, fixed for CH before its first fit and applied
here to any species × stage. Each rule's in/out on every visit enters a
binomial model with a complementary log-log link beside log(area), data set,
era and the watershed group. The modelled groups go into folds folds,
stratified: groups are ranked by detections and dealt in shuffled blocks, with
a fixed seed. Each fold is scored from a fit on the other folds: the held-out
visits take that fit's rule, area, data-set and era coefficients, and each
held-out group refits only its own intercept on its own visits (one parameter
per group for every rule). A level unseen in training takes the reference and
is counted. The score is the summed binomial deviance of the held-out visits.
Rules with the same in/out on every visit are one fit.
Arguments
- d
data.frameof visits with the response columnresp(0/1),wsg(factor),log_area,data_set(factor),era(factor), and the columnsrule_inreads.- rule_in
a function
(d, rule)returning a logical ofnrow(d): whether each visit is in under one row ofgrid(seeknw_rule_in()).- grid
the rules, as
knw_tune_grid()returns them.- resp
character(1)the response column.- seed
integer(1)fold seed.- folds, n_boot, seed_boot, seeds_more, d_floor
the spec's constants.
- workers
integer(1)socket workers for the pattern fits.- extra
logical(1)run the bootstrap, nested selection and extra seeds.- fit
"fe"fits each pattern with the groups absorbed (one intercept per group found by projection, so a fit's cost grows with the nuisance columns, not the groups; #83),"dense"with one model-matrix column per group (stats::glm.fit()). They give the same coefficients and deviances to numerical tolerance;"fe"falls back to"dense"on a fold where a nuisance level is a combination of groups.- prior_sd
numeric(1)the standard deviation, on the cloglog scale, of a normal prior on every training fit'sdata_setanderacoefficients,Inffor none. The fit addsb^2 / prior_sd^2per coefficient to the deviance (a ridge), so a coefficient costs more the further it sits from zero. These terms are in the model so that a rule is not credited for a sampling difference, and with a group intercept each is measured only by the contrast between the two levels inside the groups that hold both; where every detection of those groups sits on one level the contrast is separated and the unpenalised coefficient runs until the deviance stops moving (steelhead'sdata_setto 19.6, a hazard ratio past a hundred million, 2026-10-23). At 2.5 a measured effect of two or three units (chinook's data set, bull trout spawning's) is left nearly where it is and a runaway is held to a few units. The dense fit takes none.
Value
A list: rules, the grid with n_visits, n_detect, n_wsg,
unseen_level_visits, n_wsg_varies, converged, dev_heldout,
ddev_best, se_diff, ddev_null, in_region, n_in, n_detect_in,
raw_rate_in, raw_rate_out, pick, recommended, pick_flow,
recommended_flow (NA with no flow start), and with extra
boot_region_share, boot_rec_share and boot_rec_share_flow; folds, the fold of each group;
honest, seeds, boot_recs (with extra), and population, counts.
Details
The best rule has the lowest score. A rule is in the region when its
score is within one SE of the best, the SE being sqrt(n groups) × the sd of
the per-group differences from the best (the group is the unit). The pick
is the region rule with the fewest departures from default, ties to the
lower score. If the pick's exception distance is under d_floor (where
mainstem catches snapped to a tributary mouth cannot be told apart),
recommended is the pick among region rules with exc_d >= d_floor or no
exception; otherwise it is the pick. Where the grid carries
departures_flow (the departures from link's flow version of default,
knw_tune_grid()), the same pick is made from that starting point too
(pick_flow, recommended_flow; #80), on the same region: the region does
not depend on the start, only which of its rules is nearest. With extra = TRUE the call also runs
n_boot bootstrap resamples of groups (each rule's share in the region, and
the share of resamples recommending each rule), a nested selection (the pick
chosen on the other folds' held-out deviance and scored on each fold, against
default and against the no-rule model) and the grid on seeds_more more
fold seeds (each reporting its best, pick and recommended rule, whether
default and the main reading's recommended rule are in its region, and the
region's size).
Groups where the response never or always occurs are left out (they inform no contrast) and counted.
Examples
# a small made-up population: two "rules", eight groups
set.seed(1)
d <- data.frame(wsg = factor(rep(sprintf("W%02d", 1:8), each = 30)),
log_area = log(runif(240, 20, 400)), data_set = factor("FDIS"),
era = factor("2005on"), size = runif(240, 0.05, 3))
d$U <- rbinom(240, 1, plogis(-3 + 1.5 * (d$size > 0.5)))
grid <- data.frame(rule = c("default", "tuned"), size_var = "mad", size_min = c(0.1, 0.5),
grad_max = 0.05, exc_q = NA, exc_d = NA, departures = c(0L, 1L),
is_default = c(TRUE, FALSE))
rule_in <- function(d, r) d$size >= r$size_min
s <- knw_tune_score(d, rule_in, grid, "U", seed = 1, workers = 1)
s$rules[, c("rule", "dev_heldout", "ddev_best", "se_diff", "in_region", "pick")]
#> # A tibble: 2 × 6
#> rule dev_heldout ddev_best se_diff in_region pick
#> <chr> <dbl> <dbl> <dbl> <lgl> <lgl>
#> 1 default 202. 0.513 3.74 TRUE TRUE
#> 2 tuned 202. 0 0 TRUE FALSE
