Skip to contents

Factory function that creates an adaptive alpha adjustment system supporting branch pruning. Unlike alpha_adaptive_tree, which pre-computes a fixed schedule, this version can recompute the schedule on a pruned subtree after each depth — giving more alpha to surviving branches when dead branches are removed.

Usage

alpha_adaptive_tree_pruned(
  node_dat,
  delta_hat,
  max_depth = NULL,
  budget_weights = NULL,
  budget_total = 1,
  spending_fraction = 0.5,
  switching = FALSE
)

Arguments

node_dat

A data.frame or data.table with columns nodenum, parent, depth, and nodesize. Typically extracted from a find_blocks result. The root node must have parent = 0 and depth = 1.

delta_hat

Estimated standardized effect size (e.g., Cohen's d). Conservative (larger) values produce more stringent adjustment, which preserves the FWER guarantee.

max_depth

Maximum depth to compute. Defaults to the maximum depth present in node_dat.

budget_weights

Controls depth-wise budget allocation. Accepts the same values as compute_adaptive_alphas_tree, plus "remaining": a sequential spending process where a fixed fraction spending_fraction of the remaining budget is spent at each depth. The spending fraction is set in advance, so the resulting weights \(w_\ell\) depend only on the testing history through depth \(\ell - 1\) — they are predictable in the sense required by the budget-weighted FWER theorem with predictable denominators (Theorem B.5 in the supplement). This is the theoretical justification for the "remaining" mode itself, distinct from the switching corollary controlled by the switching argument below.

budget_total

Initial error budget (default 1.0). The constraint \(\sum w_\ell \le\) budget_total guarantees FWER control.

spending_fraction

Fraction of remaining budget to spend at each depth when budget_weights = "remaining" (default 0.5). At depth \(\ell\), the weight is \(w_\ell = f \times B_\ell\) where \(f\) is the spending fraction and \(B_\ell\) is the remaining budget.

switching

Logical (default FALSE). When TRUE, implements the switching corollary: after each update, if the remaining pruned error load fits within the remaining budget, all deeper depths revert to nominal alpha. This is a separate FWER guarantee from the predictable-weights mechanism that justifies "remaining" mode.

Value

A list with three components:

alphafn

A function with the standard alphafn interface: function(pval, batch, nodesize, thealpha, thew0, depth). Looks up the current alpha schedule by depth.

update

A function function(pruned_node_dat, thealpha) that recomputes the alpha schedule on the given (pruned) tree. Called by find_blocks after each depth's testable decisions.

reset

A function function(thealpha) that restores the alpha schedule to the full (unpruned) tree. Called by find_blocks at the start of each run to ensure independence across simulation iterations.

Details

The FWER guarantee follows from Theorem B.5 in the supplement: predictable budget weights with data-dependent denominators. The weights are "predictable" because \(w_\ell\) depends only on the testing history through depth \(\ell - 1\), not on depth-\(\ell\) outcomes. The union bound across depths gives FWER \(\le \alpha\) whenever \(\sum w_\ell \le 1\).

The switching corollary (when switching = TRUE): after pruning narrows the surviving tree, if the remaining error load \(\sum_{\ell \ge s} D_\ell \le B_s\) (remaining budget), then \(\alpha_\ell = \alpha\) for all \(\ell \ge s\). This works by setting \(w_\ell = D_\ell\), so the \(D_\ell\) in numerator and denominator cancel, leaving nominal alpha.

When find_blocks detects a list-valued alphafn, it extracts these three components and calls reset at the start of each run and update after each depth.

Examples

nd <- data.frame(
  nodenum  = 1:7,
  parent   = c(0, 1, 1, 2, 2, 3, 3),
  depth    = c(1, 2, 2, 3, 3, 3, 3),
  nodesize = c(500, 250, 250, 125, 125, 100, 150)
)
obj <- alpha_adaptive_tree_pruned(node_dat = nd, delta_hat = 0.5)
# obj$alphafn -- pass to find_blocks
# obj$update(pruned_nd, 0.05) -- recompute on surviving tree
# obj$reset(0.05) -- restore full-tree schedule