Triage, comparison & theme
Summary
Two free functions stand above the engines. They take fitted effect objects, query their public verbs, and draw — they compute nothing themselves and store nothing.
plot_triage is the survey: importance (x) against heterogeneity (y),
one labeled point per feature. Bottom-left is unimportant; bottom-right is
important and fully described by its mean effect; top-right — important and
heterogeneous — is where find_regions should look. With
partitions=effect.find_regions(features="heterogeneous") it becomes the
before/after story: an arrow runs from each partitioned feature's global
point to each leaf point — leaves of a good partition move right (more
decisive) and down (heterogeneity explained).
pdp = effector.PDP(X, model, schema=schema)
effector.plot_triage(pdp) # step (b): the survey
parts = pdp.find_regions(features="heterogeneous") # step (d)
effector.plot_triage(pdp, partitions=parts) # step (e): the arrows
compare is the cross-examination: overlay the mean effect of several
fitted engines — different methods, or even different models over the same
columns — on one feature, always centered.
effector.compare(pdp, rhale, shapdp, feature="temp")
FeatureEffect is the single-model shortcut with the same look: it
ingests once and builds its own engines (same data, subsample, limits, and
schema for every method), so the only difference between the overlaid curves
is the method itself.
fe = effector.FeatureEffect(X, model, schema=schema)
fe.plot("temp", methods=["PDP", "ALE", "RHALE"])
set_theme switches the house matplotlib style for every figure the
package produces:
effector.set_theme("dark") # "light" | "dark" | "paper" | "default"
API
effector.visualization.plot_triage(effect, partitions=None, threshold=None, features='all', title=None, show_plot=True)
The triage plane of one fitted effect: importance (x) against heterogeneity (y), one labeled point per feature.
effector.plot_triage(pdp) # survey every feature
parts = pdp.find_regions(features="heterogeneous")
effector.plot_triage(pdp, partitions=parts) # before/after arrows
Read it as a to-do list: bottom-left is unimportant and honest, the
bottom-right features are important and fully described by their mean
effect, and the top-right corner — important and heterogeneous — is
where the mean effect hides something and find_regions should look.
The before/after story
With partitions, an arrow runs from every partitioned feature's
global point to each of its leaf points (the leaf's
importance/heter_score under its rule, computed model-free from
the caches). Leaves of a good partition land right and down — more
decisive, less heterogeneous.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
effect
|
a fitted effect object ( |
required | |
partitions
|
Union[None, dict]
|
optional |
None
|
threshold
|
Union[None, bool, float]
|
the heterogeneity threshold line. |
None
|
features
|
Union[str, list]
|
which features to plot — |
'all'
|
title
|
Union[None, str]
|
figure title. |
None
|
show_plot
|
bool
|
if |
True
|
Source code in effector/visualization.py
1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 | |
effector.visualization.compare(*effects, feature, labels=None, centering=True, nof_points=100, scale_x=None, scale_y=None, y_limits=None, title=None, show_plot=True)
Overlay the mean effect of several fitted effect objects on one feature.
effector.compare(pdp, ale, rhale, feature="hr")
effector.compare(pdp_a, pdp_b, feature="hr", labels=["model A", "model B"])
The cross-examination verb above the engines: you hold the effect objects
(different methods, or even different models over the same columns);
compare queries each one's eval on a shared grid and overlays the
curves — one theme color per effect. It computes nothing itself and
stores nothing.
Always centered
A comparison is only meaningful for centered effects (each method
uses a different reference level), so centering=False is coerced to
"zero_integral" with a warning.
Derivative units don't mix
DerPDP effects (dy/dx) can only be compared with each other — never
on the same axis as the output-unit methods
(PDP/ALE/RHALE/ShapDP).
The single-model shortcut with the same look is
effector.FeatureEffect(data, model).plot(feature, methods=[...]), which
builds its own engines; compare overlays engines you already have.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*effects
|
two or more fitted effect objects ( |
()
|
|
feature
|
Union[int, str]
|
index or name of the feature to compare on. |
required |
labels
|
Union[None, list]
|
one legend label per effect; defaults to the method display
names, deduped ( |
None
|
centering
|
Union[bool, str]
|
how to center — |
True
|
nof_points
|
int
|
size of the shared grid (continuous features); the grid spans the intersection of the effects' axis limits. |
100
|
scale_x
|
Union[None, dict]
|
|
None
|
scale_y
|
Union[None, dict]
|
same, for the y axis. |
None
|
y_limits
|
Union[None, tuple]
|
|
None
|
title
|
Union[None, str]
|
figure title. |
None
|
show_plot
|
bool
|
if |
True
|
Raises:
| Type | Description |
|---|---|
ValueError
|
fewer than two effects; effects disagree on columns, feature resolution, categorical status, or observed level sets; derivative and output units are mixed; or the axis intervals do not overlap. |
Source code in effector/visualization.py
905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 | |
effector.feature_effect.FeatureEffect(data, model, model_jac=None, *, axis_limits=None, nof_instances=10000, schema=None, random_state=21)
Compare global feature-effect methods on a single figure — one model, one object.
fe = effector.FeatureEffect(X, model, schema=schema)
fe.plot(feature=3) # PDP vs ALE vs RHALE overlaid
fe.plot(feature=3, methods=["PDP", "ShapDP"]) # ShapDP is opt-in (slow)
Holds the shared ingredients (data, model, metadata, axis limits) once
and lazily builds the underlying method objects (PDP, ALE, RHALE,
ShapDP) on demand. eval returns each method's mean effect on a shared
grid; plot overlays them on one axis.
Same background data for every method
The data is filtered to axis_limits and subsampled to
nof_instances once; every method object is built on that identical
subset. The only difference between the curves is the method itself.
DerPDP is not in the pool
It lives in derivative units (dy/dx) and cannot share an axis with the output-unit methods.
To overlay effect objects you already hold — different subsets, even
different models — use effector.compare instead; FeatureEffect always
builds its own.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
the design matrix — a |
required | |
model
|
Callable
|
the black-box model, a numpy->numpy |
required |
model_jac
|
Optional[Callable]
|
the model Jacobian |
None
|
axis_limits
|
Optional[ndarray]
|
|
None
|
nof_instances
|
Union[int, str]
|
number of instances shared across all methods.
|
10000
|
schema
|
Optional[Union[Schema, dict]]
|
input metadata — an
|
None
|
random_state
|
Optional[int]
|
seed for every internal random step (e.g. the shared
|
21
|
Methods:
| Name | Description |
|---|---|
eval |
Evaluate the mean effect of several methods on one shared grid. |
plot |
Overlay the mean effect of several methods for one feature. |
Source code in effector/feature_effect.py
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | |
eval(feature, xs, methods=None, centering=True, method_kwargs=None)
Evaluate the mean effect of several methods on one shared grid.
xs = np.linspace(fe.axis_limits[0, 3], fe.axis_limits[1, 3], 100)
curves = fe.eval(feature=3, xs=xs) # {"PDP": y, "ALE": y, "RHALE": y}
Methods that do not support the feature's type (e.g. RHALE on a
nominal feature) are dropped with a warning.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
feature
|
int
|
index of the feature. |
required |
xs
|
ndarray
|
the grid to evaluate on, shape |
required |
methods
|
Optional[List[str]]
|
list of method names. Supported: |
None
|
centering
|
Union[bool, str]
|
|
True
|
method_kwargs
|
Optional[dict]
|
optional |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
|
Source code in effector/feature_effect.py
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | |
plot(feature, methods=None, centering=True, nof_points=100, scale_x=None, scale_y=None, y_limits=None, show_avg_output=False, method_kwargs=None, show_plot=True)
Overlay the mean effect of several methods for one feature.
fe.plot(feature=3) # PDP vs ALE vs RHALE
fe.plot(feature=3, methods=["PDP", "ALE", "ShapDP"]) # add ShapDP (slow)
The grid is shared: a linspace over the feature's axis limits, or the observed levels for a categorical feature.
Always centered
The comparison is only meaningful for centered effects (each
method uses a different reference level), so centering=False is
coerced to "zero_integral" with a warning.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
feature
|
int
|
index of the feature to plot. |
required |
methods
|
Optional[List[str]]
|
list of method names to compare. Supported: |
None
|
centering
|
Union[bool, str]
|
how to center the curves
|
True
|
nof_points
|
int
|
size of the shared evaluation grid (continuous features). |
100
|
scale_x
|
Optional[dict]
|
|
None
|
scale_y
|
Optional[dict]
|
same, for the y axis. |
None
|
y_limits
|
Optional[List]
|
|
None
|
show_avg_output
|
bool
|
whether to draw the model's average output as a horizontal line. |
False
|
method_kwargs
|
Optional[dict]
|
optional |
None
|
show_plot
|
bool
|
if |
True
|
Source code in effector/feature_effect.py
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 | |
effector.theme.set_theme(name='light')
Activate a house theme for every figure effector draws after this call.
effector.set_theme("dark")
effector.set_theme("default") # back to stock matplotlib
| Name | Look |
|---|---|
"light" |
the default: off-white surface, colorblind-safe palette |
"dark" |
the same palette on a dark surface |
"paper" |
pure white, no grid, 300-dpi savefig — print-ready |
"default" |
restore stock matplotlib rcParams |
Global by design
The chrome (background, font, grid, spines) lives in matplotlib's
global rcParams — matplotlib re-reads those at draw time, so a
local rc_context would revert before render. Palette colors and
chrome switch together for new figures; existing figures keep their
look.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
one of the names above (default |
'light'
|
Raises:
| Type | Description |
|---|---|
ValueError
|
unknown theme name. |
Source code in effector/theme.py
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 | |