These generics expose the pieces tidypredict_fit() is assembled from,
rather than the finished formula. They exist so that packages generating
their own code from a fitted model, such as orbital, can reuse
tidypredict's parsing instead of reimplementing it per model class.
Each generic has a single fixed return shape, described below. A model class
implements whichever generics make sense for it: a random forest has trees
and a tree count, a glmnet multinomial model has neither.
Usage
tidypredict_trees(x, ...)
tidypredict_class_trees(x, ...)
tidypredict_class_exprs(x, ...)
tidypredict_n_trees(x, ...)Value
tidypredict_trees() returns an unnamed list with one element per tree,
each a language object.
tidypredict_class_trees() returns a list named by outcome level, in model
order. Each element is itself an unnamed list of per-tree language objects
for that level, so the result is tidypredict_trees() nested one level
deeper. What the leaves hold depends on the model: randomForest gives
0/1 votes, ranger gives class probabilities.
tidypredict_class_exprs() returns a list named by outcome level, in model
order, with one language object per level. Unlike
tidypredict_class_trees() there is no per-tree structure and nothing to
combine: each expression computes that level's value on its own.
tidypredict_n_trees() returns a single integer, the number of trees in the
ensemble. For multiclass boosters this counts every tree, including the
per-class copies, so it is not the same as the number of boosting rounds.
Wherever an expression is described above, a bare numeric value may
appear in its place when the model has nothing to branch on. Callers must
handle both. This happens for a single-leaf tree, a stump, and also for a
degenerate expression such as a glmnet class whose coefficients are all
zero. Note that the constant can appear alongside language objects in the
same result, so the element type is not uniform within one list.
Details
Two shapes that look similar are worth keeping apart.
tidypredict_class_trees() returns many trees per level that a caller has
to sum or average, and needs tidypredict_n_trees() to do it.
tidypredict_class_exprs() returns one finished expression per level. Both
are named by outcome level so that callers never have to assume the order
matches levels() of the outcome.
Which models implement which generic
. means the generic is not implemented for that class and will error.
trees class_trees class_exprs n_trees combine
blackboost x . . x x
C5.0 . . . . x
catboost.Model x . . x x
cforest x . . x x
earth . . x . .
lgb.Booster x . . x x
multnet . . x . .
ObliqueForest x . . x x
party . . x . .
randomForest x x . x x
ranger x x . x x
rpart . . x . .
xgb.Booster x . . x xC50::C5.0() is the one row with a tidypredict_combine_trees() method and
no tidypredict_trees(). That method exists only to refuse, with an
explanation, rather than to let the caller reach the .default error and
guess why.
Implementing these for a new model class
The table above shows the grouping to follow. tidypredict_trees(),
tidypredict_n_trees() and tidypredict_combine_trees() are a set:
implement all three or none. Per-tree expressions are not usable without a
count to size them and a rule to recombine them, and shipping the first
without the third invites a caller to sum the trees, which is wrong for
every backend that carries an offset, a scale or a link.
A useful check on a new method is that
tidypredict_combine_trees(x, tidypredict_trees(x)) computes the same
values as tidypredict_fit(x). That identity is what the tests for the
existing methods assert, and it catches a combination rule that was assumed
rather than read out of the model.
If a model's trees genuinely cannot be recombined arithmetically, give it a
tidypredict_combine_trees() method that refuses and no
tidypredict_trees() method, as C50::C5.0() does. Splitting trees apart
that cannot be put back together only enables a wrong answer.
See also
tidypredict_combine_trees() for turning per-tree expressions back
into a prediction, and tidypredict_metadata for what the resulting
values mean.
Examples
model <- randomForest::randomForest(mpg ~ ., data = mtcars, ntree = 5)
tidypredict_n_trees(model)
#> [1] 5
trees <- tidypredict_trees(model)
length(trees)
#> [1] 5
trees[[1]]
#> case_when(wt <= 2.3325 ~ case_when(hp <= 65.5 ~ 33.9, .default = case_when(disp <=
#> 78.85 ~ 32.4, .default = 29.78)), .default = case_when(cyl <=
#> 7 ~ case_when(hp <= 116.5 ~ case_when(gear <= 3.5 ~ 19.8,
#> .default = case_when(drat <= 4.005 ~ 21, .default = 21.4)),
#> .default = 18.9), .default = case_when(carb <= 3.5 ~ 17.26,
#> .default = case_when(hp <= 217.5 ~ 10.4, .default = 14.96))))
