{"id":1987,"date":"2025-09-20T22:52:29","date_gmt":"2025-09-20T20:52:29","guid":{"rendered":"https:\/\/lorentzen.ch\/?p=1987"},"modified":"2026-04-20T13:43:32","modified_gmt":"2026-04-20T11:43:32","slug":"key-improvements-in-shapviz-and-kernelshap","status":"publish","type":"post","link":"https:\/\/lorentzen.ch\/index.php\/2025\/09\/20\/key-improvements-in-shapviz-and-kernelshap\/","title":{"rendered":"Key improvements in shapviz and kernelshap"},"content":{"rendered":"\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"968\" height=\"688\" src=\"https:\/\/lorentzen.ch\/wp-content\/uploads\/2025\/09\/image-3.png\" alt=\"\" class=\"wp-image-1992\" srcset=\"https:\/\/lorentzen.ch\/wp-content\/uploads\/2025\/09\/image-3.png 968w, https:\/\/lorentzen.ch\/wp-content\/uploads\/2025\/09\/image-3-300x213.png 300w, https:\/\/lorentzen.ch\/wp-content\/uploads\/2025\/09\/image-3-768x546.png 768w\" sizes=\"auto, (max-width: 968px) 100vw, 968px\" \/><figcaption class=\"wp-element-caption\">SHAP interaction strength for the XGBoost model (single variables reflect SHAP main effect strength). <\/figcaption><\/figure>\n\n\n\n<p>Our two sister packages are continuously being improved. A brief summary of the latest changes:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">shapviz (v0.10.2)<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Identical axes, axis titles and color bars are now collected across dependence plots.<\/li>\n\n\n\n<li>Dependence plots have received arguments  <code>share_y=FALSE<\/code> and  <code>ylim=NULL<\/code> for better comparability across subplots.<\/li>\n\n\n\n<li>New visualization for SHAP interaction strenght via <code>sv_interaction(kind=\"bar\")<\/code>. It shows mean absolute SHAP interaction\/main effects, where the interaction values are multiplied by two for symmetry.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">kernelshap (v0.9.1)<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li><code>permshap()<\/code> now offers a balanced sampling version which iterates until convergence and returns standard errors. It is used by default when the model has more than eight features, or by setting <code>exact=FALSE<\/code>.<\/li>\n\n\n\n<li>Fixed an error in <code>kernelshap()<\/code> which made the resulting values slightly off for models with interactions of order three or higher. Now, the exact version returns the same values as exact permutation SHAP and agrees with the exact explainer in Python&#8217;s shap package.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Illustrating sampling permutation SHAP<\/h2>\n\n\n\n<p>Let&#8217;s use a beautiful dataset on medical costs to fit a log-linear Gamma GLM with interactions between all features and smoking, and explain it by SHAP on log prediction (= linear) scale.<\/p>\n\n\n\n<p>Since the model does not contain interactions of order above 2,  the SHAP values perfectly reconstruct the estimated model coefficients, see our recent paper on <a href=\"https:\/\/arxiv.org\/abs\/2508.12947\">https:\/\/arxiv.org\/abs\/2508.12947<\/a> for a proof.<\/p>\n\n\n\n<p>Smoking and age are the most important features. Some strong interactions with smoking are visible. <\/p>\n\n\n<div class=\"wp-block-ub-tabbed-content wp-block-ub-tabbed-content-holder wp-block-ub-tabbed-content-horizontal-holder-mobile wp-block-ub-tabbed-content-horizontal-holder-tablet\" id=\"ub-tabbed-content-282c0f5d-8db3-4ebf-a22a-fa045791dfb8\" style=\"\">\n\t\t\t<div class=\"wp-block-ub-tabbed-content-tab-holder horizontal-tab-width-mobile horizontal-tab-width-tablet\">\n\t\t\t\t<div role=\"tablist\" class=\"wp-block-ub-tabbed-content-tabs-title wp-block-ub-tabbed-content-tabs-title-mobile-horizontal-tab wp-block-ub-tabbed-content-tabs-title-tablet-horizontal-tab\" style=\"justify-content: flex-start; \"><div role=\"tab\" id=\"ub-tabbed-content-282c0f5d-8db3-4ebf-a22a-fa045791dfb8-tab-0\" aria-controls=\"ub-tabbed-content-282c0f5d-8db3-4ebf-a22a-fa045791dfb8-panel-0\" aria-selected=\"true\" class=\"wp-block-ub-tabbed-content-tab-title-wrap active\" style=\"--ub-tabbed-title-background-color: #6d6d6d; --ub-tabbed-active-title-color: inherit; --ub-tabbed-active-title-background-color: #6d6d6d; text-align: left; \" tabindex=\"-1\">\n\t\t\t\t<div class=\"wp-block-ub-tabbed-content-tab-title\">R<\/div>\n\t\t\t<\/div><\/div>\n\t\t\t<\/div>\n\t\t\t<div class=\"wp-block-ub-tabbed-content-tabs-content\" style=\"\"><div role=\"tabpanel\" class=\"wp-block-ub-tabbed-content-tab-content-wrap active\" id=\"ub-tabbed-content-282c0f5d-8db3-4ebf-a22a-fa045791dfb8-panel-0\" aria-labelledby=\"ub-tabbed-content-282c0f5d-8db3-4ebf-a22a-fa045791dfb8-tab-0\" tabindex=\"0\">\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting='{\"showPanel\":true,\"languageLabel\":\"language\",\"fullScreenButton\":true,\"copyButton\":true,\"mode\":\"r\",\"mime\":\"text\/x-rsrc\",\"theme\":\"material\",\"lineNumbers\":false,\"styleActiveLine\":false,\"lineWrapping\":false,\"readOnly\":true,\"fileName\":\"R\",\"language\":\"R\",\"maxHeight\":\"400px\",\"modeName\":\"r\"}'>library(xgboost)\nlibrary(ggplot2)\nlibrary(patchwork)\nlibrary(shapviz)\nlibrary(kernelshap)\n\noptions(shapviz.viridis_args = list(option = \"D\", begin = 0.1, end = 0.9))\n\nset.seed(1)\n\n# https:\/\/github.com\/stedy\/Machine-Learning-with-R-datasets\ndf &lt;- read.csv(\"https:\/\/raw.githubusercontent.com\/stedy\/Machine-Learning-with-R-datasets\/refs\/heads\/master\/insurance.csv\")\n\n# Gamma GLM with interactions\nfit_glm &lt;- glm(charges ~ . * smoker, data = df, family = Gamma(link = \"log\"))\n\n# Use SHAP to explain\nxvars &lt;- c(\"age\", \"sex\", \"bmi\", \"children\", \"smoker\", \"region\")\nX_explain &lt;- head(df[xvars], 500)\n\n# The new sampling permutation algo (forced with exact = FALSE)\nshap_glm &lt;- permshap(fit_glm, X_explain, exact = FALSE, seed = 1) |&gt;\n  shapviz()\n\nsv_importance(shap_glm, kind = \"bee\")\n\nsv_dependence(\n  shap_glm,\n  v = xvars,\n  share_y = TRUE,\n  color_var = \"smoker\"\n)\n<\/pre><\/div>\n\n<\/div><\/div>\n\t\t<\/div>\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"968\" height=\"688\" src=\"https:\/\/lorentzen.ch\/wp-content\/uploads\/2025\/09\/image.png\" alt=\"\" class=\"wp-image-1989\" srcset=\"https:\/\/lorentzen.ch\/wp-content\/uploads\/2025\/09\/image.png 968w, https:\/\/lorentzen.ch\/wp-content\/uploads\/2025\/09\/image-300x213.png 300w, https:\/\/lorentzen.ch\/wp-content\/uploads\/2025\/09\/image-768x546.png 768w\" sizes=\"auto, (max-width: 968px) 100vw, 968px\" \/><figcaption class=\"wp-element-caption\">SHAP beeswarm plot of the Gamma GLM<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"694\" src=\"https:\/\/lorentzen.ch\/wp-content\/uploads\/2025\/09\/image-2-1024x694.png\" alt=\"\" class=\"wp-image-1991\" srcset=\"https:\/\/lorentzen.ch\/wp-content\/uploads\/2025\/09\/image-2-1024x694.png 1024w, https:\/\/lorentzen.ch\/wp-content\/uploads\/2025\/09\/image-2-300x203.png 300w, https:\/\/lorentzen.ch\/wp-content\/uploads\/2025\/09\/image-2-768x521.png 768w, https:\/\/lorentzen.ch\/wp-content\/uploads\/2025\/09\/image-2.png 1320w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">SHAP dependence plots of the Gamma GLM, using &#8220;smoking&#8221; on the color scale<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Illustrating SHAP interaction strength<\/h2>\n\n\n\n<p>As a second example, we sloppily fit an XGBoost model with Gamma deviance loss to illustrate some of the SHAP interaction functionality of {shapviz}. As with the GLM, the SHAP values are being calculated on log scale.<\/p>\n\n\n\n<p>For the sake of brevity, we focus on plots visualizing SHAP interactions. In practice, make sure to use a clean train\/test\/(x-)validation and tuning approach.<\/p>\n\n\n\n<p>The strongest interaction effects (smoker * age, smoker * bmi) are stronger than most main effects. Not all interactions seem natural.<\/p>\n\n\n<div class=\"wp-block-ub-tabbed-content wp-block-ub-tabbed-content-holder wp-block-ub-tabbed-content-horizontal-holder-mobile wp-block-ub-tabbed-content-horizontal-holder-tablet\" id=\"ub-tabbed-content-70acc06d-e17d-4682-b61f-2f61dc7c69b3\" style=\"\">\n\t\t\t<div class=\"wp-block-ub-tabbed-content-tab-holder horizontal-tab-width-mobile horizontal-tab-width-tablet\">\n\t\t\t\t<div role=\"tablist\" class=\"wp-block-ub-tabbed-content-tabs-title wp-block-ub-tabbed-content-tabs-title-mobile-horizontal-tab wp-block-ub-tabbed-content-tabs-title-tablet-horizontal-tab\" style=\"justify-content: flex-start; \"><div role=\"tab\" id=\"ub-tabbed-content-70acc06d-e17d-4682-b61f-2f61dc7c69b3-tab-0\" aria-controls=\"ub-tabbed-content-70acc06d-e17d-4682-b61f-2f61dc7c69b3-panel-0\" aria-selected=\"true\" class=\"wp-block-ub-tabbed-content-tab-title-wrap active\" style=\"--ub-tabbed-title-background-color: #6d6d6d; --ub-tabbed-active-title-color: inherit; --ub-tabbed-active-title-background-color: #6d6d6d; text-align: left; \" tabindex=\"-1\">\n\t\t\t\t<div class=\"wp-block-ub-tabbed-content-tab-title\">R<\/div>\n\t\t\t<\/div><\/div>\n\t\t\t<\/div>\n\t\t\t<div class=\"wp-block-ub-tabbed-content-tabs-content\" style=\"\"><div role=\"tabpanel\" class=\"wp-block-ub-tabbed-content-tab-content-wrap active\" id=\"ub-tabbed-content-70acc06d-e17d-4682-b61f-2f61dc7c69b3-panel-0\" aria-labelledby=\"ub-tabbed-content-70acc06d-e17d-4682-b61f-2f61dc7c69b3-tab-0\" tabindex=\"0\">\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting='{\"showPanel\":true,\"languageLabel\":\"language\",\"fullScreenButton\":true,\"copyButton\":true,\"mode\":\"r\",\"mime\":\"text\/x-rsrc\",\"theme\":\"material\",\"lineNumbers\":false,\"styleActiveLine\":false,\"lineWrapping\":false,\"readOnly\":true,\"fileName\":\"R\",\"language\":\"R\",\"maxHeight\":\"400px\",\"modeName\":\"r\"}'># XGBoost model (sloppily without tuning)\nX_num &lt;- data.matrix(df[xvars])\nfit_xgb &lt;- xgb.train(\n  params = list(objective = \"reg:gamma\", learning_rate = 0.2),\n  data = xgb.DMatrix(X_num, label = df$charges),\n  nrounds = 100\n)\n\nshap_xgb &lt;- shapviz(fit_xgb, X_pred = X_num, X = df, interactions = TRUE)\n\n# SHAP interaction\/main-effect strength\nsv_interaction(shap_xgb, kind = \"bar\", fill = \"darkred\")\n\n# Study interaction\/main-effects of \"smoking\"\nsv_dependence(\n  shap_xgb,\n  v = xvars,\n  color_var = \"smoker\",\n  ylim = c(-1, 1),\n  interactions = TRUE\n) + # we rotate axis labels of *last* plot, otherwise use &amp;\n  guides(x = guide_axis(angle = 45))\n<\/pre><\/div>\n\n<\/div><\/div>\n\t\t<\/div>\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"968\" height=\"688\" src=\"https:\/\/lorentzen.ch\/wp-content\/uploads\/2025\/09\/image-3.png\" alt=\"\" class=\"wp-image-1992\" srcset=\"https:\/\/lorentzen.ch\/wp-content\/uploads\/2025\/09\/image-3.png 968w, https:\/\/lorentzen.ch\/wp-content\/uploads\/2025\/09\/image-3-300x213.png 300w, https:\/\/lorentzen.ch\/wp-content\/uploads\/2025\/09\/image-3-768x546.png 768w\" sizes=\"auto, (max-width: 968px) 100vw, 968px\" \/><figcaption class=\"wp-element-caption\">SHAP interaction strength for the XGBoost model (single variables reflect SHAP main effect strength). <\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"694\" src=\"https:\/\/lorentzen.ch\/wp-content\/uploads\/2025\/09\/image-4-1024x694.png\" alt=\"\" class=\"wp-image-1993\" srcset=\"https:\/\/lorentzen.ch\/wp-content\/uploads\/2025\/09\/image-4-1024x694.png 1024w, https:\/\/lorentzen.ch\/wp-content\/uploads\/2025\/09\/image-4-300x203.png 300w, https:\/\/lorentzen.ch\/wp-content\/uploads\/2025\/09\/image-4-768x521.png 768w, https:\/\/lorentzen.ch\/wp-content\/uploads\/2025\/09\/image-4.png 1320w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">SHAP interaction effects with smoking for the XGBoost model, including the main effect of smoking<\/figcaption><\/figure>\n\n\n\n<p>Keep an eye on these two packages for further improvements&#8230; \ud83d\ude42<\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/lorentzenchr\/notebooks\/blob\/master\/blogposts\/2025-09-20%20recent%20updates%20shapviz%20kernelshap.R\">R script<\/a><\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recent updates to the two SHAP packages<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[27,16,17,9],"tags":[5],"class_list":["post-1987","post","type-post","status-publish","format-standard","hentry","category-data","category-machine-learning","category-programming","category-statistics","tag-r"],"featured_image_src":null,"author_info":{"display_name":"Michael Mayer","author_link":"https:\/\/lorentzen.ch\/index.php\/author\/michael\/"},"_links":{"self":[{"href":"https:\/\/lorentzen.ch\/index.php\/wp-json\/wp\/v2\/posts\/1987","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/lorentzen.ch\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/lorentzen.ch\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/lorentzen.ch\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/lorentzen.ch\/index.php\/wp-json\/wp\/v2\/comments?post=1987"}],"version-history":[{"count":3,"href":"https:\/\/lorentzen.ch\/index.php\/wp-json\/wp\/v2\/posts\/1987\/revisions"}],"predecessor-version":[{"id":2066,"href":"https:\/\/lorentzen.ch\/index.php\/wp-json\/wp\/v2\/posts\/1987\/revisions\/2066"}],"wp:attachment":[{"href":"https:\/\/lorentzen.ch\/index.php\/wp-json\/wp\/v2\/media?parent=1987"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/lorentzen.ch\/index.php\/wp-json\/wp\/v2\/categories?post=1987"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/lorentzen.ch\/index.php\/wp-json\/wp\/v2\/tags?post=1987"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}