View source: R/ctree_stacked.R
| ctree_stacked | R Documentation |
Fits a conditional inference tree (ctree) on stacked multiply imputed datasets using the Stack / M rescaling procedure described in Sherlock et al. (2026). Multiply imputed datasets are concatenated vertically ("stacked") and one tree is grown on the combined data. Each node-level chi-square statistic is then divided by the number of imputations (M) to counteract the artificially inflated sample size, the node-level p-values are recomputed, and the tree is compressed bottom-up. This yields a single, coherent, interpretable tree that incorporates imputation variability without requiring the pooling of structurally different trees.
ctree_stacked(
formula,
data,
m = NULL,
alpha = 0.05,
scale_minsize = TRUE,
verbose = TRUE,
...
)
formula |
A model formula, passed to |
data |
A |
m |
Integer. Number of imputations to use. If |
alpha |
Numeric. The nominal significance threshold for node-level
splitting (default 0.05). It is applied to p-values recomputed from
node statistics that have been divided by |
scale_minsize |
Logical. If |
verbose |
Logical. If |
... |
Additional arguments passed to |
When data contain missing values, a common and principled approach is multiple imputation (Rubin, 1987), wherein M completed datasets are generated by drawing from the posterior predictive distribution of the missing values. For most statistical models, results from M datasets are pooled using Rubin's rules.
Conditional inference trees (ctree; Hothorn, Hornik & Zeileis, 2006) are not straightforward to pool across imputations because the tree structure itself – which variables are split, at what values, and in what order – can differ across imputations. Pooling structurally different trees produces inconsistent subgroup definitions and uninterpretable results.
Rodgers et al. (2021) proposed stacking the M imputed datasets vertically and fitting a single tree to the combined data. This circumvents the structural-mismatch problem and produces one interpretable tree. However, stacking inflates the nominal sample size by a factor of M, causing node-level test statistics to be similarly inflated and the tree to split more aggressively than warranted.
Sherlock et al. (2026) proposed and validated the Stack / M
correction: each node-level test statistic computed on the stacked data
is divided by M, the p-value is recomputed from the rescaled statistic,
the Bonferroni adjustment for the number of candidate splitting
variables is reapplied, and nodes that no longer meet alpha are
pruned. See "Scope" and "Node-level calibration" below for the
conditions under which the procedure has been examined and for the
behavior of its node-level test.
Rescaling the statistic is not equivalent to dividing the
significance threshold by M. Writing q(p, df) for the chi-square
quantile function, the two rules are:
statistic rescaling (correct): reject when
X > M * q(1 - alpha, df)
threshold rescaling (incorrect): reject when
X > q(1 - alpha / M, df)
These coincide only at M = 1. At df = 1, alpha = 0.05, M = 30
the first requires X > 115.2 and the second only X > 9.9, so
threshold rescaling under-corrects by an order of magnitude and grows
substantially larger trees than the published method.
Versions 0.1.0 and 0.2.0 of this package implemented threshold rescaling. Trees fitted with those versions are under-corrected and should be refitted.
The problem it solves is specific. Multiple imputation produces M completed datasets, and for most models their results are combined by Rubin's rules; a tree offers no estimand that can be averaged, since the M trees may split on different variables in a different order. Stacking avoids that, and the correction addresses the inflated sample size that stacking introduces. If a single interpretable tree is wanted from multiply imputed data, that is what this package is for.
In the simulations archived at the DOI given below, the procedure recovered known structure more often than listwise deletion, surrogate splits, missingness incorporated in attributes, or single imputation, and produced the most stable partitions across independent sets of imputations. Its accuracy held across missingness rates of 15% to 45% while each of those four declined, listwise deletion most steeply. Tree sizes tracked the true size closely wherever real structure was present, and were stable from M = 5 to M = 50.
Two settings matter in practice. The outcome should be included in the
imputation model, which mice() does by default: omitting it attenuates
the associations a tree is meant to find, at a cost described under
"Node-level calibration" below. And M should follow the usual guidance
for the fraction of missing information; M = 30 was used throughout
these simulations.
The procedure suits least the exploratory case in which no structure may be present, since the miscalibration described below is concentrated under a true null. A single unreplicated subgroup found in data with no prior reason to expect one warrants the checks given below rather than the node-level p-value.
The procedure assumes the data are missing at random in the sense of Rubin (1976), as does the multiple imputation it is built on. In the simulations archived at the DOI given below it was examined under MCAR and MAR across a range of missingness rates, sample sizes and outcome types, and it is intended for use where missingness is plausibly at random. Under MNAR, recovery of the true structure degrades for every imputation-based approach, this one included, and a tree fitted in that setting should be treated as provisional. The mechanism is rarely known in applied work, and MNAR cannot be distinguished from MAR using the observed data, so this is a statement about where the assumption is defensible rather than a condition that can be checked.
The node-level test is not calibrated to its nominal level when the
imputation model conditions on the outcome. That is recommended
practice, and it is what mice() does by default when run on a data
frame containing the outcome. Under a true null the test rejects more
often than alpha implies, increasingly so as the missingness rate
rises. Omitting the outcome from the imputation model reverses this
into conservatism rather than restoring calibration, and is not
recommended, since it attenuates the associations a tree is meant to
detect.
Node-level p-values should therefore be read as approximate rather than
nominal, and should not be reported as error rates. The recommended
remedy is to treat the tree as discovery and test its partition on
independent data with confirm_ctreeMI(), which pools across
imputations by Rubin's rules and so has valid error control;
discover_confirm() performs the whole sequence. Two further checks
help in interpreting a fitted tree. node_table() reports the
effective sample size behind each terminal node, in original rather
than stacked observations, so a node resting on few original cases can
be identified. And refitting on an independent set of imputations, then
comparing the resulting partitions, indicates whether the structure is
stable.
Simulations characterizing this behavior are archived at \Sexpr[results=rd]{tools:::Rd_expr_doi("10.5281/zenodo.21939940")}.
Documentation prior to version 1.0.1 described the correction as sub-nominal under MCAR. That characterization comes from the simulations in Sherlock et al. (2026), which used a marginal imputation model conditioning on neither the outcome nor the remaining predictors. It does not hold under outcome-conditioned imputation.
micelibrary(mice) imp <- mice(my_data, m = 30, printFlag = FALSE) fit <- ctree_stacked(outcome ~ ., data = imp, alpha = 0.05) print(fit) plot(fit)
imp_list <- lapply(1:30, function(i) complete(imp, i)) fit <- ctree_stacked(outcome ~ ., data = imp_list, alpha = 0.05)
An object of class c("ctreeMI", "constparty", "party"). This
inherits all methods from partykit::ctree() output and additionally
carries a ctreeMI_info attribute with the following elements:
mNumber of imputations used.
n_originalNumber of rows in a single imputed dataset.
n_stackedTotal rows in the stacked dataset.
alphaThe significance threshold applied to the rescaled p-values.
correctionCharacter, "statistic/M".
node_statsData frame of per-node raw statistics, degrees of freedom, rescaled statistics, p-values, and retention.
n_splits_before, n_splits_afterSplits before and after the correction was applied.
outcome_dimRank of the influence function of the response, i.e. the degrees of freedom carried by a numeric or ordered predictor.
minsplit, minbucketMinimum node sizes actually used, in stacked rows.
formulaThe model formula.
callThe matched call.
Sherlock, P., Mansolf, M., Hofheimer, J., Hockett, C. W., O'Connor, T. G., Roubinov, D., Graff, J. C., Lai, J.-S., Bush, N. R., Wright, R. J., & Chiu, Y.-H. M. (2026). Beyond linear risk: A machine learning approach to understanding perinatal depression in context. Multivariate Behavioral Research, 1-16. \Sexpr[results=rd]{tools:::Rd_expr_doi("10.1080/00273171.2026.2661244")}
Hothorn, T., Hornik, K., & Zeileis, A. (2006). Unbiased recursive partitioning: A conditional inference framework. Journal of Computational and Graphical Statistics, 15(3), 651-674. \Sexpr[results=rd]{tools:::Rd_expr_doi("10.1198/106186006X133933")}
Hothorn, T., & Zeileis, A. (2015). partykit: A modular toolkit for recursive partitioning in R. Journal of Machine Learning Research, 16, 3905-3909.
Rodgers, D. M., Jacobucci, R., & Grimm, K. J. (2021). A multiple imputation approach for handling missing data in classification and regression trees. Journal of Behavioral Data Science, 1(1), 127-153. \Sexpr[results=rd]{tools:::Rd_expr_doi("10.35566/jbds/v1n1/p6")}
Rubin, D. B. (1987). Multiple imputation for nonresponse in surveys. Wiley.
partykit::ctree(), partykit::ctree_control(), mice::mice(),
stack_imputations(), rescale_statistic(), prune_stackM(), node_table()
## Not run:
library(mice)
# Introduce missingness into the airquality dataset
set.seed(42)
aq <- airquality
aq$Ozone[sample(nrow(aq), 20)] <- NA
aq$Solar.R[sample(nrow(aq), 15)] <- NA
# Impute
imp <- mice(aq, m = 10, printFlag = FALSE)
# Fit ctree with Stack/M correction
fit <- ctree_stacked(Ozone ~ Solar.R + Wind + Temp + Month,
data = imp,
alpha = 0.05)
print(fit)
plot(fit)
## End(Not run)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.