MuTATE (Multi-Target Automated Tree Engine) recursively partitions a
dataset on binary feature splits, chosen to jointly separate
multiple outcome variables at once, rather than a
single target the way a standard decision tree (e.g. rpart)
does. This is useful when you want one interpretable tree that explains,
say, response, tumor size, adverse-event burden, and survival
simultaneously - for example when characterizing candidate patient
subtypes across several clinically relevant endpoints at once.
At each node, MuTATE evaluates every candidate feature split against
every outcome, standardizes the information gain across outcome types,
and picks the split that performs best in aggregate (governed by the
evalmethod argument - see ?MTPart).
MuTATE supports four outcome types, set via
outcome_defs:
| Code | Outcome type | Example |
|---|---|---|
"Cat" |
Categorical | treatment response |
"Cont" |
Continuous | tumor size |
"Count" |
Count / event rate | adverse event count |
"Surv" |
Time-to-event (survival) | overall survival |
library(MuTATE)
data(mutate_example)
head(mutate_example)
#> age sex biomarker response tumor_size ae_count time status
#> 1 74 M 4.990758 non_responder 28.01214 3 13.07537131 1
#> 2 54 M 6.520484 responder 33.37856 2 22.34313266 0
#> 3 64 M 5.077982 non_responder 37.90123 3 26.00931724 1
#> 4 66 F 6.470144 non_responder 36.68455 1 4.60484425 0
#> 5 64 M 4.707055 non_responder 24.71583 2 54.52374708 0
#> 6 59 M 4.884225 responder 42.51256 3 0.02140851 1
#> OS_definition_time_status
#> 1 1
#> 2 0
#> 3 1
#> 4 0
#> 5 0
#> 6 1mutate_example is a small simulated dataset shipped with
the package (see ?mutate_example) with three features
(age, sex, biomarker) and four
outcomes, one of each supported type.
Before fitting a tree, note one MuTATE-specific quirk: for a
"Surv" outcome, the name you pass in outcomes
must itself be a column in data, and its 3rd and 4th
underscore-separated tokens must exactly match the names of the actual
time and event columns. In mutate_example, the time column
is time, the event column is status, and the
outcome name that encodes both is
OS_definition_time_status:
features <- c("age", "sex", "biomarker")
outcomes <- c("response", "tumor_size", "ae_count", "OS_definition_time_status")
outcome_defs <- c("Cat", "Cont", "Count", "Surv")
tree <- MTPart(
features, outcomes, outcome_defs, mutate_example,
depth = 2, # maximum tree depth
nodesize = 30 # minimum observations per node before it can be split
)MTPart() returns a list with two elements:
partitions: a data frame describing the parent/child
relationships between nodes.tree_nodes: a list with one entry per node, holding the
split rule, sample size, and per-outcome summary statistics for that
node.MTPartSummary() collapses the tree into a table of one
row per split depth, reporting the complexity parameter (CP),
average/total relative error, and a cross-validated error estimate at
each depth - the multi-target analogue of an rpart
complexity table:
summ <- MTPartSummary(tree)
summ$summary_table
#> nsplit leaves CP AvgRelError TotRelError Xerror Xstd Eval
#> 1 0 1 0.5042458 1.0000000 1.0000000 1 0 1
#> 2 1 2 0.5042458 0.4957542 0.9915085 1 0 1MTSummary() computes the same per-outcome summary
statistics used inside MTPart(), callable directly on any
outcome/data combination - useful for inspecting a single node’s target
distributions:
targets <- list(Definitions = outcome_defs, Z = mutate_example[, outcomes])
MTSummary(targets, mutate_example)$response
#> $absent
#> [1] 0
#>
#> $non_responder_count
#> [1] 104
#>
#> $non_responder_prop
#> [1] 0.52
#>
#> $responder_count
#> [1] 96
#>
#> $responder_prop
#> [1] 0.48
#>
#> $predicted
#> [1] "non_responder"
#>
#> $expectedloss
#> [1] 0.48MTPrune() performs cost-complexity pruning, dropping
splits whose CP falls below a chosen threshold:
MTTest() applies a fitted tree’s split rules to new data
and recomputes the per-node outcome summaries and error metrics on that
new sample - the multi-target analogue of predict():
CV_Tune() performs a grid search with k-fold
cross-validation over depth, nodesize,
evalmethod, and related tuning parameters. Because it fits
one MuTATE tree per fold per grid point, even a small grid can take a
while - the example below uses a minimal single-point grid purely to
illustrate the call:
cv_results <- CV_Tune(
features, outcomes, outcome_defs, mutate_example,
kfolds = 2, Y = "response",
drange = 2, noderange = 30, splitmin_div = 2,
method = "avgIG", alpharange = 0.05, igrange = 0.95,
psplitrange = 1, pdepthrange = 1, cp_val = 0
)For a real tuning run, widen drange,
noderange, and method to search a meaningful
range - see ?CV_Tune for the full parameter list.