Note
Go to the end to download the full example code.
Shapley Additive Global Importance (SAGE) example#
In this example, we demonstrate how to measure feature importance using SAGE Covert et al.[1] on the diabetes dataset. Read more in the User Guide. For this example, we use the marginal version of SAGE, which limits the computational cost. To further reduce the computational cost, Shapley values are estimated using a Monte Carlo approximation. Only a subset of all possible feature coalitions is sampled. This is controlled by the n_subsets parameter. Finally, the expectation over the marginal distribution is also approximated using n_permutations.
LightGBM example on the bike sharing dataset#
We demonstrate how to use SAGE on the bike sharing dataset. We fit a LightGBM model and compute its \(R^2\) score on a held-out test set.
import numpy as np
from sklearn.datasets import fetch_openml
from sklearn.ensemble import HistGradientBoostingRegressor
from sklearn.metrics import r2_score
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import OrdinalEncoder
bike_sharing = fetch_openml("Bike_Sharing_Demand", version=2, as_frame=True)
df = bike_sharing.frame
df = df[df["year"] == 0].drop(columns=["year"])
X = df.drop(columns=["count"]).to_numpy()
X = OrdinalEncoder().fit_transform(X)
y = df["count"].to_numpy()
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
model = HistGradientBoostingRegressor(random_state=0, max_depth=5)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print("R2 score:", r2_score(y_test, y_pred))
R2 score: 0.9283833661677658
SAGE feature importance#
We compute the SAGE feature importance for the fitted model. To keep the computational cost tractable, we use a subset of the test set to compute the SAGE values. Finally, the SAGE values are plotted using the plot_importance function.
import matplotlib.pyplot as plt
from hidimstat import SAGE
sage = SAGE(
model,
n_subsets=512,
n_permutations=10,
random_state=0,
n_jobs=8,
)
sage.fit(X_train)
subsample_size = 1024
rng = np.random.default_rng(0)
subsample_ids = rng.choice(len(X_test), size=subsample_size, replace=False)
sage.importance(X_test[subsample_ids], y_test[subsample_ids])
ax = sage.plot_importance(
feature_names=df.drop(columns=["count"]).columns.tolist(),
color="tab:purple",
)
ax.semilogx()
plt.tight_layout()
plt.show()

