Skip to content

Api report

Summary

effector.explain(data, model, ...) runs the whole explanation pipeline in one call and returns a Report — a serializable value (design contract R12):

  1. fit the chosen method once
  2. rank features by importance (R13)
  3. compute the mean-effect + heterogeneity curves for the top-top_k
  4. find_regions on the features whose heter_score clears the threshold

Every model call happens through the single fit; importance, curves, and find_regions are all model-free afterwards.


Usage

import effector

report = effector.explain(X, predict, method="pdp", top_k=5)

report.show()                    # importance-ranked table + region trees
report.plot_importance()         # horizontal bar chart
report.to_html("report.html")    # self-contained page (figures inlined)

d = report.to_dict()             # serialize (no effect needed to round-trip)
report2 = effector.Report.from_dict(d)

API

effector.report.explain(data, model, model_jac=None, *, y=None, schema=None, method='pdp', top_k=5, coverage=0.8, heter_threshold=None, min_r2_gain=0.01, finder='best', candidate_conditioning_features='all', nof_instances=10000, random_state=21)

Run the whole explanation pipeline and return a Report — a value.

report = effector.explain(X, model, method="pdp")
report.show()                     # ledger + ranked table + partition trees
report.to_html("report.html")     # self-contained page

Fits the chosen method once, searches regions on every heterogeneous feature (heter_score >= heter_threshold), greedily selects which splits earn their complexity (select_regions — the CALM chain), and plots the features that carry the analysis: descending final-CALM importance until coverage of the total importance mass is shown, never more than top_k, plus every accepted split feature.

Search wide, display by coverage

The region search and the R² selection run over all heterogeneous features — a feature outside the display cut can still carry the biggest explained-variance gain. coverage/top_k only trim the curve plots; the triage plane and the ranked table always cover every supported feature.

One model touch

All model calls happen through the single fit, plus one prediction pass for the explained-variance denominator (f̂(X), cached on the effect); everything after — importances, curves, find_regions, the surrogate R²s — is model-free, so the call count does not grow with top_k.

Parameters:

Name Type Description Default
data

(N, D) numpy design matrix.

required
model

callable (N, D) -> (N,) — the black box.

required
model_jac

callable (N, D) -> (N, D) Jacobian; required by derivative-based methods ("rhale", "derpdp").

None
y

optional (N,) ground truth aligned with data. When given, the report header states the model's score on the explained subsample — R² for a continuous target, accuracy for a binary one — so the report is self-contained.

None
schema

optional feature schema (names/types/categories).

None
method

effect method — "pdp" (default), "derpdp", "ale", "rhale", or "shapdp" (aliases accepted).

'pdp'
top_k

hard ceiling on how many features get curve plots.

5
coverage

stop plotting once the shown features carry this share of the total importance mass (final-CALM importances, share-of-sum over the supported features; default 0.8). The report states the achieved share.

0.8
heter_threshold

minimum heter_score to enter the region search; None (default) uses the median across the supported features — the same convention as find_regions(features="heterogeneous").

None
min_r2_gain

smallest explained-variance marginal (fraction of Var(f̂), default 0.01 = 1%) a split must add — on top of the splits already applied — to earn a snapshot in the CALM chain and its regional plots; splits below it are skipped as redundant or below-threshold.

0.01
finder

region finder — "best" (default), "best_level_wise", or a configured finder instance.

'best'
candidate_conditioning_features

features allowed to define splits ("all" or a list).

'all'
nof_instances

subsample size the effect is built on.

10000
random_state

seed for the subsample.

21

Returns:

Type Description
Report

a Report bound to the fitted effect — FeatureReports in

Report

final-CALM-importance-descending order, partitions stored as dicts,

Report

an overview (global importance + heterogeneity) over every supported

Report

feature, and explained_variance holding the serialized CALM chain

Report

(CalmSequence.to_dict(): GAM R², per-stage marginal gains, skipped

Report

splits, one snapshot per accepted split).

