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:
strsplit("OS_definition_time_status", "_")[[1]]
#> [1] "OS" "definition" "time" "status"
# [3] "time" -> must match the time column name
# [4] "status" -> must match the event column namefeatures <- 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.tree$partitions
#> parent child
#> 1 <NA> 1
#> NA 1 2 *
#> 3 1 3 *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:
pruned <- MTPrune(tree, cp = 0.02)
pruned$partitions
#> parent child
#> NA 1 2 *
#> 3 1 3 *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():
test_result <- MTTest(tree, features, outcomes, outcome_defs, mutate_example)PlotTree(tree)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.