0%| | 0/1925 [00:00<?, ?it/s]
1%| | 16/1925 [00:04<09:46, 3.25it/s]
1%| | 24/1925 [00:05<06:32, 4.84it/s]
2%|▏ | 32/1925 [00:05<04:29, 7.02it/s]
2%|▏ | 40/1925 [00:05<03:14, 9.71it/s]
2%|▏ | 48/1925 [00:06<02:25, 12.87it/s]
3%|▎ | 56/1925 [00:06<01:56, 16.00it/s]
3%|▎ | 64/1925 [00:06<01:37, 19.18it/s]
4%|▎ | 72/1925 [00:06<01:19, 23.21it/s]
4%|▍ | 80/1925 [00:07<01:09, 26.63it/s]
5%|▍ | 88/1925 [00:07<01:03, 29.00it/s]
5%|▍ | 96/1925 [00:07<00:59, 30.68it/s]
5%|▌ | 104/1925 [00:07<00:57, 31.41it/s]
6%|▌ | 112/1925 [00:07<00:56, 32.08it/s]
6%|▌ | 120/1925 [00:08<00:54, 32.99it/s]
7%|▋ | 128/1925 [00:08<00:52, 34.31it/s]
7%|▋ | 136/1925 [00:08<00:53, 33.32it/s]
7%|▋ | 144/1925 [00:08<00:53, 33.51it/s]
8%|▊ | 152/1925 [00:09<00:48, 36.24it/s]
8%|▊ | 160/1925 [00:09<00:48, 36.24it/s]
9%|▊ | 168/1925 [00:09<00:48, 36.19it/s]
9%|▉ | 176/1925 [00:09<00:47, 37.10it/s]
10%|▉ | 184/1925 [00:09<00:46, 37.09it/s]
10%|▉ | 192/1925 [00:10<00:46, 37.06it/s]
10%|█ | 200/1925 [00:10<00:45, 37.69it/s]
11%|█ | 208/1925 [00:10<00:45, 37.57it/s]
11%|█ | 216/1925 [00:10<00:45, 37.45it/s]
12%|█▏ | 224/1925 [00:11<00:49, 34.59it/s]
12%|█▏ | 232/1925 [00:11<00:50, 33.28it/s]
12%|█▏ | 240/1925 [00:11<00:47, 35.55it/s]
13%|█▎ | 248/1925 [00:11<00:52, 31.90it/s]
13%|█▎ | 256/1925 [00:11<00:47, 35.50it/s]
14%|█▎ | 264/1925 [00:12<00:48, 34.45it/s]
14%|█▍ | 272/1925 [00:12<00:49, 33.14it/s]
15%|█▍ | 280/1925 [00:12<00:47, 34.98it/s]
15%|█▍ | 288/1925 [00:12<00:49, 33.27it/s]
15%|█▌ | 296/1925 [00:13<00:47, 34.59it/s]
16%|█▌ | 304/1925 [00:13<00:47, 34.26it/s]
16%|█▌ | 312/1925 [00:13<00:47, 34.21it/s]
17%|█▋ | 320/1925 [00:13<00:45, 35.10it/s]
17%|█▋ | 328/1925 [00:14<00:44, 35.52it/s]
17%|█▋ | 336/1925 [00:14<00:45, 34.59it/s]
18%|█▊ | 344/1925 [00:14<00:45, 34.62it/s]
18%|█▊ | 352/1925 [00:14<00:45, 34.40it/s]
19%|█▊ | 360/1925 [00:15<00:45, 34.12it/s]
19%|█▉ | 368/1925 [00:15<00:43, 36.10it/s]
20%|█▉ | 376/1925 [00:15<00:42, 36.17it/s]
20%|█▉ | 384/1925 [00:15<00:42, 36.14it/s]
20%|██ | 392/1925 [00:15<00:43, 35.31it/s]
21%|██ | 400/1925 [00:16<00:41, 37.18it/s]
21%|██ | 408/1925 [00:16<00:41, 36.22it/s]
22%|██▏ | 416/1925 [00:16<00:40, 37.13it/s]
22%|██▏ | 424/1925 [00:16<00:39, 38.17it/s]
22%|██▏ | 432/1925 [00:16<00:38, 38.30it/s]
23%|██▎ | 440/1925 [00:17<00:40, 37.00it/s]
23%|██▎ | 448/1925 [00:17<00:42, 35.14it/s]
24%|██▎ | 456/1925 [00:17<00:40, 36.43it/s]
24%|██▍ | 464/1925 [00:17<00:39, 36.94it/s]
25%|██▍ | 472/1925 [00:18<00:41, 35.30it/s]
25%|██▍ | 480/1925 [00:18<00:41, 34.69it/s]
25%|██▌ | 488/1925 [00:18<00:43, 33.12it/s]
26%|██▌ | 496/1925 [00:18<00:41, 34.47it/s]
26%|██▌ | 504/1925 [00:19<00:40, 34.77it/s]
27%|██▋ | 512/1925 [00:19<00:40, 34.80it/s]
27%|██▋ | 520/1925 [00:19<00:39, 35.30it/s]
27%|██▋ | 528/1925 [00:19<00:42, 33.14it/s]
28%|██▊ | 536/1925 [00:19<00:39, 35.49it/s]
28%|██▊ | 544/1925 [00:20<00:37, 36.38it/s]
29%|██▊ | 552/1925 [00:20<00:40, 33.71it/s]
29%|██▉ | 560/1925 [00:20<00:40, 33.94it/s]
30%|██▉ | 568/1925 [00:20<00:39, 34.76it/s]
30%|██▉ | 576/1925 [00:21<00:37, 35.80it/s]
30%|███ | 584/1925 [00:21<00:37, 35.69it/s]
31%|███ | 592/1925 [00:21<00:39, 34.12it/s]
31%|███ | 600/1925 [00:21<00:37, 35.45it/s]
32%|███▏ | 608/1925 [00:22<00:38, 34.18it/s]
32%|███▏ | 616/1925 [00:22<00:38, 34.23it/s]
32%|███▏ | 624/1925 [00:22<00:39, 33.14it/s]
33%|███▎ | 632/1925 [00:22<00:39, 33.10it/s]
33%|███▎ | 640/1925 [00:22<00:37, 34.39it/s]
34%|███▎ | 648/1925 [00:23<00:38, 32.89it/s]
34%|███▍ | 656/1925 [00:23<00:35, 35.78it/s]
34%|███▍ | 664/1925 [00:23<00:35, 35.67it/s]
35%|███▍ | 672/1925 [00:23<00:36, 34.80it/s]
35%|███▌ | 680/1925 [00:24<00:36, 34.12it/s]
36%|███▌ | 688/1925 [00:24<00:35, 34.72it/s]
36%|███▌ | 696/1925 [00:24<00:34, 35.89it/s]
37%|███▋ | 704/1925 [00:24<00:34, 35.52it/s]
37%|███▋ | 712/1925 [00:24<00:32, 37.46it/s]
37%|███▋ | 720/1925 [00:25<00:30, 39.45it/s]
38%|███▊ | 728/1925 [00:25<00:28, 42.48it/s]
38%|███▊ | 736/1925 [00:25<00:26, 44.41it/s]
39%|███▊ | 744/1925 [00:25<00:25, 45.69it/s]
39%|███▉ | 752/1925 [00:25<00:24, 47.38it/s]
39%|███▉ | 760/1925 [00:25<00:23, 49.32it/s]
40%|███▉ | 768/1925 [00:26<00:23, 49.76it/s]
40%|████ | 776/1925 [00:26<00:23, 48.99it/s]
41%|████ | 784/1925 [00:26<00:22, 50.51it/s]
41%|████ | 792/1925 [00:26<00:24, 46.70it/s]
42%|████▏ | 800/1925 [00:26<00:24, 46.06it/s]
42%|████▏ | 808/1925 [00:26<00:23, 47.76it/s]
42%|████▏ | 816/1925 [00:27<00:23, 47.76it/s]
43%|████▎ | 824/1925 [00:27<00:24, 45.58it/s]
43%|████▎ | 832/1925 [00:27<00:23, 46.87it/s]
44%|████▎ | 840/1925 [00:27<00:27, 39.63it/s]
44%|████▍ | 848/1925 [00:27<00:24, 43.92it/s]
44%|████▍ | 856/1925 [00:28<00:26, 40.77it/s]
45%|████▍ | 864/1925 [00:28<00:25, 40.92it/s]
45%|████▌ | 872/1925 [00:28<00:25, 41.85it/s]
46%|████▌ | 880/1925 [00:28<00:25, 41.34it/s]
46%|████▌ | 888/1925 [00:28<00:27, 37.56it/s]
47%|████▋ | 896/1925 [00:29<00:25, 40.22it/s]
47%|████▋ | 904/1925 [00:29<00:26, 38.21it/s]
47%|████▋ | 912/1925 [00:29<00:26, 38.87it/s]
48%|████▊ | 920/1925 [00:29<00:24, 40.60it/s]
48%|████▊ | 928/1925 [00:29<00:24, 41.43it/s]
49%|████▊ | 936/1925 [00:30<00:25, 39.20it/s]
49%|████▉ | 944/1925 [00:30<00:25, 37.87it/s]
49%|████▉ | 952/1925 [00:30<00:25, 38.00it/s]
50%|████▉ | 960/1925 [00:30<00:25, 38.43it/s]
50%|█████ | 968/1925 [00:30<00:24, 39.04it/s]
51%|█████ | 976/1925 [00:31<00:26, 35.74it/s]
51%|█████ | 984/1925 [00:31<00:25, 36.81it/s]
52%|█████▏ | 992/1925 [00:31<00:25, 36.11it/s]
52%|█████▏ | 1000/1925 [00:31<00:23, 39.42it/s]
52%|█████▏ | 1008/1925 [00:32<00:24, 37.25it/s]
53%|█████▎ | 1016/1925 [00:32<00:26, 34.82it/s]
53%|█████▎ | 1024/1925 [00:32<00:25, 35.80it/s]
54%|█████▎ | 1032/1925 [00:32<00:24, 36.05it/s]
54%|█████▍ | 1040/1925 [00:32<00:24, 36.36it/s]
54%|█████▍ | 1048/1925 [00:33<00:26, 33.63it/s]
55%|█████▍ | 1056/1925 [00:33<00:25, 33.97it/s]
55%|█████▌ | 1064/1925 [00:33<00:26, 32.87it/s]
56%|█████▌ | 1072/1925 [00:33<00:26, 32.00it/s]
56%|█████▌ | 1080/1925 [00:34<00:24, 33.83it/s]
57%|█████▋ | 1088/1925 [00:34<00:25, 32.78it/s]
57%|█████▋ | 1096/1925 [00:34<00:24, 33.19it/s]
57%|█████▋ | 1104/1925 [00:34<00:23, 35.11it/s]
58%|█████▊ | 1112/1925 [00:35<00:24, 33.82it/s]
58%|█████▊ | 1120/1925 [00:35<00:23, 34.54it/s]
59%|█████▊ | 1128/1925 [00:35<00:22, 34.91it/s]
59%|█████▉ | 1136/1925 [00:35<00:23, 33.75it/s]
59%|█████▉ | 1144/1925 [00:36<00:21, 36.12it/s]
60%|█████▉ | 1152/1925 [00:36<00:22, 34.84it/s]
60%|██████ | 1160/1925 [00:36<00:21, 35.45it/s]
61%|██████ | 1168/1925 [00:36<00:22, 34.27it/s]
61%|██████ | 1176/1925 [00:36<00:20, 36.16it/s]
62%|██████▏ | 1184/1925 [00:37<00:21, 35.29it/s]
62%|██████▏ | 1192/1925 [00:37<00:21, 34.82it/s]
62%|██████▏ | 1200/1925 [00:37<00:21, 34.42it/s]
63%|██████▎ | 1208/1925 [00:37<00:21, 33.60it/s]
63%|██████▎ | 1216/1925 [00:38<00:19, 35.95it/s]
64%|██████▎ | 1224/1925 [00:38<00:20, 34.05it/s]
64%|██████▍ | 1232/1925 [00:38<00:20, 34.62it/s]
64%|██████▍ | 1240/1925 [00:38<00:19, 35.90it/s]
65%|██████▍ | 1248/1925 [00:39<00:18, 35.69it/s]
65%|██████▌ | 1256/1925 [00:39<00:18, 35.71it/s]
66%|██████▌ | 1264/1925 [00:39<00:19, 33.89it/s]
66%|██████▌ | 1272/1925 [00:39<00:19, 33.75it/s]
66%|██████▋ | 1280/1925 [00:39<00:18, 35.03it/s]
67%|██████▋ | 1288/1925 [00:40<00:18, 34.74it/s]
67%|██████▋ | 1296/1925 [00:40<00:18, 33.25it/s]
68%|██████▊ | 1304/1925 [00:40<00:17, 34.57it/s]
68%|██████▊ | 1312/1925 [00:40<00:18, 33.15it/s]
69%|██████▊ | 1320/1925 [00:41<00:17, 35.08it/s]
69%|██████▉ | 1328/1925 [00:41<00:17, 33.63it/s]
69%|██████▉ | 1336/1925 [00:41<00:17, 34.08it/s]
70%|██████▉ | 1344/1925 [00:41<00:16, 34.35it/s]
70%|███████ | 1352/1925 [00:42<00:17, 33.20it/s]
71%|███████ | 1360/1925 [00:42<00:17, 31.64it/s]
71%|███████ | 1368/1925 [00:42<00:17, 32.70it/s]
71%|███████▏ | 1376/1925 [00:42<00:16, 33.24it/s]
72%|███████▏ | 1384/1925 [00:43<00:16, 32.62it/s]
72%|███████▏ | 1392/1925 [00:43<00:16, 32.16it/s]
73%|███████▎ | 1400/1925 [00:43<00:15, 33.55it/s]
73%|███████▎ | 1408/1925 [00:43<00:15, 32.55it/s]
74%|███████▎ | 1416/1925 [00:44<00:15, 32.44it/s]
74%|███████▍ | 1424/1925 [00:44<00:14, 33.90it/s]
74%|███████▍ | 1432/1925 [00:44<00:15, 32.53it/s]
75%|███████▍ | 1440/1925 [00:44<00:14, 32.90it/s]
75%|███████▌ | 1448/1925 [00:44<00:13, 35.69it/s]
76%|███████▌ | 1456/1925 [00:45<00:13, 35.53it/s]
76%|███████▌ | 1464/1925 [00:45<00:12, 35.70it/s]
76%|███████▋ | 1472/1925 [00:45<00:13, 33.79it/s]
77%|███████▋ | 1480/1925 [00:45<00:13, 33.79it/s]
77%|███████▋ | 1488/1925 [00:46<00:12, 35.15it/s]
78%|███████▊ | 1496/1925 [00:46<00:11, 35.89it/s]
78%|███████▊ | 1504/1925 [00:46<00:11, 36.89it/s]
79%|███████▊ | 1512/1925 [00:46<00:12, 33.92it/s]
79%|███████▉ | 1520/1925 [00:47<00:11, 35.23it/s]
79%|███████▉ | 1528/1925 [00:47<00:11, 34.74it/s]
80%|███████▉ | 1536/1925 [00:47<00:10, 36.19it/s]
80%|████████ | 1544/1925 [00:47<00:11, 34.49it/s]
81%|████████ | 1552/1925 [00:47<00:10, 35.04it/s]
81%|████████ | 1560/1925 [00:48<00:10, 35.28it/s]
81%|████████▏ | 1568/1925 [00:48<00:10, 34.25it/s]
82%|████████▏ | 1576/1925 [00:48<00:09, 35.71it/s]
82%|████████▏ | 1584/1925 [00:48<00:10, 34.08it/s]
83%|████████▎ | 1592/1925 [00:49<00:09, 34.74it/s]
83%|████████▎ | 1600/1925 [00:49<00:09, 35.73it/s]
84%|████████▎ | 1608/1925 [00:49<00:08, 35.54it/s]
84%|████████▍ | 1616/1925 [00:49<00:09, 34.17it/s]
84%|████████▍ | 1624/1925 [00:50<00:09, 32.91it/s]
85%|████████▍ | 1632/1925 [00:50<00:08, 34.89it/s]
85%|████████▌ | 1640/1925 [00:50<00:08, 31.79it/s]
86%|████████▌ | 1648/1925 [00:50<00:08, 32.53it/s]
86%|████████▌ | 1656/1925 [00:50<00:07, 36.42it/s]
86%|████████▋ | 1664/1925 [00:51<00:07, 34.91it/s]
87%|████████▋ | 1672/1925 [00:51<00:07, 34.44it/s]
87%|████████▋ | 1680/1925 [00:51<00:07, 34.85it/s]
88%|████████▊ | 1688/1925 [00:51<00:07, 33.64it/s]
88%|████████▊ | 1696/1925 [00:52<00:06, 33.67it/s]
89%|████████▊ | 1704/1925 [00:52<00:06, 34.84it/s]
89%|████████▉ | 1712/1925 [00:52<00:06, 32.54it/s]
89%|████████▉ | 1720/1925 [00:52<00:06, 34.06it/s]
90%|████████▉ | 1728/1925 [00:53<00:05, 34.28it/s]
90%|█████████ | 1736/1925 [00:53<00:05, 35.48it/s]
91%|█████████ | 1744/1925 [00:53<00:04, 37.39it/s]
91%|█████████ | 1752/1925 [00:53<00:04, 36.76it/s]
91%|█████████▏| 1760/1925 [00:53<00:04, 37.05it/s]
92%|█████████▏| 1768/1925 [00:54<00:04, 37.17it/s]
92%|█████████▏| 1776/1925 [00:54<00:04, 35.45it/s]
93%|█████████▎| 1784/1925 [00:54<00:03, 37.41it/s]
93%|█████████▎| 1792/1925 [00:54<00:03, 37.38it/s]
94%|█████████▎| 1800/1925 [00:54<00:03, 37.88it/s]
94%|█████████▍| 1808/1925 [00:55<00:03, 37.56it/s]
94%|█████████▍| 1816/1925 [00:55<00:03, 34.71it/s]
95%|█████████▍| 1824/1925 [00:55<00:02, 34.99it/s]
95%|█████████▌| 1832/1925 [00:55<00:02, 34.30it/s]
96%|█████████▌| 1840/1925 [00:56<00:02, 33.33it/s]
96%|█████████▌| 1848/1925 [00:56<00:02, 34.27it/s]
96%|█████████▋| 1856/1925 [00:56<00:01, 34.69it/s]
97%|█████████▋| 1864/1925 [00:56<00:01, 35.04it/s]
97%|█████████▋| 1872/1925 [00:57<00:01, 34.99it/s]
98%|█████████▊| 1880/1925 [00:57<00:01, 35.36it/s]
98%|█████████▊| 1888/1925 [00:57<00:01, 35.85it/s]
98%|█████████▊| 1896/1925 [00:57<00:00, 36.69it/s]
99%|█████████▉| 1904/1925 [00:57<00:00, 35.64it/s]
99%|█████████▉| 1912/1925 [00:58<00:00, 36.29it/s]
100%|█████████▉| 1920/1925 [00:58<00:00, 35.97it/s]
100%|██████████| 1925/1925 [00:58<00:00, 32.96it/s]
This analysis reveals that the hour of the day is the most important feature
for predicting bike sharing demand. This example also illustrates a specific
property of SAGE: it tends to distribute importance over correlated features.
This can be seen for the temperature and “feel temperature” features,
which have a near-perfect correlation, as seen in the correlation matrix below,
and are both assigned similar importance.
This property is a consequence of the axioms of Shapley values, and contrasts
with other feature importance methods, such as
LOCO or CFI.
corr_mat = np.corrcoef(X_train, rowvar=False)
fig, ax = plt.subplots()
ax.imshow(corr_mat, cmap="coolwarm", vmin=-1, vmax=1)
ax.set_xticks(np.arange(corr_mat.shape[0]))
ax.set_yticks(np.arange(corr_mat.shape[0]))
ax.set_xticklabels(
df.drop(columns=["count"]).columns.tolist(), rotation=45, ha="right"
)
ax.set_yticklabels(df.drop(columns=["count"]).columns.tolist())
cbar = ax.figure.colorbar(
ax.imshow(corr_mat, cmap="coolwarm", vmin=-1, vmax=1), label="correlation"
)
plt.tight_layout()
plt.show()

References#
Total running time of the script: (1 minutes 4.070 seconds)
Estimated memory usage: 221 MB