Source code in effector/report.py
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
def explain(
    data,
    model,
    model_jac=None,
    *,
    y=None,
    schema=None,
    method="pdp",
    top_k=5,
    coverage=0.8,
    heter_threshold=None,
    min_r2_gain=0.01,
    finder="best",
    candidate_conditioning_features="all",
    nof_instances=10_000,
    random_state=21,
) -> Report:
    """Run the whole explanation pipeline and return a `Report` — a value.

    ```python
    report = effector.explain(X, model, method="pdp")
    report.show()                     # ledger + ranked table + partition trees
    report.to_html("report.html")     # self-contained page
    ```

    Fits the chosen `method` once, searches regions on every heterogeneous
    feature (`heter_score >= heter_threshold`), greedily selects which splits
    earn their complexity (`select_regions` — the CALM chain), and plots the
    features that carry the analysis: descending final-CALM importance until
    `coverage` of the total importance mass is shown, never more than
    `top_k`, plus every accepted split feature.

    !!! note "Search wide, display by coverage"
        The region search and the R² selection run over all heterogeneous
        features — a feature outside the display cut can still carry the
        biggest explained-variance gain. `coverage`/`top_k` only trim the
        curve plots; the triage plane and the ranked table always cover every
        supported feature.

    !!! note "One model touch"
        All model calls happen through the single `fit`, plus one prediction
        pass for the explained-variance denominator (`f̂(X)`, cached on the
        effect); everything after — importances, curves, `find_regions`, the
        surrogate R²s — is model-free, so the call count does not grow with
        `top_k`.

    Args:
        data: `(N, D)` numpy design matrix.
        model: callable `(N, D) -> (N,)` — the black box.
        model_jac: callable `(N, D) -> (N, D)` Jacobian; required by
            derivative-based methods (`"rhale"`, `"derpdp"`).
        y: optional `(N,)` ground truth aligned with `data`. When given, the
            report header states the model's score on the explained
            subsample — R² for a continuous target, accuracy for a binary
            one — so the report is self-contained.
        schema: optional feature schema (names/types/categories).
        method: effect method — `"pdp"` (default), `"derpdp"`, `"ale"`,
            `"rhale"`, or `"shapdp"` (aliases accepted).
        top_k: hard ceiling on how many features get curve plots.
        coverage: stop plotting once the shown features carry this share of
            the total importance mass (final-CALM importances, share-of-sum
            over the supported features; default 0.8). The report states the
            achieved share.
        heter_threshold: minimum `heter_score` to enter the region search;
            `None` (default) uses the median across the supported features —
            the same convention as `find_regions(features="heterogeneous")`.
        min_r2_gain: smallest explained-variance marginal (fraction of
            `Var(f̂)`, default 0.01 = 1%) a split must add — on top of the
            splits already applied — to earn a snapshot in the CALM chain
            and its regional plots; splits below it are skipped as redundant
            or below-threshold.
        finder: region finder — `"best"` (default), `"best_level_wise"`, or
            a configured finder instance.
        candidate_conditioning_features: features allowed to define splits
            (`"all"` or a list).
        nof_instances: subsample size the effect is built on.
        random_state: seed for the subsample.

    Returns:
        a `Report` bound to the fitted effect — `FeatureReport`s in
        final-CALM-importance-descending order, partitions stored as dicts,
        an `overview` (global importance + heterogeneity) over every supported
        feature, and `explained_variance` holding the serialized CALM chain
        (`CalmSequence.to_dict()`: GAM R², per-stage marginal gains, skipped
        splits, one snapshot per accepted split).
    """
    spec = method_registry.resolve(method)
    ctor_args = (model, model_jac) if spec.needs_jac else (model,)
    effect = spec.cls(
        data,
        *ctor_args,
        schema=schema,
        nof_instances=nof_instances,
        random_state=random_state,
    )
    return _explain_effect(
        effect,
        y=y,
        top_k=top_k,
        coverage=coverage,
        heter_threshold=heter_threshold,
        min_r2_gain=min_r2_gain,
        finder=finder,
        candidate_conditioning_features=candidate_conditioning_features,
    )

