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 "depth-sequential": 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 "depth-sequential" mode itself, distinct from the switching corollary controlled by the switching argument below. "remaining" is accepted as a deprecated alias for "depth-sequential" and emits a warning.

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 = "depth-sequential" (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 "depth-sequential" 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