Inspect a pruned mask directly
If you want programmatic access to what survived pruning – without going through JSON output – work with the mask and Mill sample directly.
Apply the mask to get a pruned Mill sample
result = explain(ds, model)
pruned = ds[result.mask] # a new, smaller Mill sample; ds itself is unchangedPruned items become missing in the returned sample's data (not removed positionally for leaf arrays; bag instances that are pruned away are removed, shrinking that bag's observation count).
Walk every maskable item
for (node, level) in collectmasks(result.mask)
println("level $level: ", count(prunemask(node)), " / ", length(node), " kept")
endcollectmasks returns (node, level) => ... pairs for every own-bearing node in the tree, in depth order (level starts at 1 at the root and increases going deeper – see the note on router nodes "consuming" a level number without contributing an entry, in docs/design/masks.md if you need the exact semantics).
Aggregate statistics without JSON
nodes = first.(collectmasks(result.mask))
n_total = sum(length, nodes)
n_kept = sum(count, prunemask.(nodes))This is exactly what ExplanationResult.n_total/.n_kept already give you (fraction_kept(result), fraction_pruned(result), n_pruned(result)) – reach for those first; this is what to fall back on if you need a custom breakdown (e.g. per-level, or restricted to one field).
Check reachability, not just the mask's own value
A mask item can be "on" in its own prunemask while still being unreachable because an ancestor (e.g. a bag instance) was pruned away. participate reports genuine reachability:
reachable_and_kept = prunemask(node) .& participate(node)This matters mainly if you're writing your own pruning/scoring logic against masks directly; ds[mask] and explain_json already account for this correctly on their own.