| Title: | Global Explanations for Tree-Based Models |
|---|---|
| Description: | Global explanations for tree-based models by decomposing regression or classification functions into the sum of main components and interaction components of arbitrary order. Calculates SHAP values and q-interaction SHAP for all values of q for tree-based models such as xgboost. |
| Authors: | Marvin N. Wright [aut, cre] (ORCID: <https://orcid.org/0000-0002-8542-6291>), Joseph Theo Meyer [aut], Munir Hiabu [aut], Lukas Burk [aut] (ORCID: <https://orcid.org/0000-0001-7528-3795>), Jinyang Liu [aut] (ORCID: <https://orcid.org/0009-0005-3167-9014>) |
| Maintainer: | Marvin N. Wright <[email protected]> |
| License: | GPL-3 |
| Version: | 0.7.0.9000 |
| Built: | 2026-11-12 20:11:55 UTC |
| Source: | https://github.com/PlantedML/glex |
A reduced version of the Bikeshare data as included with ISLR2.
The dataset has been converted to a data.table::data.table(), with the following changes:
bikebike
An object of class data.table (inherits from data.frame) with 8645 rows and 11 columns.
hr has been converted to a numeric
workingday was recoded to a binary factor with labels c("No Workingday", "Workingday")
season was recoded to a factor with labels c("Winter", "Spring", "Summer", "Fall")
Variables atemp, day, registered and casual were removed
Bikeshare in package ISLR2
Builds the groups list for group_components() from the original,
un-encoded data: every factor column of data whose encoded level columns
are found in object$x becomes one group. By default, level columns are
expected under the model.matrix() naming convention
(paste0(feature, levels), e.g. season with level "Winter" becomes
"seasonWinter"), which covers both one-hot (~ f - 1) and treatment
(~ f) coding – levels without a matching column (such as a dropped
reference level) are simply skipped.
dummy_groups( object, data, naming = function(feature, levels) paste0(feature, levels) )dummy_groups( object, data, naming = function(feature, levels) paste0(feature, levels) )
object |
( |
data |
( |
naming |
( |
If the data was encoded with a different scheme, pass naming: a function
of (feature, levels) returning the encoded column names, e.g.
function(feature, levels) paste(feature, levels, sep = "_"). It must
reproduce the column names the model was trained with, i.e. those found in
object$x.
Named list of encoded column names, one element per matched
factor, suitable as the groups argument of group_components().
library(xgboost) set.seed(1) n <- 200 d <- data.frame( f = factor(sample(c("a", "b", "c"), n, replace = TRUE)), x1 = rnorm(n) ) y <- c(a = 0, b = 2, c = -1)[d$f] + d$x1 + rnorm(n) x <- model.matrix(~ f + x1 - 1, d) xg <- xgboost(x, y, nrounds = 10, max_depth = 3, nthreads = 1) gl <- glex(xg, x) dummy_groups(gl, d) grouped <- group_components(gl, dummy_groups(gl, d)) names(grouped$m)library(xgboost) set.seed(1) n <- 200 d <- data.frame( f = factor(sample(c("a", "b", "c"), n, replace = TRUE)), x1 = rnorm(n) ) y <- c(a = 0, b = 2, c = -1)[d$f] + d$x1 + rnorm(n) x <- model.matrix(~ f + x1 - 1, d) xg <- xgboost(x, y, nrounds = 10, max_depth = 3, nthreads = 1) gl <- glex(xg, x) dummy_groups(gl, d) grouped <- group_components(gl, dummy_groups(gl, d)) names(grouped$m)
Global explanations for tree-based models by decomposing regression or classification functions into the sum of main components and interaction components of arbitrary order. Calculates SHAP values and q-interaction SHAP for all values of q for tree-based models such as xgboost.
glex(object, x, max_interaction = NULL, features = NULL, ...) ## Default S3 method: glex(object, ...) ## S3 method for class 'rpf' glex(object, x, max_interaction = NULL, features = NULL, ...) ## S3 method for class 'rpf_pruned' glex(object, x, max_interaction = NULL, features = NULL, ...) ## S3 method for class 'xgb.Booster' glex( object, x, max_interaction = NULL, features = NULL, max_background_sample_size = NULL, weighting_method = "fastpd", ... ) ## S3 method for class 'ranger' glex( object, x, max_interaction = NULL, features = NULL, max_background_sample_size = NULL, weighting_method = "fastpd", ... )glex(object, x, max_interaction = NULL, features = NULL, ...) ## Default S3 method: glex(object, ...) ## S3 method for class 'rpf' glex(object, x, max_interaction = NULL, features = NULL, ...) ## S3 method for class 'rpf_pruned' glex(object, x, max_interaction = NULL, features = NULL, ...) ## S3 method for class 'xgb.Booster' glex( object, x, max_interaction = NULL, features = NULL, max_background_sample_size = NULL, weighting_method = "fastpd", ... ) ## S3 method for class 'ranger' glex( object, x, max_interaction = NULL, features = NULL, max_background_sample_size = NULL, weighting_method = "fastpd", ... )
object |
Model to be explained, either of class |
x |
Data to be explained. For |
max_interaction |
( |
features |
Vector of column names in |
... |
Further arguments passed to methods. Arguments a method does not use are disregarded with a warning. |
max_background_sample_size |
The maximum number of background samples used for the FastPD algorithm, only used when |
weighting_method |
Use either "path-dependent", "fastpd" (default), or "empirical". See References for details. |
For parallel execution using xgboost models, register a backend, e.g. with
doParallel::registerDoParallel().
Random Planted Forests of class rpf_pruned. rpf() of randomPlantedForest (from version 0.6) returns by
default a model of class rpf_pruned with few components (see randomPlantedForest::prune_components()), each
stored in the form it predicts with (scaled by its coefficient). glex() of such a model returns their values at
x, which with the intercept sum to the model's prediction, and SHAP values derived from them as for the other
models. Components the model does not have are absent from m, not zero columns. The result also carries pruned
(see Value), which plot.glex(), plot_twoway_effects() and plot_threeway_effects() use to draw the model
exactly as it predicts, and which glex_vi() uses for measure = "share". A forest fitted with prune = FALSE
(class rpf) is also supported: glex() then decomposes the whole forest.
The different weighting methods are described in detail in Liu et al. (2025). The default
method is "fastpd" as it consistently estimates the correct partial dependence function.
Decomposition of the regression or classification function.
A list with elements:
shap: SHAP values, derived from the functional decomposition as
. This reconstruction is only valid if the
decomposition is complete: if it is constrained (see constrained), the components
no longer sum to the full model prediction and the SHAP efficiency property cannot
hold, so shap is a scalar NA (with a warning) while m remains valid.
For multiclass models, columns are class-specific like those of m.
m: Functional decomposition into all main and interaction
components in the model, up to the degree specified by max_interaction.
The variable names correspond to the original variable names,
with : separating interaction terms as one would specify in a formula interface.
intercept: Intercept term, the expected value of the prediction. For multiclass
models a named vector with one value per class.
constrained: Character vector naming the arguments that constrained the
decomposition ("max_interaction", "features"), or character(0) if it is
complete. Use length(x$constrained) > 0 to check whether shap is valid.
A constraint that only drops terms whose value is zero leaves the decomposition
unchanged; glex() confirms this against the model's predictions, reports it with
a message, and treats the result as complete.
remainder: What the dropped terms are collectively worth, per observation:
prediction - (intercept + rowSums(m)). Present exactly when the decomposition is
constrained, and absent otherwise, so intercept + rowSums(m) + remainder
reconstructs the prediction in either case. For multiclass randomPlantedForest
models it is class-wise, mirroring m.
Like m and shap, it is on the scale that the model is decomposed on, which for
xgboost is the link scale and not the response: for a binary:logistic model
the reconstruction gives the margin, and plogis(intercept + rowSums(m) + remainder)
gives the predicted probability. ranger probability forests and
randomPlantedForest are decomposed on the response scale, where no such
back-transformation is needed. Adding remainder to a probability is therefore
never correct for xgboost.
rpf_pruned
The list has one more element, pruned, a list with
components: a data.frame of the model's components, with columns term, degree, share (the share of the
variance of the prediction that prune_components() measured on the training rows, before the coefficients),
beta (the coefficient), form and heat_maps (see randomPlantedForest::prune_components());
coarse and asis: for each component of three or more predictors, the predictors C its heat maps are split by
and their cut points cuts;
max_panels, intercept, mode and loss of the model, and the model itself (model), which the plots
evaluate on grids.
Liu, J., Steensgaard, T., Wright, M. N., Pfister, N., & Hiabu, M. (2025). Fast Estimation of Partial Dependence Functions using Trees. Proceedings of the 42nd International Conference on Machine Learning, PMLR 267:39496-39534. PMLR | arXiv:2410.13448
# Random Planted Forest ----- library(randomPlantedForest) set.seed(1) rp <- rpf(mpg ~ ., data = mtcars[1:26, ], max_interaction = 2) glex_rpf <- glex(rp, mtcars[27:32, ]) str(glex_rpf, list.len = 5) # rpf() above returned a pruned model (randomPlantedForest >= 0.6): its components glex_rpf$pruned$components # xgboost ----- library(xgboost) x <- as.matrix(mtcars[, -1]) y <- mtcars$mpg xg <- xgboost(x[1:26, ], y[1:26], max_depth = 4, learning_rate = .1, nrounds = 10, verbosity = 0, nthreads = 1 ) glex(xg, x[27:32, ]) glex(xg, mtcars[27:32, ]) ## Not run: # Parallel execution doParallel::registerDoParallel() glex(xg, x[27:32, ]) ## End(Not run) # ranger ----- library(ranger) x <- as.matrix(mtcars[, -1]) y <- mtcars$mpg rf <- ranger( x = x[1:26, ], y = y[1:26], num.trees = 5, max.depth = 3, node.stats = TRUE ) glex(rf, x[27:32, ]) glex(rf, mtcars[27:32, ]) ## Not run: # Parallel execution doParallel::registerDoParallel() glex(rf, x[27:32, ]) ## End(Not run)# Random Planted Forest ----- library(randomPlantedForest) set.seed(1) rp <- rpf(mpg ~ ., data = mtcars[1:26, ], max_interaction = 2) glex_rpf <- glex(rp, mtcars[27:32, ]) str(glex_rpf, list.len = 5) # rpf() above returned a pruned model (randomPlantedForest >= 0.6): its components glex_rpf$pruned$components # xgboost ----- library(xgboost) x <- as.matrix(mtcars[, -1]) y <- mtcars$mpg xg <- xgboost(x[1:26, ], y[1:26], max_depth = 4, learning_rate = .1, nrounds = 10, verbosity = 0, nthreads = 1 ) glex(xg, x[27:32, ]) glex(xg, mtcars[27:32, ]) ## Not run: # Parallel execution doParallel::registerDoParallel() glex(xg, x[27:32, ]) ## End(Not run) # ranger ----- library(ranger) x <- as.matrix(mtcars[, -1]) y <- mtcars$mpg rf <- ranger( x = x[1:26, ], y = y[1:26], num.trees = 5, max.depth = 3, node.stats = TRUE ) glex(rf, x[27:32, ]) glex(rf, mtcars[27:32, ]) ## Not run: # Parallel execution doParallel::registerDoParallel() glex(rf, x[27:32, ]) ## End(Not run)
The color choices used across all glex visualization functions can be
adjusted globally via options():
glex.palette (NULL): Diverging palette used to color continuous
interaction effects in plot_twoway_effects() and
plot_threeway_effects(). The default NULL uses a blue/red gradient
built from glex.colors_sign, matching the look of the Python shap
and shapiq packages. Set to the name of a diverging
scico palette (e.g. "vik", "roma") to use that
instead.
glex.palette_discrete ("Dark2"): Discrete palette used to color
categorical predictors in interaction plots. Accepts a vector of colors
(used via ggplot2::scale_color_manual()), the string "okabe-ito"
(the colorblind-safe Okabe-Ito palette via grDevices::palette.colors()),
the name of a scico palette, or the name of an
RColorBrewer palette.
glex.colors_sign (c("#008BFB", "#FF0051")): Two colors for
negative and positive contributions in plot_waterfall(), plot_force()
and plot_shap_decomposition(), also used as
the endpoints of the default continuous gradient. The defaults
follow the blue/red convention familiar from the Python shap and
shapiq packages.
glex.color_line ("#194155"): Color for main effect lines and
columns drawn by plot.glex() and plot_pdp().
# Use a scico palette for continuous effects instead of the default gradient options(glex.palette = "roma") # Restore the default shap-style gradient options(glex.palette = NULL) # Categorical predictors: Okabe-Ito, a scico/brewer palette, or custom colors options(glex.palette_discrete = "okabe-ito") options(glex.palette_discrete = "batlow") options(glex.palette_discrete = c("#E69F00", "#56B4E9", "#009E73")) # Restore the default options(glex.palette_discrete = NULL)# Use a scico palette for continuous effects instead of the default gradient options(glex.palette = "roma") # Restore the default shap-style gradient options(glex.palette = NULL) # Categorical predictors: Okabe-Ito, a scico/brewer palette, or custom colors options(glex.palette_discrete = "okabe-ito") options(glex.palette_discrete = "batlow") options(glex.palette_discrete = c("#E69F00", "#56B4E9", "#009E73")) # Restore the default options(glex.palette_discrete = NULL)
Variable Importance for Main and Interaction Terms
glex_vi(object, measure = c("mean_abs", "variance", "share"), ...)glex_vi(object, measure = c("mean_abs", "variance", "share"), ...)
object |
Object of class |
measure |
(
|
... |
(Unused) |
measure = "mean_abs" (the default). The m reported is the average absolute value of m as reported by
glex(), aggregated by term (and by class for multiclass models):
In turn, m_rel rescales m by the average prediction of the model (, intercept as reported by glex();
its class's for models with one intercept per class):
It is in units of the prediction: how far the term moves a typical prediction.
measure = "variance". m is the mean square of the term,
which is the mean squared change of the prediction if the term were dropped: what dropping it costs in squared
error. m_rel is m over the variance of the prediction at x (the intercept plus all terms, and remainder if
any), its share of the variance of the prediction. For the components of a purified decomposition (mean zero), m
is the variance of the term. For multiclass models, m and the variance of the prediction are summed over the
classes' scores (one row per term). This is the size by which randomPlantedForest's prune_components() ranks and
prunes the components, except that it measures it on the training rows: for the glex() object of a pruned
model, measure = "share" gives the pruning's own numbers. (For classification with loss = "logit" or
"exponential", randomPlantedForest reports the scores centred across the classes, as the pruning measures them.)
measure = "share". For the glex() object of a pruned Random Planted Forest: m and m_rel are both the
pruning's share of the variance of the prediction on the training rows, as in object$pruned$components.
The two measures can rank terms differently: a term that is large on a few observations and zero elsewhere ranks higher by its variance than by its mean absolute value.
A data.table::data.table() with columns:
degree (integer): Degree of interaction of the term, with 1 being main effects,
2 being 2-degree interactions etc.
term (character): Model term, e.g. main effect x1 or interaction term x1:x2, x1:x3:x5 etc.
class (factor): For multiclass targets and measure = "mean_abs" only: The associated target class. Lists all
classes in the target, not limited to the majority vote.
m (numeric): The importance of term by measure, see Details.
m_rel (numeric): m relative to a scale, see Details.
The measure is stored as the attribute "measure". Terms with m of zero are left out; the rows are sorted by
decreasing m.
set.seed(1) # xgboost ----- library(xgboost) x <- as.matrix(mtcars[, -1]) y <- mtcars$mpg xg <- xgboost(x[1:26, ], y[1:26], max_depth = 4, learning_rate = .1, nrounds = 10, verbosity = 0, nthreads = 1 ) glex_xgb <- glex(xg, x[27:32, ]) vi_xgb <- glex_vi(glex_xgb) plot(vi_xgb) plot(vi_xgb, by_degree = TRUE) # by the share of the variance of the prediction plot(glex_vi(glex_xgb, measure = "variance"), scale = "relative")set.seed(1) # xgboost ----- library(xgboost) x <- as.matrix(mtcars[, -1]) y <- mtcars$mpg xg <- xgboost(x[1:26, ], y[1:26], max_depth = 4, learning_rate = .1, nrounds = 10, verbosity = 0, nthreads = 1 ) glex_xgb <- glex(xg, x[27:32, ]) vi_xgb <- glex_vi(glex_xgb) plot(vi_xgb) plot(vi_xgb, by_degree = TRUE) # by the share of the variance of the prediction plot(glex_vi(glex_xgb, measure = "variance"), scale = "relative")
Aggregates the terms of a decomposition such that a set of features is treated as a single feature, named after the group. All terms involving only members of a group are summed into the group's main effect, and terms mixing group members with other features become interactions of the group. Since the decomposition is additive, this regrouping is exact: components still sum to the model prediction, and SHAP values of a group are the sums of its members' SHAP values, preserving the efficiency property.
group_components(object, groups)group_components(object, groups)
object |
( |
groups |
( |
The motivating use case is dummy-encoded categorical features in xgboost:
the decomposition of a one-hot encoded factor spreads its effect across the
dummies and their interactions (e.g. fa:fb), which are artifacts of the
encoding rather than meaningful interactions. Grouping the dummies restores
the factor as one feature, e.g. groups = list(f = c("fa", "fb", "fc"))
turns fa, fb, fc, fa:fb, ... into f, and fa:x1, fa:fb:x1, ...
into f:x1.
For the grouped columns of x, a factor is reconstructed when the group's
columns form a dummy encoding (0/1 values, at most one active per row), with
the active column's name as the level and "(base)" for rows with no active
column (treatment coding). Otherwise the group's column in x is NA and
plot functions cannot be used for that group's main effect.
Object of class glex with grouped m, shap and x.
library(xgboost) set.seed(1) n <- 200 f <- factor(sample(c("a", "b", "c"), n, replace = TRUE)) x1 <- rnorm(n) y <- c(a = 0, b = 2, c = -1)[f] + x1 + c(a = 1, b = 0, c = -1)[f] * x1 + rnorm(n) x <- cbind(model.matrix(~ f - 1), x1) xg <- xgboost(x, y, nrounds = 10, max_depth = 3, nthreads = 1) gl <- glex(xg, x) names(gl$m) grouped <- group_components(gl, groups = list(f = c("fa", "fb", "fc"))) names(grouped$m) all.equal(rowSums(grouped$m), rowSums(gl$m))library(xgboost) set.seed(1) n <- 200 f <- factor(sample(c("a", "b", "c"), n, replace = TRUE)) x1 <- rnorm(n) y <- c(a = 0, b = 2, c = -1)[f] + x1 + c(a = 1, b = 0, c = -1)[f] * x1 + rnorm(n) x <- cbind(model.matrix(~ f - 1), x1) xg <- xgboost(x, y, nrounds = 10, max_depth = 3, nthreads = 1) gl <- glex(xg, x) names(gl$m) grouped <- group_components(gl, groups = list(f = c("fa", "fb", "fc"))) names(grouped$m) all.equal(rowSums(grouped$m), rowSums(gl$m))
A compact view of the same information as plot_waterfall(): the components
of one observation's prediction as segments of a single band. Positive terms
push the prediction up from the left, negative terms push it down from the
right, and both meet at the prediction f(x). The largest terms sit next to
f(x).
plot_force( object, id, threshold = NULL, max_terms = 10L, max_interaction = NULL, class = NULL )plot_force( object, id, threshold = NULL, max_terms = 10L, max_interaction = NULL, class = NULL )
object |
Object of class |
id |
( |
threshold |
( |
max_terms |
( |
max_interaction |
( |
class |
( |
Only segments at least 5% as wide as the plotted range are labeled; the
caption states how many are not. Use max_terms or threshold to aggregate
small terms instead.
The layout follows the force plots introduced by Lundberg et al. (2018) in the Python 'shap' library, also available in the R package 'shapviz', which show SHAP values (one segment per feature) rather than the terms of a functional decomposition.
A ggplot object.
Lundberg, S. M., Nair, B., Vavilala, M. S., et al. (2018). Explainable machine-learning predictions for the prevention of hypoxaemia during surgery. Nature Biomedical Engineering, 2, 749–760. doi:10.1038/s41551-018-0304-0
Mayer, M. (2026). shapviz: SHAP Visualizations. R package. https://CRAN.R-project.org/package=shapviz
Other Visualization functions:
plot.glex(),
plot.glex_vi(),
plot_pdp(),
plot_shap_decomposition(),
plot_waterfall()
library(randomPlantedForest) set.seed(1) rp <- rpf(mpg ~ ., data = mtcars[1:26, ], max_interaction = 2) gl <- glex(rp, mtcars[27:32, ]) plot_force(gl, id = 3, max_terms = 6)library(randomPlantedForest) set.seed(1) rp <- rpf(mpg ~ ., data = mtcars[1:26, ], max_interaction = 2) gl <- glex(rp, mtcars[27:32, ]) plot_force(gl, id = 3, max_terms = 6)
A version of plot_main_effect with the intercept term (horizontal line) added,
resulting in a partial dependence plot.
plot_pdp(object, predictor, rug_sides = "b", ...)plot_pdp(object, predictor, rug_sides = "b", ...)
object |
Object of class |
predictor |
|
rug_sides |
|
... |
Used for future expansion. |
A ggplot2 object.
Other Visualization functions:
plot.glex(),
plot.glex_vi(),
plot_force(),
plot_shap_decomposition(),
plot_waterfall()
library(randomPlantedForest) # introduce factor variables to show categorical feature handling mtcars$cyl <- factor(mtcars$cyl) mtcars$vs <- factor(mtcars$vs) # Fit the model, get components set.seed(12) rpfit <- rpf(mpg ~ cyl + wt + hp + drat + vs, data = mtcars, max_interaction = 3) components <- glex(rpfit, mtcars) plot_pdp(components, "wt") plot_pdp(components, "cyl")library(randomPlantedForest) # introduce factor variables to show categorical feature handling mtcars$cyl <- factor(mtcars$cyl) mtcars$vs <- factor(mtcars$vs) # Fit the model, get components set.seed(12) rpfit <- rpf(mpg ~ cyl + wt + hp + drat + vs, data = mtcars, max_interaction = 3) components <- glex(rpfit, mtcars) plot_pdp(components, "wt") plot_pdp(components, "cyl")
For one observation, shows the components involving each selected feature,
starting at the intercept E[f]. Interaction terms are split evenly among
their features, so each feature's bars sum to its SHAP value, shown in the
bottom row. To see the whole prediction instead, use plot_waterfall() or
plot_force().
plot_shap_decomposition( object, id, threshold = NULL, max_interaction = NULL, predictors = NULL, class = NULL, barheight = 0.5 ) glex_explain(...)plot_shap_decomposition( object, id, threshold = NULL, max_interaction = NULL, predictors = NULL, class = NULL, barheight = 0.5 ) glex_explain(...)
object |
Object of class |
id |
( |
threshold |
( |
max_interaction |
( |
predictors |
( |
class |
( |
barheight |
( |
... |
Arguments passed to |
The plot can get busy; use predictors, max_interaction, or threshold to
restrict it.
A ggplot object.
Other Visualization functions:
plot.glex(),
plot.glex_vi(),
plot_force(),
plot_pdp(),
plot_waterfall()
set.seed(1) library(randomPlantedForest) rp <- rpf(mpg ~ ., data = mtcars[1:26, ], max_interaction = 2) glex_rpf <- glex(rp, mtcars[27:32, ]) plot_shap_decomposition(glex_rpf, id = 3, predictors = "hp", threshold = 0.01)set.seed(1) library(randomPlantedForest) rp <- rpf(mpg ~ ., data = mtcars[1:26, ], max_interaction = 2) glex_rpf <- glex(rp, mtcars[27:32, ]) plot_shap_decomposition(glex_rpf, id = 3, predictors = "hp", threshold = 0.01)
Shows how the prediction for one observation is built from the components of
the decomposition: starting at the intercept E[f], each bar adds one main or
interaction term until the prediction f(x) is reached. Unlike
plot_shap_decomposition(), which splits interaction terms among their
features, every term is its own bar.
plot_waterfall( object, id, threshold = NULL, max_terms = 10L, max_interaction = NULL, class = NULL )plot_waterfall( object, id, threshold = NULL, max_terms = 10L, max_interaction = NULL, class = NULL )
object |
Object of class |
id |
( |
threshold |
( |
max_terms |
( |
max_interaction |
( |
class |
( |
Bars of interaction terms are more transparent the higher their degree. For
constrained decompositions (see glex()), what the dropped terms account for
is shown as a final "remainder (constrained)" bar, so the plot still ends at
the model's prediction.
The layout follows the waterfall plots of the Python 'shap' library and of the R package 'shapviz', which show SHAP values (one bar per feature) rather than the terms of a functional decomposition.
A ggplot object.
Lundberg, S. M. & Lee, S.-I. (2017). A unified approach to interpreting model predictions. Advances in Neural Information Processing Systems 30. https://proceedings.neurips.cc/paper/2017/hash/8a20a8621978632d76c43dfd28b67767-Abstract.html
Mayer, M. (2026). shapviz: SHAP Visualizations. R package. https://CRAN.R-project.org/package=shapviz
Other Visualization functions:
plot.glex(),
plot.glex_vi(),
plot_force(),
plot_pdp(),
plot_shap_decomposition()
library(randomPlantedForest) set.seed(1) rp <- rpf(mpg ~ ., data = mtcars[1:26, ], max_interaction = 2) gl <- glex(rp, mtcars[27:32, ]) plot_waterfall(gl, id = 3) plot_waterfall(gl, id = 3, max_terms = 5)library(randomPlantedForest) set.seed(1) rp <- rpf(mpg ~ ., data = mtcars[1:26, ], max_interaction = 2) gl <- glex(rp, mtcars[27:32, ]) plot_waterfall(gl, id = 3) plot_waterfall(gl, id = 3, max_terms = 5)
Plotting the main effects among the prediction components is effectively identical to a partial dependence plot, centered to 0.
## S3 method for class 'glex' plot( x, predictors = NULL, ..., measure = NULL, max_components = NULL, ncol = NULL, resolution = 100L ) plot_main_effect(object, predictor, rug_sides = "b", ...) plot_threeway_effects( object, predictors, rug_sides = "b", resolution = 100L, ... ) plot_twoway_effects( object, predictors, rug_sides = "b", resolution = 100L, ... )## S3 method for class 'glex' plot( x, predictors = NULL, ..., measure = NULL, max_components = NULL, ncol = NULL, resolution = 100L ) plot_main_effect(object, predictor, rug_sides = "b", ...) plot_threeway_effects( object, predictors, rug_sides = "b", resolution = 100L, ... ) plot_twoway_effects( object, predictors, rug_sides = "b", resolution = 100L, ... )
x |
Object of class |
... |
Used for future expansion. |
measure |
(
|
max_components |
( |
ncol |
( |
resolution |
( |
object |
Object of class |
predictor, predictors
|
|
rug_sides |
|
plot() without predictors draws the model's components in one figure (with the patchwork package), the most
important first by measure, each titled by its name and importance, e.g. x2:x3 (24%): main effects and two-way
interactions for any model, and for a model of class rpf_pruned (what rpf() returns from randomPlantedForest
0.6 on) all of its components, including those of three or more predictors.
Models of class rpf_pruned. Such a model has few components, each stored in the form it predicts with. The
plots show each component as it enters the prediction:
a main effect as a step curve through its values at x;
a two-way interaction as a heat map, evaluated on a grid of the values of its predictors in x: at most
resolution cells along each predictor, the distinct values if there are at most resolution, otherwise the
values at the quantiles (i - 0.5) / resolution, each cell reaching halfway to its neighbours (a factor: one cell
per level);
a component of three or more predictors as heat maps over two of its predictors, one per cell of the others (the
cells the model stores for it), each labelled by its cell, on one colour scale. The heat maps are exact in the
other predictors: the component is constant on each cell. plot_threeway_effects() draws them for three
predictors, plot() for any number; the order of predictors does not matter. (A model from an earlier
randomPlantedForest that does not store the cells gets one heat map, the other predictors at their values in the
first row of x, with a message.)
For multiclass models every plot has one panel (or set of heat maps) per class. The grids need the model, which the
glex object keeps: a model saved to a file and restored without randomPlantedForest::rpf_unmarshal() can't be
evaluated.
A ggplot2 object.
Other Visualization functions:
plot.glex_vi(),
plot_force(),
plot_pdp(),
plot_shap_decomposition(),
plot_waterfall()
library(randomPlantedForest) # Every 8th hour of the bike sharing data (see ?bike) bike_8 <- bike[seq(1, nrow(bike), by = 8), ] # Fit the model, get components set.seed(12) rpfit <- rpf(bikers ~ hr + temp + workingday + season, data = bike_8, max_interaction = 3) components <- glex(rpfit, bike_8) # Main effects ---- plot_main_effect(components, "temp") plot_main_effect(components, "season") # plot() picks the plot by the number of predictors ---- plot(components, c("hr", "temp")) # and without predictors draws the whole model ---- plot(components) # plot_threeway_effects(components, c("hr", "temp", "workingday")) library(randomPlantedForest) # 2-degree interaction effects ---- # 2d continuous plot_twoway_effects(components, c("hr", "temp")) # continuous + categorical plot_twoway_effects(components, c("hr", "workingday"))library(randomPlantedForest) # Every 8th hour of the bike sharing data (see ?bike) bike_8 <- bike[seq(1, nrow(bike), by = 8), ] # Fit the model, get components set.seed(12) rpfit <- rpf(bikers ~ hr + temp + workingday + season, data = bike_8, max_interaction = 3) components <- glex(rpfit, bike_8) # Main effects ---- plot_main_effect(components, "temp") plot_main_effect(components, "season") # plot() picks the plot by the number of predictors ---- plot(components, c("hr", "temp")) # and without predictors draws the whole model ---- plot(components) # plot_threeway_effects(components, c("hr", "temp", "workingday")) library(randomPlantedForest) # 2-degree interaction effects ---- # 2d continuous plot_twoway_effects(components, c("hr", "temp")) # continuous + categorical plot_twoway_effects(components, c("hr", "workingday"))
Plot glex Variable Importances
## S3 method for class 'glex_vi' plot( x, by_degree = FALSE, threshold = NULL, max_interaction = NULL, scale = "absolute", ... )## S3 method for class 'glex_vi' plot( x, by_degree = FALSE, threshold = NULL, max_interaction = NULL, scale = "absolute", ... )
x |
Object of class |
by_degree |
( |
threshold |
( |
max_interaction |
( |
scale |
( |
... |
(Unused) |
A ggplot object.
Other Visualization functions:
plot.glex(),
plot_force(),
plot_pdp(),
plot_shap_decomposition(),
plot_waterfall()
library(xgboost) x <- as.matrix(mtcars[, -1]) xg <- xgboost(x, mtcars$mpg, max_depth = 3, nrounds = 10, nthreads = 1) vi <- glex_vi(glex(xg, x)) plot(vi, threshold = 0.05) plot(vi, by_degree = TRUE)library(xgboost) x <- as.matrix(mtcars[, -1]) xg <- xgboost(x, mtcars$mpg, max_depth = 3, nrounds = 10, nthreads = 1) vi <- glex_vi(glex(xg, x)) plot(vi, threshold = 0.05) plot(vi, by_degree = TRUE)
This is implemented mainly to avoid flooding the console in cases where the glex object
uses many terms, which leads to a large amount of column names of $m being printed to the console.
This function wraps str() with a truncated output for a more compact representation.
## S3 method for class 'glex' print(x, ...)## S3 method for class 'glex' print(x, ...)
x |
Object to print. |
... |
(Unused) |
# Random Planted Forest ----- library(randomPlantedForest) rp <- rpf(mpg ~ hp + wt + drat, data = mtcars[1:26, ], max_interaction = 2) glex(rp, mtcars[27:32, ])# Random Planted Forest ----- library(randomPlantedForest) rp <- rpf(mpg ~ hp + wt + drat, data = mtcars[1:26, ], max_interaction = 2) glex(rp, mtcars[27:32, ])
Subset components
subset_components(components, term) subset_component_names(components, term)subset_components(components, term) subset_component_names(components, term)
components |
An object of class |
term |
( |
subset_components(): An object of class glex.
subset_component_names(): A character vector.
library(randomPlantedForest) # introduce factor variables to show categorical feature handling mtcars$cyl <- factor(mtcars$cyl) mtcars$vs <- factor(mtcars$vs) # Fit the model, get components set.seed(12) rpfit <- rpf(mpg ~ cyl + wt + hp + drat + vs, data = mtcars, max_interaction = 3) components <- glex(rpfit, mtcars) # Get component object with only "hp" and its interactions subset_components(components, "hp") subset_component_names(components, "hp")library(randomPlantedForest) # introduce factor variables to show categorical feature handling mtcars$cyl <- factor(mtcars$cyl) mtcars$vs <- factor(mtcars$vs) # Fit the model, get components set.seed(12) rpfit <- rpf(mpg ~ cyl + wt + hp + drat + vs, data = mtcars, max_interaction = 3) components <- glex(rpfit, mtcars) # Get component object with only "hp" and its interactions subset_components(components, "hp") subset_component_names(components, "hp")
This is a slight variation of ggplot2::theme_minimal() with increased font size.
theme_glex( base_size = 13, base_family = "", base_line_size = base_size/22, base_rect_size = base_size/22, grid_x = TRUE, grid_y = FALSE )theme_glex( base_size = 13, base_family = "", base_line_size = base_size/22, base_rect_size = base_size/22, grid_x = TRUE, grid_y = FALSE )
base_size |
( |
base_family |
( |
base_line_size, base_rect_size
|
( |
grid_x |
( |
grid_y |
( |
A ggplot2 theme object
library(ggplot2) ggplot(mtcars, aes(wt, mpg)) + geom_point() + theme_glex()library(ggplot2) ggplot(mtcars, aes(wt, mpg)) + geom_point() + theme_glex()