effector.global_effect.GlobalEffectBase.explain(*, y=None, top_k=5, coverage=0.8, heter_threshold=None, min_r2_gain=0.01, finder='best', candidate_conditioning_features='all')

The one-liner on this engine — effector.explain without leaving the session.

pdp = effector.PDP(X, model, schema=schema)
report = pdp.explain()            # same Report as effector.explain

Runs the same pipeline as effector.explain on the already-built engine: features you have fit with custom config keep it (missing ones are computed with the defaults), and every cache the pipeline warms stays on the engine for your follow-up queries.

Parameters:

Name Type Description Default
y Optional[ndarray]

optional ground truth aligned with the original data (or this engine's subsample); when given, the report header states the model's score on the explained subsample.

None
top_k int

hard ceiling on how many features get curve plots.

5
coverage float

stop plotting once the shown features carry this share of the total importance mass (default 0.8).

0.8
heter_threshold Optional[float]

minimum heter_score to enter the region search; None (default) = the median convention.

None
min_r2_gain float

smallest explained-variance marginal a split must add to earn a snapshot in the CALM chain.

0.01
finder

region finder, as in find_regions.

'best'
candidate_conditioning_features

features allowed to define splits.

'all'

Returns:

Type Description

a Report bound to this engine.

Source code in effector/global_effect.py
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
def explain(
    self,
    *,
    y: Optional[np.ndarray] = None,
    top_k: int = 5,
    coverage: float = 0.8,
    heter_threshold: Optional[float] = None,
    min_r2_gain: float = 0.01,
    finder="best",
    candidate_conditioning_features="all",
):
    """The one-liner on *this* engine — `effector.explain` without leaving the session.

    ```python
    pdp = effector.PDP(X, model, schema=schema)
    report = pdp.explain()            # same Report as effector.explain
    ```

    Runs the same pipeline as `effector.explain` on the already-built
    engine: features you have `fit` with custom config keep it (missing
    ones are computed with the defaults), and every cache the pipeline
    warms stays on the engine for your follow-up queries.

    Args:
        y: optional ground truth aligned with the original `data` (or
            this engine's subsample); when given, the report header
            states the model's score on the explained subsample.
        top_k: hard ceiling on how many features get curve plots.
        coverage: stop plotting once the shown features carry this share
            of the total importance mass (default 0.8).
        heter_threshold: minimum `heter_score` to enter the region
            search; `None` (default) = the median convention.
        min_r2_gain: smallest explained-variance marginal a split must
            add to earn a snapshot in the CALM chain.
        finder: region finder, as in `find_regions`.
        candidate_conditioning_features: features allowed to define
            splits.

    Returns:
        a `Report` bound to this engine.
    """
    from effector import report as _report  # lazy: one-way dep guard

    return _report._explain_effect(
        self,
        y=y,
        top_k=top_k,
        coverage=coverage,
        heter_threshold=heter_threshold,
        min_r2_gain=min_r2_gain,
        finder=finder,
        candidate_conditioning_features=candidate_conditioning_features,
    )

effector.report.Report(method_name, feature_names, target_name, features, config=dict(), overview=list(), explained_variance=None, summary=None) dataclass

An importance-ranked, serializable explanation of a model — a value.

report = effector.explain(X, model)
report.show()                     # ledger + ranked table + partition trees
report.to_html("report.html")     # self-contained page

features holds one FeatureReport per plotted feature, final-CALM importance descending (split features at the instance-weighted mean of their subregions). overview holds the cheap global scalars — importance and heterogeneity — for every supported feature (superset of features), so the triage plane renders even on unbound reports. explained_variance holds the serialized CALM chain (CalmSequence.to_dict()): the flat decision-sequence keys (gam_r2, regional_r2, min_gain, stages, skipped) plus one serialized snapshot per accepted split under calms — plain floats, so it too renders unbound; None for derivative-scale methods. A report produced by explain is bound to its fitted effect, which to_html uses for the per-leaf regional plots; everything else works from the stored values alone.

Methods:

Name Description
show

Print the report as three tables: what was explained (data + model),

plot_importance

Horizontal bar chart of feature importance: sorted descending,

to_html

Render the report as one self-contained HTML page — the pipeline in reading order.

to_dict

Serialize to a plain JSON-able dict.

from_dict

Rebuild a Report from to_dict() output.

show(ascii=False)

Print the report as three tables: what was explained (data + model), the explained-variance decision sequence (accepted splits, then the rejected ones), and the ranked feature table (final-CALM values — split features at the instance-weighted mean of their subregions). Each accepted partition tree follows.

Parameters:

Name Type Description Default
ascii

draw with plain ASCII instead of box-drawing/block characters, for terminals and logs that mangle unicode.

False

Works on unbound reports (rebuilt via from_dict) too.

Source code in effector/report.py
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
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
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
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
def show(self, ascii=False):
    """Print the report as three tables: what was explained (data + model),
    the explained-variance decision sequence (accepted splits, then the
    rejected ones), and the ranked feature table (final-CALM values — split
    features at the instance-weighted mean of their subregions). Each
    accepted partition tree follows.

    Args:
        ascii: draw with plain ASCII instead of box-drawing/block
            characters, for terminals and logs that mangle unicode.

    Works on unbound reports (rebuilt via `from_dict`) too.
    """
    g = _ASCII_GLYPHS if ascii else _UNICODE_GLYPHS
    dash, heavy, bar, dot = g["dash"], g["heavy"], g["bar"], g["dot"]
    # every table body is INDENT + TABLE wide, and the rules match it, so a
    # column set that does not sum to TABLE shears the table visibly.
    INDENT, TABLE = 4, 70

    def p(line=""):
        print(line.rstrip())

    def rule(ch=dash, width=TABLE, indent=INDENT):
        p(" " * indent + ch * width)

    def section(name, note=""):
        p()
        head = " " * (INDENT - 2) + name
        if note:
            pad = INDENT + TABLE - len(head) - len(note)
            head += " " * max(2, pad) + note
        p(head)
        rule(dash, TABLE + 2, INDENT - 2)

    title = method_registry.resolve(self.method_name).display_name
    p()
    rule(heavy, TABLE + 2, INDENT - 2)
    p(f"  {title} report  {dot}  target: {self.target_name}")
    rule(heavy, TABLE + 2, INDENT - 2)

    # -- data & model ------------------------------------------------------
    s = self.summary
    if s:
        section("DATA & MODEL")
        kinds = f" {dot} ".join(
            f"{v} {k}" for k, v in s.get("feature_types", {}).items() if v
        )
        rows = [
            ("instances", f"{s['n_instances']:,}"),
            ("features", f"{s['n_features']}  {dot}  {kinds}"),
            (
                "model output",
                f"mean {s['pred_mean']:.3g} {dot} std {s['pred_std']:.3g} "
                f"{dot} range [{s['pred_min']:.3g}, {s['pred_max']:.3g}]",
            ),
        ]
        if s.get("score") is not None:
            kind = s["score_kind"]
            if ascii:
                kind = kind.replace("²", "2")
            rows.append((f"model {kind}", f"{s['score']:.3f}  (on this subsample)"))
        for k, v in rows:
            p(f"{' ' * INDENT}{k:<14}{v}")

    # -- explained variance ------------------------------------------------
    ev = self.explained_variance
    accepted = {st["feature"] for st in ev["stages"]} if ev else None
    if ev and self._ev_headline():
        _print_explained_variance(ev, ascii=ascii)

    # -- the ranked features -----------------------------------------------
    # 14 + 11 + 2 + 18 + 11 + 14 = 70
    section("FEATURES", "ranked, in the selected snapshot")
    p(
        f"{' ' * INDENT}{'feature':<14}{'importance':>11}  {'':<18}"
        f"{'heter':>11}{'#regions':>14}"
    )
    rule()
    imax = max((fr.importance for fr in self.features), default=0.0)
    for fr in self.features:
        n = int(round(18 * fr.importance / imax)) if imax > 0 else 0
        p(
            f"{' ' * INDENT}{fr.name:<14.14}{fr.importance:>11.4f}  "
            f"{bar * n:<18}{fr.heter_score:>11.4f}"
            f"{self._regions_of(fr, accepted):>14d}"
        )
    cov = self.config.get("coverage_achieved")
    if cov is not None:
        rule()
        p(
            f"{' ' * INDENT}the features above carry {cov:.0%} of the total"
            " importance mass"
        )
    p()

    # -- the trees ---------------------------------------------------------
    for fr in self.features:
        if self._regions_of(fr, accepted) > 1:
            p = Partition.from_dict(fr.partition)
            if self._effect is not None:
                # bound: rule text resolves level names and raw units
                p = p.bind(self._effect)
            p.show()

plot_importance(show_plot=True)

Horizontal bar chart of feature importance: sorted descending, value at each bar tip, accepted split features tagged · split.

Covers every feature in overview (all supported features), not just the reported top-k.

Parameters:

Name Type Description Default
show_plot

True (default) displays the figure and returns None; False returns (fig, ax) instead.

True

Returns:

Type Description

None, or (fig, ax) when show_plot=False.

Source code in effector/report.py
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
530
531
532
533
534
535
536
537
538
def plot_importance(self, show_plot=True):
    """Horizontal bar chart of feature importance: sorted descending,
    value at each bar tip, accepted split features tagged `· split`.

    Covers every feature in `overview` (all supported features), not just
    the reported top-k.

    Args:
        show_plot: `True` (default) displays the figure and returns
            `None`; `False` returns `(fig, ax)` instead.

    Returns:
        `None`, or `(fig, ax)` when `show_plot=False`.
    """
    import matplotlib.pyplot as plt

    from effector import theme

    t = theme.active()
    rows = self._sorted_overview()
    split = self._split_features()
    fig, ax = plt.subplots(figsize=(7, 0.35 * len(rows) + 1.2))
    vals = [o["_imp"] for o in rows]
    ax.barh(range(len(rows)), vals, height=0.55, color=t.BAR_FACE)
    ax.set_yticks(range(len(rows)))
    ax.set_yticklabels([o["name"] for o in rows])
    ax.invert_yaxis()  # most important on top
    self._value_labels(
        ax,
        vals,
        [
            f"{v:.3f}" + ("  · split" if o["feature"] in split else "")
            for v, o in zip(vals, rows)
        ],
        muted=theme.MUTED,
        emphasized=theme.INK2,
        emphasis=[o["feature"] in split for o in rows],
    )
    ax.grid(axis="y", visible=False)
    ax.set_xlabel(f"importance ({self.target_name} units)")
    ax.set_title(
        f"{method_registry.resolve(self.method_name).display_name} — "
        f"feature importance",
        loc="left",
    )
    fig.tight_layout()
    if show_plot:
        plt.show(block=False)
        return None
    return fig, ax

to_html(path=None, share_y='across')

Render the report as one self-contained HTML page — the pipeline in reading order.

The page reads top-down as the analysis: the overview first — the explained-variance ledger bar (the headline: what the global read buys, what each accepted split adds, what stays unexplained), the triage plane over all supported features with one arrow per accepted split (global point → weighted-mean point), the clickable ranked table with the achieved importance coverage, and the decision-sequence table. Then the regional analysis — the final CALM: one section per plotted feature in descending final-CALM importance; an accepted split renders as a group of per-leaf regional plots in rule order, everything else as its global curve (with a one-line pointer when a found split was rejected). Last, the global baseline: the split features' global curves alone — the counterfactual read without regions. Every figure is a base64 PNG; navigation, click-to-zoom, and collapsing are inline vanilla JS/CSS — no external assets, one file, one click.

For at-a-glance comparison, every panel of every effect plot — the effect axis, and (RH)ALE's dy/dx axis below it — shares a y range with the same panel of the other plots, either across all features (share_y="across") or within each one ("within"). The x range is always shared per feature (its global plot and its leaves). Every figure is drawn with the method's own default heterogeneity view — the dy/dx bars for the (RH)ALE family, the (d-)ICE cloud for the PDP family, the SHAP scatter for SHAP-DP.

Unbound reports

A report rebuilt with from_dict renders everything from stored values — including the ledger and the triage arrows; only the per-leaf regional plots need the live effect, and leaf statistics are shown in a table instead.

Parameters:

Name Type Description Default
path

optional file path to write the page to.

None
share_y

scope of the shared y (and dy/dx) range — "across" (default) unions it over all features, "within" over each feature's own global plot and leaves.

'across'

No windows

The figures are built off-screen and closed once encoded, so rendering a report never pops up a plot — even in an interactive session where plot() normally does.

Returns:

Type Description

the HTML string when path is None; None after writing to

path — so an interactive shell doesn't echo a megabyte of

markup.

Source code in effector/report.py
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
def to_html(self, path=None, share_y="across"):
    """Render the report as one self-contained HTML page — the pipeline in reading order.

    The page reads top-down as the analysis: **the overview first** —
    the explained-variance ledger bar (the headline: what the global
    read buys, what each accepted split adds, what stays unexplained),
    the triage plane over all supported features with one arrow per
    accepted split (global point → weighted-mean point), the clickable
    ranked table with the achieved importance coverage, and the
    decision-sequence table. Then **the regional analysis — the final
    CALM**: one section per plotted feature in descending final-CALM
    importance; an accepted split renders as a group of per-leaf
    regional plots in rule order, everything else as its global curve
    (with a one-line pointer when a found split was rejected). Last,
    **the global baseline**: the split features' global curves alone —
    the counterfactual read without regions. Every figure is a base64
    PNG; navigation, click-to-zoom, and collapsing are inline vanilla
    JS/CSS — no external assets, one file, one click.

    For at-a-glance comparison, every panel of every effect plot — the
    effect axis, and (RH)ALE's `dy/dx` axis below it — shares a y range
    with the same panel of the other plots, either across all features
    (`share_y="across"`) or within each one (`"within"`). The x range is
    always shared per feature (its global plot and its leaves). Every
    figure is drawn with the method's own default heterogeneity view — the
    `dy/dx` bars for the (RH)ALE family, the (d-)ICE cloud for the PDP
    family, the SHAP scatter for SHAP-DP.

    !!! note "Unbound reports"
        A report rebuilt with `from_dict` renders everything from stored
        values — including the ledger and the triage arrows; only the
        per-leaf regional plots need the live effect, and leaf
        statistics are shown in a table instead.

    Args:
        path: optional file path to write the page to.
        share_y: scope of the shared y (and `dy/dx`) range —
            `"across"` (default) unions it over all features, `"within"`
            over each feature's own global plot and leaves.

    !!! note "No windows"
        The figures are built off-screen and closed once encoded, so
        rendering a report never pops up a plot — even in an interactive
        session where `plot()` normally does.

    Returns:
        the HTML string when `path` is `None`; `None` after writing to
        `path` — so an interactive shell doesn't echo a megabyte of
        markup.
    """
    import matplotlib as mpl

    from effector import theme

    # C1: the page always renders in the active house theme — every
    # savefig happens inside _render_html, so a local rc_context holds
    # (interactively it would revert before draw; here it cannot)
    with _offscreen_figures(), mpl.rc_context(theme.active().rcparams):
        html = self._render_html(share_y)
    if path is not None:
        with open(path, "w") as fh:
            fh.write(html)
        return None
    return html

to_dict()

Serialize to a plain JSON-able dict.

Values only

Curves, scores, and partition rules are serialized — never the model, the data, or the fitted effect.

Returns:

Type Description

a dict that from_dict round-trips.

Source code in effector/report.py
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
def to_dict(self):
    """Serialize to a plain JSON-able dict.

    !!! note "Values only"
        Curves, scores, and partition rules are serialized — never the
        model, the data, or the fitted effect.

    Returns:
        a dict that `from_dict` round-trips.
    """
    return {
        "schema_version": 1,
        "method_name": self.method_name,
        "feature_names": list(self.feature_names),
        "target_name": self.target_name,
        "config": self.config,
        "overview": [dict(o) for o in self.overview],
        "features": [fr.to_dict() for fr in self.features],
        "explained_variance": self.explained_variance,
        "summary": self.summary,
    }

from_dict(d) classmethod

Rebuild a Report from to_dict() output.

The result is unbound: show, plot_importance, and to_html all work from the stored values; to_html skips only the per-leaf regional plots and the triage arrows, which need the live effect.

Parameters:

Name Type Description Default
d

a dict produced by to_dict().

required

Returns:

Type Description

an unbound Report.

Source code in effector/report.py
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
@classmethod
def from_dict(cls, d):
    """Rebuild a `Report` from `to_dict()` output.

    The result is unbound: `show`, `plot_importance`, and `to_html` all
    work from the stored values; `to_html` skips only the per-leaf
    regional plots and the triage arrows, which need the live effect.

    Args:
        d: a dict produced by `to_dict()`.

    Returns:
        an unbound `Report`.
    """
    return cls(
        method_name=d["method_name"],
        feature_names=list(d["feature_names"]),
        target_name=d["target_name"],
        features=[FeatureReport.from_dict(x) for x in d["features"]],
        config=d.get("config", {}),
        overview=[dict(o) for o in d.get("overview", [])],
        explained_variance=d.get("explained_variance"),
        summary=d.get("summary"),
    )

effector.report.FeatureReport(feature, name, importance, heter_score, xs, y, h, partition=None) dataclass

The per-feature slice of a Report — plain values, no live effect.

Attributes:

Name Type Description
feature int

feature index.

name str

feature display name.

importance float

the feature's importance score.

heter_score float

the feature's scalar heterogeneity.

xs ndarray

evaluation grid, (T,).

y ndarray

mean-effect curve at xs, (T,).

h ndarray

heterogeneity curve at xs, (T,).

partition Optional[dict]

Partition.to_dict() when find_regions ran, else None.

Methods:

Name Description
from_dict

Rebuild a FeatureReport from to_dict() output.

to_dict

Serialize to a plain JSON-able dict (arrays become lists).

from_dict(d) classmethod

Rebuild a FeatureReport from to_dict() output.

Source code in effector/report.py
244
245
246
247
248
249
250
251
252
253
254
255
256
@classmethod
def from_dict(cls, d):
    """Rebuild a `FeatureReport` from `to_dict()` output."""
    return cls(
        feature=d["feature"],
        name=d["name"],
        importance=d["importance"],
        heter_score=d["heter_score"],
        xs=np.asarray(d["xs"]),
        y=np.asarray(d["y"]),
        h=np.asarray(d["h"]),
        partition=d["partition"],
    )

to_dict()

Serialize to a plain JSON-able dict (arrays become lists).

Source code in effector/report.py
231
232
233
234
235
236
237
238
239
240
241
242
def to_dict(self):
    """Serialize to a plain JSON-able dict (arrays become lists)."""
    return {
        "feature": self.feature,
        "name": self.name,
        "importance": self.importance,
        "heter_score": self.heter_score,
        "xs": np.asarray(self.xs).tolist(),
        "y": np.asarray(self.y).tolist(),
        "h": np.asarray(self.h).tolist(),
        "partition": self.partition,
    }