ctreeMI 1.1.0

New: discover-then-confirm workflow

Three functions implement the workflow recommended in the documentation of ctree_stacked(): treat the tree as discovery, and test its partition on independent data with a procedure that has valid error control.

Each split is reported with the pooled difference between its children and a confidence interval, alongside the test. The package argues that node-level p-values should not be read as error rates; it would be inconsistent to report only p-values here.

The confirmation test addresses two problems at once: the selection effect of testing subgroups on the data used to find them, and the miscalibration of the stacked node-level test under outcome-conditioned imputation.

Documentation now renders as written

The roxygen comments throughout the package use markdown syntax for emphasis, code, cross-references and section headings, but markdown processing had never been enabled, so these rendered literally on CRAN. It is now enabled. A percent sign in ?ctree_stacked that Rd read as a comment marker, silently truncating a sentence in the 1.0.1 manual, is now escaped.

Corrected package-level documentation

?ctreeMI described the correction as dividing the significance threshold by M. That is threshold rescaling, which versions 0.1.0 and 0.2.0 implemented and which under-corrects by an order of magnitude at M = 30; the package has applied the correction to the statistic since 0.3.0, and ?ctree_stacked says so. The package-level page now describes the mechanism correctly, no longer characterizes the node-level test as conservative, and points to the confirmation workflow.

ctreeMI 1.0.1

Documentation only. No function has changed and results are identical to 1.0.0.

Corrected description of node-level calibration

?ctree_stacked reported sub-nominal, and therefore conservative, type-I error under MCAR. That holds under the marginal imputation model used in the simulations of Sherlock et al. (2026), which conditioned on neither the outcome nor the remaining predictors. It does not hold under outcome-conditioned imputation, which is recommended practice and is what mice() does by default when the outcome is present in the data frame. In that setting the node-level test rejects more often than alpha implies, increasingly so as the missingness rate rises.

The documentation now states this, notes that omitting the outcome from the imputation model reverses the direction rather than restoring calibration, and points to node_table() and to refitting on independent imputations as the checks to use in place of node-level p-values.

The package DESCRIPTION previously described the result as “a conservative but interpretable single tree”. The word “conservative” has been removed for the same reason.

Added a scope section

?ctree_stacked now states that the procedure assumes missingness at random, and that recovery degrades under MNAR for every imputation-based approach.

Supporting simulations are archived at https://doi.org/10.5281/zenodo.21939940.

ctreeMI 1.0.0

0.3.0 introduced the right correction. This release makes it survive contact with real stacked data, and derives the degrees of freedom instead of relying on a p-value that has usually underflowed by the time it is needed. Results will differ from 0.3.0.

ctree_stacked() no longer aborts on large stacked datasets

partykit stores node-level p-values on the natural scale, and they underflow to exactly 0 once the statistic passes roughly 1400 at df = 2. On stacked data that is the normal case, not an edge case: 3,000 rows with M = 30 already produces a root statistic above 5,000. 0.3.0 recovered the degrees of freedom by inverting the stored p-value and, failing that, stopped with

could not recover degrees of freedom at node 1 (statistic = 5187.76,
p = 0). Refusing to prune on unverified statistics.

so the package could not fit the kind of dataset it was written for. 1.0.0 recovers df where the stored p-value is interior and falls back on the structural rule (below) where it is not, and computes the corrected p-value as -expm1(k * pchisq(stat, df, lower.tail = TRUE, log.p = TRUE)), which stays accurate to about 1e-300 rather than collapsing to 0.

Degrees of freedom are derived per node and per candidate variable

With teststat = "quadratic" the node-level statistic is chi-square with df equal to the rank of the covariance matrix of the linear statistic:

0.3.0 recovered a single continuous df for the variable that was split on. 1.0.0 recovers an integer df for every candidate variable at every node and cross-checks it against the structural rule. df = 2 (or any number) holds it fixed, and df = "outcome" uses the outcome dimension throughout.

All candidate variables are tested, not only the one split on

ctree splits a node when any candidate variable meets alpha, and that is the rule the correction must reapply. Rescaling can reorder the candidates, because they carry different degrees of freedom, so the variable with the smallest p-value before the correction is not always the smallest after. node_stats gains p_min and variable_min recording the decisive candidate, and the full per-candidate table is attached as attr(node_stats, "candidates").

Pruning is bottom-up

An internal node is now collapsed only once all of its own internal descendants have been collapsed. A node whose descendant survives the correction keeps its split even if it would fail on its own. 0.3.0 deleted every failing node outright, taking any surviving descendants with it, which is more aggressive than the procedure used for the paper. With df held fixed, prune_stackM() now reproduces that procedure exactly; this is asserted in the test suite for M = 2, 5, 10 and 30.

prune_stackM() handles a default ctree() fit

ctree_stacked() grows with testtype = "Univariate" so that the stored p-values are raw, but prune_stackM() is documented as accepting any tree fitted on stacked data, and partykit’s default is testtype = "Bonferroni", where the stored p-values already carry the multiplicity adjustment. Inverting those as if they were raw returned a plausible but wrong df (2.32 instead of 2; 12.05 instead of 8 in testing) and the internal verification could not detect it, because it re-solved the same equation it had just inverted. 1.0.0 reads the multiplicity setting from the fitted control and inverts the matching relationship, and check_stackM_extraction() now exercises both settings and checks the recovered df against the structural rule rather than against itself.

Other changes

ctreeMI 0.3.0

Correctness fix – please refit any tree produced by 0.1.0 or 0.2.0

Versions 0.1.0 and 0.2.0 did not implement the Stack/M correction of Sherlock et al. (2026). They divided the significance threshold by M and passed alpha / M to ctree_control(). The published and validated method divides the node-level chi-square statistic by M and recomputes the p-value.

These are not equivalent. Writing q(p, df) for the chi-square quantile function, the statistic rule rejects when X > M * q(1 - alpha, df) while the threshold rule rejects when X > q(1 - alpha / M, df). They coincide only at M = 1. At df = 1 and alpha = 0.05:

M statistic rule threshold rule
5 X > 19.2 X > 6.63
10 X > 38.4 X > 7.88
30 X > 115.2 X > 9.88
50 X > 192.1 X > 10.83

Threshold rescaling therefore under-corrects severely, and trees fitted with 0.1.0-0.2.0 contain more splits than the published method supports. Refit any such tree with 0.3.0 and re-check any reported subgroups.

Changes

ctreeMI 0.2.0

ctreeMI 0.1.0