SHAP (SHapley Additive exPlanations) values were introduced by Lundberg & Lee (2017) and quantify the
contribution of individual predictors to a model’s predictions. SHAP
values quantify these contributions for specific observations, and
therefore provide measures of local importance Molnar (2025). Although exact SHAP values can be
computationally expensive to obtain, in Spadaccini, Fokkema, & Wiel (2025) we
derived a simplified expression for computing exact marginal SHAP values
for prediction rule ensembles. This functionality is implemented in
function shap. This vignette illustrates its use, and how
the results can be plotted using package shapviz.
We fit a PRE to predict Ozone (daily ozone readings)
with the airquality data:
airq <- na.omit(airquality)
set.seed(42)
airq.ens <- pre(Ozone ~ ., data = airq)
airq.ens ## equivalent to print(airq.ens)##
## Final ensemble with cv error within 1se of minimum:
##
## lambda = 3.543968
## number of terms = 12
## mean cv error (se) = 352.395 (99.13754)
##
## cv error type : Mean-Squared Error
##
## rule coefficient description
## (Intercept) 68.48270407 1
## rule191 -10.97368180 Wind > 5.7 & Temp <= 87
## rule173 -10.90385519 Wind > 5.7 & Temp <= 82
## rule42 -8.79715538 Wind > 6.3 & Temp <= 84
## rule204 7.16114781 Wind <= 10.3 & Solar.R > 148
## rule10 -4.68646145 Temp <= 84 & Temp <= 77
## rule192 -3.34460038 Wind > 5.7 & Temp <= 87 & Day <= 23
## rule51 -2.27864287 Wind > 5.7 & Temp <= 84
## rule93 2.18465676 Temp > 77 & Wind <= 8.6
## rule74 -1.36479545 Wind > 6.9 & Temp <= 84
## rule28 -1.15326093 Temp <= 84 & Wind > 7.4
## rule25 -0.70818400 Wind > 6.3 & Temp <= 82
## rule166 -0.04751152 Wind > 6.9 & Temp <= 82
Twelve rules were selected, involving predictors Temp,
Wind, Solar.R and Day. No linear
terms were selected.
By default, the 1SE rule is used to select the final ensemble by the
print method, which can be overridden by specifying the
penalty.par.val argument.
We compute SHAP values using function shap (it uses the
same default for the penalty.par.val argument, which can be
overridden):
The newdata argument specifies for which observations
SHAP values should be computed. This can be one or more new (or test)
observations, but here we requested SHAP values for the full training
dataset. Not specifying the newdata argument would have
yielded the exact same result.
The result is a list of two elements, marginal and
interactions:
## List of 2
## $ marginal : num [1:111, 1:5] 1.839 -3.935 0.677 0.677 1.839 ...
## ..- attr(*, "dimnames")=List of 2
## .. ..$ : chr [1:111] "1" "2" "3" "4" ...
## .. ..$ : chr [1:5] "Solar.R" "Wind" "Temp" "Month" ...
## $ interactions: NULL
Marginal SHAP values are computed for each predictor, by default. The
marginal SHAP values are returned as an \(N
\times p\) matrix, with SHAP values for each observation (rows)
and predictor (columns). Interaction SHAP values will be computed when
interactions = TRUE is specified.
shapvizWe use R package shapviz to visualize the SHAP values.
We need to construct a shapviz object from the SHAP values,
predictor variable values and a baseline:
library("shapviz")
baseline <- mean(predict(airq.ens, newdata = airq))
sv <- shapviz(object = airq.shaps$marginal,
X = airq,
baseline = baseline)With the default reference data used by function shap
(see also argument reference_data for changing it), the
baseline is the mean prediction over the the training data. Furthermore,
argument X specifies the original predictor values
corresponding to the rows of the SHAP matrix. Note that categorical
predictors (if present) should be supplied in their original factor
representation.
From this shapviz object, we can obtain e.g. beeswarm
plots:
The predictors are ordered by the variation in their SHAP values.
More variation corresponds to greater magnitude, because a variable that
does not contribute to predictions would have SHAP values of 0.
Temp thus appears most important, followed by
Wind, then by Solar.R, then by
Day which has a very minor effect and finally
Month which has no effect on predictions.
The contributions of Temp and Solar.R
appear monotonically increasing, the effect of Wind
monotonically decreasing. If observations with similar feature values
(and therefore similar colors) have substantially different SHAP values,
this suggests interactions with other predictors, which does not seem to
be the case here.
Month does not affect predictions, but its points are
colored mostly yellow because the observations in airq are
ordered by Month. The apparent color pattern is therefore
an artifact of the observation order, which is also used for
plotting.
Often, waterfall plots are used for explaining individual predictions, e.g.:
The waterfall starts at the baseline prediction and successively adds
the SHAP contributions to arrive at the model prediction. For the first
observation in the dataset, Solar.R and Temp
have small positive effects and Day has a small negative
effect. We observe zero contribution for Month, because it
does not appear in any of the terms of the rule ensemble.
Other functions from package shapviz, like
sv_importance and sv_dependence can also be
applied to sv.
We can obtain SHAP interaction values by specifying
interactions = TRUE:
## List of 2
## $ marginal : num [1:111, 1:5] 1.839 -3.935 0.677 0.677 1.839 ...
## ..- attr(*, "dimnames")=List of 2
## .. ..$ : chr [1:111] "1" "2" "3" "4" ...
## .. ..$ : chr [1:5] "Solar.R" "Wind" "Temp" "Month" ...
## $ interactions: num [1:111, 1:5, 1:5] 1.35 -3.03 1.35 1.35 1.35 ...
## ..- attr(*, "dimnames")=List of 3
## .. ..$ : chr [1:111] "1" "2" "3" "4" ...
## .. ..$ : chr [1:5] "Solar.R" "Wind" "Temp" "Month" ...
## .. ..$ : chr [1:5] "Solar.R" "Wind" "Temp" "Month" ...
Interaction SHAP values are saved in a three-dimensional \(N \times p \times p\) array. That is, for each of the \(N\) observations, a \(p \times p\) matrix of SHAP values is returned. The diagonal entries are predictor variables’ main-effect SHAP values, and the off-diagonal entries represent their interaction SHAP values. E.g., for the first observation the SHAP interaction matrix looks as follows:
## Solar.R Wind Temp Month Day
## Solar.R 1.3548117 0.48386134 0.00000000 0 0.00000000
## Wind 0.4838613 1.43358614 -1.77812376 0 -0.03766442
## Temp 0.0000000 -1.77812376 -8.98161032 0 -0.06779595
## Month 0.0000000 0.00000000 0.00000000 0 0.00000000
## Day 0.0000000 -0.03766442 -0.06779595 0 -0.56747724
The diagonal entries tend to have stronger magnitude than the
off-diagonal entries, indicating that the model’s effects for this
observation are mostly main effects, while interactions are minor. There
is one exception to this rule: The interaction SHAP for
Wind and Temp, which has stronger magnitude
than the main effect of Wind (but weaker than the main
effect of Temp) for this observation.
Interaction SHAP values decompose the marginal SHAP values into contributions due to main effects and to interactions. Because the matrix of interaction SHAP values is symmetric, the row sums and column sums are identical, and they are also identical to the marginal SHAP values. E.g. for the first observation we have:
## Solar.R Wind Temp Month Day
## 1.8386731 0.1016593 -10.8275300 0.0000000 -0.6729376
## Solar.R Wind Temp Month Day
## 1.8386731 0.1016593 -10.8275300 0.0000000 -0.6729376
## Solar.R Wind Temp Month Day
## 1.8386731 0.1016593 -10.8275300 0.0000000 -0.6729376
SHAP values represent the predictor variables’ contribution to the predicted value, so by definition they sum to the difference between the model’s predicted value and the baseline prediction. More formally, a model’s prediction \(f(x)\) is given by:
\[f(x) = E[f(X)] + \sum_j \phi_j(x),\] where \(f\) is the predictive model, \(x\) is a vector of predictor variable values, \(E[f(X)]\) is the expected (or mean) model prediction over the reference distribution and \(\phi_j(x)\) is the SHAP value for predictor \(j\).
The SHAP values \(\phi_j\) can be further decomposed into a main-effect contribution (diagonal elements) and interaction contributions with all other predictors (the corresponding off-diagonal elements). More formally,
\[ \phi_j(x) = \phi_{jj}(x) + \sum_{k\neq j}\phi_{jk}(x), \]
where \(\phi_{jj}\) is the main effect (diagonal entries of the SHAP interaction matrix) and \(\phi_{jk}\) are pairwise interactions (off-diagonal entries of the SHAP interaction matrix).
From the computed (interaction) SHAP values, we again create a
shapviz object:
baseline <- mean(predict(airq.ens, newdata = airq))
sv_int <- shapviz(object = airq.shaps$marginal,
X = airq,
baseline = baseline,
S_inter = airq.shaps$interactions)
sv_interaction(sv_int)The plots on the diagonal show greatest variation in SHAP values, indicating again that main effects appear strongest in the model. Evidence for interaction effects would be found in off-diagonal plots showing variation in SHAP values.
We again observe that Month does not contribute to
predictions, indicated by all SHAP values being zero.
Solar.R and Day contribute only somewhat.
Wind and Temp show the strongest effects, and
there might be some interaction between them. Note that the two plots
for this interaction are identical, save for the coloring, which
indicates the values of Wind in the 2nd row, 1st column,
and the values of Temp in the 1st row, 2nd column. However,
the direction and form of this interaction are not easy to discern from
these plots alone.
In case you obtained different results, the results above were obtained using the following:
## R version 4.5.3 (2026-03-11 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 22631)
##
## Matrix products: default
## LAPACK version 3.12.1
##
## locale:
## [1] LC_COLLATE=C LC_CTYPE=Dutch_Netherlands.utf8
## [3] LC_MONETARY=Dutch_Netherlands.utf8 LC_NUMERIC=C
## [5] LC_TIME=Dutch_Netherlands.utf8
##
## time zone: Europe/Amsterdam
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] shapviz_0.10.3 pROC_1.19.0.1 caret_7.0-1 lattice_0.22-9 ggplot2_4.0.3
## [6] pre_1.1.0 mice_3.19.0
##
## loaded via a namespace (and not attached):
## [1] Rdpack_2.6.6 deldir_2.0-4 rlang_1.2.0
## [4] magrittr_2.0.4 otel_0.2.0 compiler_4.5.3
## [7] vctrs_0.7.2 reshape2_1.4.5 stringr_1.6.0
## [10] pkgconfig_2.0.3 shape_1.4.6.1 fastmap_1.2.0
## [13] backports_1.5.0 inum_1.0-5 labeling_0.4.3
## [16] rmarkdown_2.31 prodlim_2026.03.11 ggfittext_0.10.4
## [19] nloptr_2.2.1 MatrixModels_0.5-4 purrr_1.2.1
## [22] xfun_0.57 glmnet_5.0 jomo_2.7-6
## [25] cachem_1.1.0 shades_1.5.0 jsonlite_2.0.0
## [28] recipes_1.3.2 gggenes_0.7.0 pan_1.9
## [31] broom_1.0.12 parallel_4.5.3 R6_2.6.1
## [34] bslib_0.10.0 stringi_1.8.7 RColorBrewer_1.1-3
## [37] parallelly_1.47.0 boot_1.3-32 rpart_4.1.24
## [40] lubridate_1.9.5 jquerylib_0.1.4 xgboost_3.2.1.1
## [43] Rcpp_1.1.1 iterators_1.0.14 knitr_1.51
## [46] future.apply_1.20.2 Matrix_1.7-4 splines_4.5.3
## [49] nnet_7.3-20 timechange_0.4.0 tidyselect_1.2.1
## [52] rstudioapi_0.18.0 yaml_2.3.12 partykit_1.2-27
## [55] timeDate_4052.112 codetools_0.2-20 listenv_0.10.1
## [58] tibble_3.3.1 plyr_1.8.9 withr_3.0.3
## [61] S7_0.2.2 evaluate_1.0.5 future_1.70.0
## [64] survival_3.8-6 pillar_1.11.1 foreach_1.5.2
## [67] stats4_4.5.3 reformulas_0.4.4 generics_0.1.4
## [70] scales_1.4.0 minqa_1.2.8 plotmo_3.7.0
## [73] globals_0.19.1 class_7.3-23 glue_1.8.0
## [76] tools_4.5.3 interp_1.1-6 data.table_1.18.4
## [79] lme4_2.0-1 ModelMetrics_1.2.2.2 gower_1.0.2
## [82] mvtnorm_1.3-6 grid_4.5.3 plotrix_3.8-14
## [85] tidyr_1.3.2 rbibutils_2.4.1 libcoin_1.0-12
## [88] ipred_0.9-15 nlme_3.1-168 earth_5.3.6
## [91] Formula_1.2-5 cli_3.6.6 viridisLite_0.4.3
## [94] lava_1.9.0 dplyr_1.2.0 gtable_0.3.6
## [97] sass_0.4.10 digest_0.6.39 farver_2.1.2
## [100] htmltools_0.5.9 lifecycle_1.0.5 hardhat_1.4.3
## [103] mitml_0.4-5 MASS_7.3-65