Api partition
Summary
Regional effects are found on a global effect object: find_regions(feature)
searches for heterogeneity-reducing subregions of a feature and returns a
Partition — a value object, not stored state (design contract R12).
- create and
.fit()a global effect method (PDP,RHALE,ALE,ShapDP,DerPDP) partition = effect.find_regions(feature)to search for subregionspartition.show()to print the partition treepartition.plot(idx)to plot the effect within regionidxpartition.eval(idx, xs)/partition.eval_heter(idx, xs)to evaluate it
Usage
# set up the input
X = ... # input data
predict = ... # model to be explained
jacobian = ... # jacobian of the model (RHALE / DerPDP only)
-
Create and fit a global effect method:
effect = effector.PDP(data=X, model=predict) effect.fit(features=[0, 1])effect = effector.RHALE(data=X, model=predict, model_jac=jacobian) effect.fit(features=[0, 1])effect = effector.ShapDP(data=X, model=predict, nof_instances=500) effect.fit(features=[0, 1])effect = effector.ALE(data=X, model=predict) effect.fit(features=[0, 1])effect = effector.DerPDP(data=X, model=predict, model_jac=jacobian) effect.fit(features=[0, 1]) -
Search for subregions of a feature (returns a
Partition):find_regions(feature, *, finder="best", candidate_conditioning_features="all")Customize the search
finderaccepts a name ("best"/"best_level_wise") or a configured partitioner instance:finder = effector.space_partitioning.Best( min_heterogeneity_decrease_pcg=0.3, # drop threshold (default: 0.1) max_depth=1, # max split levels (default: 2) ) partition = effect.find_regions(0, finder=finder) -
Print the partition tree:
partition.show()Example Output
Feature 3 - Full partition tree: 🌳 Full Tree Structure: ─────────────────────── hr 🔹 [id: 0 | heter: 0.43 | inst: 3476 | w: 1.00] workingday = 0.00 🔹 [id: 1 | heter: 0.36 | inst: 1129 | w: 0.32] temp < 6.50 🔹 [id: 3 | heter: 0.17 | inst: 568 | w: 0.16] temp ≥ 6.50 🔹 [id: 4 | heter: 0.21 | inst: 561 | w: 0.16] workingday = 1.00 🔹 [id: 2 | heter: 0.28 | inst: 2347 | w: 0.68] temp < 6.50 🔹 [id: 5 | heter: 0.19 | inst: 953 | w: 0.27] temp ≥ 6.50 🔹 [id: 6 | heter: 0.20 | inst: 1394 | w: 0.40] -------------------------------------------------- Feature 3 - Statistics per tree level: 🌳 Tree Summary: ───────────────── Level 0🔹heter: 0.43 Level 1🔹heter: 0.31 | 🔻0.12 (28.15%) Level 2🔹heter: 0.19 | 🔻0.11 (37.10%) -
Plot the effect within a region:
partition.plot(idx)—idxis a region id from the tree (0is the full data). -
Evaluate the effect / heterogeneity within a region:
y = partition.eval(idx, xs) # mean effect within region idx h = partition.eval_heter(idx, xs) # heterogeneity within region idx
Rules
Every Region is defined by a Rule — a normalized conjunction of
per-feature conditions (effector.rules). Membership, display, and
serialization all derive from the same rule, and rules are also a direct
query surface:
# ad-hoc subregion queries, no Partition needed
effect.eval(feature, xs, rule="temp < 6.5 and workingday == 0")
effect.heter_score(feature, rule="temp < 6.5")
# user-authored partitions — same object find_regions returns
part = effector.Partition.from_rules(
["temp < 6.5", "temp >= 6.5"], effect=effect, feature=3
)
# serialization round-trip: rules travel, masks are recomputed on bind
d = part.to_dict() # small: rules + stats, no masks
restored = effector.Partition.from_dict(d).bind(effect)
API
effector.global_effect.GlobalEffectBase.find_regions(feature=None, *, features=None, finder='best', candidate_conditioning_features='all')
Search for subregions that resolve a feature's heterogeneity.
part = pdp.find_regions("hr") # one feature -> Partition
part.show() # the tree + level stats
pdp.plot("hr", rule=part.leaves[0].rule) # drill into a leaf
parts = pdp.find_regions(features="heterogeneous") # several -> {name: Partition}
effector.plot_triage(pdp, partitions=parts) # the before/after picture
A query, not a mutation (R12)
The result is a value — nothing is stored on the effect. Don't like a partition? Search again with different finder kwargs; nothing needs resetting.
Model-free
Every candidate split is scored by heter_score(feature, mask)
on the cached local effects — zero model calls, whatever the grid
size. Binning/scope are those the feature was fitted with,
replayed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
feature
|
Union[int, str, None]
|
index or name of the one feature to partition
(→ |
None
|
features
|
Union[list, str, None]
|
several at once — a list, |
None
|
finder
|
|
'best'
|
|
candidate_conditioning_features
|
features allowed to define splits
( |
'all'
|
Returns:
| Type | Description |
|---|---|
|
a |
|
|
with |
Source code in effector/global_effect.py
815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 | |
effector.partition.Partition(regions, *, feature, feature_name, finder_name, feature_names=None)
An ordered set of Regions covering one feature's domain — a value, not stored state.
part = pdp.find_regions("hr") # a Partition, bound to the effect
part.show() # tree + per-level stats
part.plot(1) # the effect inside region 1
regions[0] is always the root — full data, weight 1.0, the global effect
itself — and the leaves partition it: pairwise disjoint, jointly covering
(verified whenever masks are present). Each region's identity is its
Rule; masks are derived caches, recomputed and re-verified by bind.
Heterogeneity = heter_score(feature, mask)
The heterogeneity a region reports is exactly the effect's
heter_score(feature, mask=region_mask) — the same scalar the
region finders minimize.
Methods:
| Name | Description |
|---|---|
mask |
The boolean mask of region |
label |
Human-readable label for region |
show |
Print the partition tree and per-level heterogeneity statistics. |
show_axes |
Print the leaves as a partition of the conditioning axes. |
eval |
The mean effect within region |
eval_heter |
The heterogeneity curve within region |
plot |
Plot the effect within region |
bind |
Attach an effect: recompute every region's mask from its rule and verify it. |
from_rules |
Build a partition from your own rules — no search. |
to_dict |
Serialize to a plain JSON-able dict. |
Attributes:
| Name | Type | Description |
|---|---|---|
leaves |
The regions that are no other region's parent — the finest partition. |
Source code in effector/partition.py
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | |
leaves
property
The regions that are no other region's parent — the finest partition.
A one-region partition's only leaf is the root.
mask(idx)
The boolean mask of region idx — a copy, safe to mutate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
idx
|
region index (0 = root). |
required |
Returns:
| Type | Description |
|---|---|
|
boolean array |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
the region has no mask (unbound partition — rebuilt
from |
Source code in effector/partition.py
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 | |
label(idx, scale_x_list=None)
Human-readable label for region idx.
Root -> the feature name; otherwise "<feature> where <formatted rule>".
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
idx
|
region index. |
required | |
scale_x_list
|
optional per-feature |
None
|
Returns:
| Type | Description |
|---|---|
|
the label string. |
Source code in effector/partition.py
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 | |
show(scale_x_list=None)
Print the partition tree and per-level heterogeneity statistics.
Each node shows its splitting condition plus a
[id | heter | inst | w] chip; the summary shows the heterogeneity
drop per level. Works on unbound partitions too.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
scale_x_list
|
optional per-feature |
None
|
Source code in effector/partition.py
376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 | |
show_axes(scale_x_list=None)
Print the leaves as a partition of the conditioning axes.
When the leaves differ on one conditioning feature, print them as a
partition of that axis; on two, as a grid. Anything else falls back
to the tree print (show).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
scale_x_list
|
optional per-feature |
None
|
Source code in effector/partition.py
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 | |
eval(idx, xs, **kwargs)
The mean effect within region idx at positions xs.
Sugar for effect.eval(feature, xs, mask=part.mask(idx)) —
re-summarized from cached local effects, zero model calls.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
idx
|
region index (0 = root = the global effect). |
required | |
xs
|
where to evaluate, |
required | |
**kwargs
|
forwarded to |
{}
|
Returns:
| Type | Description |
|---|---|
|
the mean effect at |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
the partition is unbound; call |
Source code in effector/partition.py
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 | |
eval_heter(idx, xs)
The heterogeneity curve within region idx at positions xs.
Sugar for effect.eval_heter(feature, xs, mask=part.mask(idx)) —
model-free.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
idx
|
region index (0 = root = the global effect). |
required | |
xs
|
where to evaluate, |
required |
Returns:
| Type | Description |
|---|---|
|
the heterogeneity curve at |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
the partition is unbound; call |
Source code in effector/partition.py
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 | |
plot(idx, scale_x_list=None, **plot_kwargs)
Plot the effect within region idx, titled with the region's rule.
Sugar for effect.plot(feature, mask=part.mask(idx), ...) with the
region's label as the feature label.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
idx
|
region index (0 = root = the global effect). |
required | |
scale_x_list
|
optional per-feature |
None
|
|
**plot_kwargs
|
forwarded to |
{}
|
Raises:
| Type | Description |
|---|---|
RuntimeError
|
the partition is unbound; call |
Source code in effector/partition.py
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 | |
bind(effect)
Attach an effect: recompute every region's mask from its rule and verify it.
part = effector.Partition.from_dict(d).bind(pdp) # live again
Masks are recomputed from the rules against effect.data, then checked
against the stored evidence — the finder's mask when present (the
find_regions path), else the serialized nof_instances (the
from_dict path). This is what makes a deserialized partition safely
re-attachable.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
effect
|
a fitted global effect whose |
required |
Returns:
| Type | Description |
|---|---|
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
a rule selects different instances than the stored
evidence — the effect's data differs from the data the
partition was built on (check |
Source code in effector/partition.py
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | |
from_rules(rules, *, effect, feature, finder_name='user')
classmethod
Build a partition from your own rules — no search.
part = effector.Partition.from_rules(
["workingday == 0", "workingday == 1"], effect=pdp, feature=3
)
part.plot(1)
Strings are parsed with the effect's metadata (Rule.parse); stats
(heterogeneity, counts, weights) are stamped from the effect's cached
local effects — zero model calls — and the result comes back bound.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rules
|
|
required | |
effect
|
a fitted global effect providing data and metadata. |
required | |
feature
|
index of the feature of interest. |
required | |
finder_name
|
label recorded on the partition (default |
'user'
|
Returns:
| Type | Description |
|---|---|
|
a bound |
Raises:
| Type | Description |
|---|---|
ValueError
|
the rules do not partition the data — some instance is covered more than once or not at all. |
Source code in effector/partition.py
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 243 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 | |
to_dict()
Serialize to a plain JSON-able dict.
Rules + stats only
Each region's rule and stamped statistics are serialized — never
masks, never the data, never the effect or the model.
from_dict(...) + bind(effect) restore the rest.
Returns:
| Type | Description |
|---|---|
|
a dict with |
|
|
per region (rule, heterogeneity, counts, weight, tree links). |
Source code in effector/partition.py
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 | |
effector.partition.Region(idx, name, rule, heterogeneity, nof_instances, weight, level=0, parent_idx=None, mask=None)
dataclass
One subregion of a feature's domain — a frozen value whose identity is its Rule.
idx == 0 is the root: full data, weight 1.0, the global effect itself.
heterogeneity is effect.heter_score(feature, mask) within the region,
stamped when the region was built. mask is a derived cache — the rule
applied to one dataset — and is None on a deserialized region until
Partition.bind recomputes it.
Attributes:
| Name | Type | Description |
|---|---|---|
idx |
int
|
position in the partition; 0 is the root. |
name |
str
|
display name — the feature name, or |
rule |
Rule
|
the |
heterogeneity |
float
|
|
nof_instances |
int
|
how many instances the rule selects. |
weight |
float
|
|
level |
int
|
depth in the partition tree (root = 0). |
parent_idx |
Optional[int]
|
index of the parent region; |
mask |
Optional[ndarray]
|
boolean |
effector.rules.Rule(conditions=())
A normalized conjunction of per-feature conditions — a region's identity.
rule = effector.Rule.parse("temp < 3 and season == 0", ...)
mask = rule.contains(X) # (N,) bool
pdp.plot("hr", rule=rule) # effects accept rules directly
At most one subset per feature (a hyperbox with categorical level sets);
membership, display, and serialization all derive from this one object.
Rules are immutable and hashable; equality is order-insensitive.
Rule({}) is the root rule — it contains everything and formats to
"". A full (unbounded) Interval constrains nothing and is dropped at
construction, so Rule({0: Interval()}) == Rule({}). Emptiness
(is_empty) is representable and never raises; only parse rejects
contradictions, as a courtesy to humans.
Methods:
| Name | Description |
|---|---|
contains |
Which rows of |
intersect |
The conjunction of two rules — subsets on shared features intersect. |
refine |
A new rule with one more condition — sugar for |
format |
Human-readable conjunction. A single condition stays bare |
parse |
Parse |
to_dict |
Serialize to a plain JSON-able dict — a list of per-feature conditions. |
from_dict |
Rebuild a |
Source code in effector/rules.py
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | |
contains(X)
Which rows of X satisfy the rule — the only place rules become masks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
data matrix |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
boolean mask |
Raises:
| Type | Description |
|---|---|
ValueError
|
|
Source code in effector/rules.py
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 | |
intersect(other)
The conjunction of two rules — subsets on shared features intersect.
Returns:
| Type | Description |
|---|---|
'Rule'
|
a new |
Source code in effector/rules.py
336 337 338 339 340 341 342 343 344 345 | |
refine(condition)
A new rule with one more condition — sugar for intersect(Rule([condition])).
Source code in effector/rules.py
347 348 349 | |
format(feature_names=None, scale_x_list=None, category_names=None)
Human-readable conjunction. A single condition stays bare
("temp < 3.00"); two or more are parenthesized:
"(temp < 3.00) and (season = winter)".
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
feature_names
|
display names, position = index; defaults to
|
None
|
|
scale_x_list
|
per-feature |
None
|
|
category_names
|
|
None
|
Returns:
| Type | Description |
|---|---|
str
|
the formatted string; the root rule formats to |
Source code in effector/rules.py
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 | |
parse(text, *, feature_names, feature_types=None, levels=None, category_names=None)
classmethod
Parse "temp < 3 and season != 2 and hr in {7, 8}" into a Rule.
Grammar: clauses joined by and; each clause
<feature_name> <op> <value> with op in < <= > >= == != in
(= is accepted as ==). Continuous features take the four
inequalities; discrete features take ==, in {…}, and !=.
Level names are accepted where category_names covers the feature.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
the rule string. |
required |
feature_names
|
feature display names, position = index. |
required | |
feature_types
|
per-feature types; required for the discrete-only
ops |
None
|
|
levels
|
|
None
|
|
category_names
|
|
None
|
Returns:
| Type | Description |
|---|---|
'Rule'
|
the parsed |
Raises:
| Type | Description |
|---|---|
ValueError
|
unknown feature, unparsable clause, an op unsupported for the feature's type, or contradictory clauses. |
Source code in effector/rules.py
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 | |
to_dict()
Serialize to a plain JSON-able dict — a list of per-feature conditions.
Source code in effector/rules.py
377 378 379 380 381 382 383 | |
from_dict(d)
classmethod
Rebuild a Rule from to_dict() output.
Source code in effector/rules.py
385 386 387 388 | |