Skip to content

ProfilingResult

The central hub returned by fit_profiler. Holds the archetype artifacts, exposes the per-case justifier / justify, plot helpers, and save/load. save stamps the joblib with parleml_mli.result.PROFILING_RESULT_SCHEMA_VERSION; load refuses any other stamp (or an unstamped pickle) with a parleml_mli.StaleArtifactError (a ValueError) that says to refit the profile with the installed mli. Configure optional stages eagerly via ProfilingConfig sections, or populate them lazily afterwards with the build_*_result functions documented below (build_rules_result(result, ...) and friends — these are module-level functions that take the result as their first argument, not methods on ProfilingResult). See the Profiling guide.

In short

  • ProfilingResult holds the archetypes and every optional artifact, the plot helpers and the per-case justifier / justify.
  • result.capabilities says what the backend could produce; result.resolve_capabilities() gives a uniform view.
  • The build_*_result functions fill in an optional stage later. They are module-level functions, not methods.
  • save / load use one joblib with a schema stamp; another stamp raises StaleArtifactError.

ProfilingResult dataclass

ProfilingResult(
    labels_: ndarray,
    summary_df: DataFrame,
    medoids_df: DataFrame,
    characterization_: dict[int, DataFrame],
    clustering_result: ClusteringResult,
    space_type: SpaceType,
    task_type: TaskType,
    contamination_df: DataFrame | None = None,
    umap_embedding_: ndarray | None = None,
    cluster_thresholds_: dict[int, float] = dict(),
    base_target_rate_: float = 0.0,
    min_odds_ratio_: float = 3.0,
    min_stability_: float = DEFAULT_MIN_STABILITY,
    stability_: StabilityResult | None = None,
    llm_analysis_: dict[str, object] | None = None,
    rules_: (
        RulesResult | MulticlassRulesResult | None
    ) = None,
    error_analysis_: ErrorAnalysisResult | None = None,
    model_evaluation_: "ModelEvaluationResult | None" = None,
    independent_rules_: "IndependentRuleModelResult | None" = None,
    tree_diagrams_: list[str] | None = None,
    explanations_: ExplanationsResult | None = None,
    eda_: "EDAResult | None" = None,
    target_: ResolvedTarget | None = None,
    threshold_: ResolvedThreshold | None = None,
    _space: DistanceSpace | None = None,
    _target_data_t: ndarray | None = None,
    _medoid_data_t: ndarray | None = None,
    _target_shap_values: ndarray | None = None,
    _shap_class_index: int | None = None,
    _predictions_target: ndarray | None = None,
    _feature_names: list[str] = list(),
    _model: object | None = None,
    _model_type: ModelType | None = None,
    _umap_sample_idx: ndarray | None = None,
    _nn_index: NeighborIndex | None = None,
    _nn_lock: Lock = Lock(),
    _nn_index_original: "OriginalFeatureIndex | None" = None,
    _nn_lock_original: Lock = Lock(),
    _X_target: DataFrame | None = None,
    _X_target_orig: DataFrame | None = None,
    _target_mask: ndarray | None = None,
    _y_full: ndarray | None = None,
    _id_columns: list[str] | None = None,
    _encoding_registry: EncodingRegistry = (
        lambda: IdentityRegistry()
    )(),
    _n_jobs: int = -1,
    meta: dict[str, Any] = dict(),
    config: "RunSettings | None" = None,
    narration_: "NarrationBundle | None" = None,
    _classes: "dict[str, ClassProfile] | None" = None,
    _submodel_results: "dict[str, ProfilingResult] | None" = None,
    capabilities: "Capabilities | None" = None,
)

Bases: _ProfilingResultPlotsMixin, _RetrievalMixin, _InferenceMixin, _MulticlassMixin, _ReportSerializationMixin

Container for all profiling outputs with retrieval and plotting methods.

decision_threshold property

decision_threshold: float | None

The resolved binary cut — :attr:threshold_'s value.

has_predictions property

has_predictions: bool

True when model scores are available in _predictions_target (enables plot_scores).

evaluation_ property

evaluation_: dict[str, Any]

Per-split holdout evaluations, keyed by split name.

Returns {split_name: BinaryEvaluation | MulticlassEvaluation | RegressionSplitEvaluation}. Convenience alias for self.model_evaluation_.splits (with empty dict fallback).

cluster_ids property

cluster_ids: list[int]

Sorted list of cluster IDs (excluding noise -1).

model_ property

model_: object | None

The fitted model used for profiling (None if not stored).

Stored when fit_profiler is called with include_model=True (the default for the native path). Needed by SHAP / surrogate / permutation-importance recomputation and by the justification path.

model_type_ property

model_type_: ModelType | None

Detected :class:~parleml_mli.core.types.ModelType of :attr:model_.

feature_names_ property

feature_names_: list[str]

Encoded feature names aligned with :attr:target_features_ columns.

target_predictions_ property

target_predictions_: ndarray | None

Model scores for the target cohort (None when unavailable).

Probabilities for the positive class on classifiers, raw model output on regressors — aligned row-for-row with :attr:labels_.

target_shap_values_ property

target_shap_values_: ndarray | None

(n_target_rows, n_features) SHAP matrix for the target cohort.

None when the profile was not fit with a SHAP-aware space. Aligned row-for-row with :attr:labels_ and column-for-column with :attr:feature_names_.

target_features_ property

target_features_: DataFrame | None

Encoded target-cohort feature matrix (None if not stored).

The model-space frame the profile was built from. For the human-readable original values see :attr:target_features_original_.

target_features_original_ property

target_features_original_: DataFrame | None

Original-space (human-readable) target-cohort features.

Populated only when X_target_orig= was passed to fit_profiler; None otherwise.

target_mask_ property

target_mask_: ndarray | None

Boolean mask selecting target rows from the full fit-time X.

None when the profile was fit directly on a target-only matrix.

id_columns_ property

id_columns_: list[str] | None

Original columns pinned into representative examples (e.g. record IDs).

umap_sample_index_ property

umap_sample_index_: ndarray | None

Row index of the UMAP scatter sample, or None when not downsampled.

When the run downsampled the 2-D scatter, :attr:umap_embedding_ covers only these rows; use this index to align :attr:labels_ / :attr:target_predictions_ to the embedding. None means the embedding is row-aligned with the full target cohort.

encoding_registry_ property

encoding_registry_: EncodingRegistry

Categorical-encoding registry used to decode engineered features.

Defaults to :class:~parleml_mli.core.encoding.IdentityRegistry (no decoding) when the profile was fit without categorical encoders.

resolve_capabilities

resolve_capabilities() -> Capabilities

Return self.capabilities when stamped, else derive from field presence.

The derivation also seeds :func:parleml_mli.fit_profiler's own stamp, so consumers get a uniform Capabilities view across native and lite artifacts and the UI / AutoDoc need no second code path.

get_cluster_features

get_cluster_features(cluster_id: int) -> DataFrame

Return the SHAP / z-score characterization table for one cluster.

Parameters:

Name Type Description Default
cluster_id int

Cluster label. Use -1 for the noise bucket.

required

Returns:

Type Description
DataFrame

Columns depend on whether SHAP was available at fit time:

  • SHAP: feature, mean_shap, mean_abs_shap
  • z-score fallback: feature, z_score, abs_z_score

Rows are sorted by importance (descending).

Raises:

Type Description
KeyError

If cluster_id does not exist in the characterization.

save

save(path: str, *, include_model: bool = True) -> None

Persist this result to disk via :func:joblib.dump.

Persists alongside the pickled result a small metadata header recording :data:PROFILING_RESULT_SCHEMA_VERSION, the mli version, the RUN_SETTINGS_SCHEMA_VERSION, and the installed versions of the stochastic libraries the artifact depends on (scikit-learn, umap-learn, hdbscan, shap). :meth:load refuses any other schema stamp and emits a :class:UserWarning when the mli or library versions differ.

Parameters:

Name Type Description Default
path str

File path (recommended extension: .joblib).

required
include_model bool

When False, the stored model reference is excluded to reduce file size. Leaf-space retrieval (retrieve_new_cases) will not work after reloading without a model.

True

load classmethod

load(path: str) -> ProfilingResult

Load a result written by :meth:save.

Returns:

Type Description
ProfilingResult

Fully restored result object with all methods available. If the result was saved with include_model=False, leaf-space retrieval will raise at call time.

Raises:

Type Description
StaleArtifactError

The joblib carries no schema stamp or one other than :data:PROFILING_RESULT_SCHEMA_VERSION, or holds an artifact written under another schema — refit the profile with this mli. A :class:ValueError subclass.

TypeError

The envelope holds something other than a ProfilingResult.

Notes

The active mli version and the installed versions of the stochastic libraries (scikit-learn, umap-learn, hdbscan, shap) are checked against the recorded header; a :class:UserWarning is emitted per mismatch.

Capabilities

The flag matrix stamped on every result (result.capabilities) by fit_profiler (native) and monitor_predictions (lite) so consumers can grey out surfaces the underlying model can't power. Use ProfilingResult.resolve_capabilities for a uniform view across native and lite artifacts (an unstamped SHAP-only run derives its flags from field presence).

Capabilities dataclass

Capabilities(
    model_evaluation: bool = False,
    archetypes: bool = False,
    rules: bool = False,
    surrogate: bool = False,
    error_tree: bool = False,
    drift: bool = False,
    fairness: bool = False,
    shap: bool = False,
    pdp_ice: bool = False,
    counterfactual: bool = False,
    eda: bool = False,
    leakage: bool = False,
    backend: Literal[
        "native", "lite_model", "lite_predictions"
    ] = "native",
    notes: str = "",
)

Flag matrix indicating which downstream surfaces are populated.

Stamped by :func:parleml_mli.fit_profiler (backend="native", when a recognised model was supplied) and :func:parleml_mli.monitor_predictions (backend="lite_model" / "lite_predictions") so consumers (UI, AutoDoc, backend wrappers) can grey out tabs the underlying model can't power. An unstamped result (a SHAP-only native run) derives its flags from field presence — see :meth:ProfilingResult.resolve_capabilities.

Compute-on-demand builders

Populate an optional stage on an already-fitted result, e.g. result.rules_ = build_rules_result(result, X=X_train). Each is the lazy peer of the eager ProfilingConfig section with the same name; all are re-exported from the top level (from parleml_mli import build_rules_result).

build_rules_result

build_rules_result(
    result: ProfilingResult,
    config: RulesConfig | None = None,
    *,
    X: DataFrame | None = None,
    y: ndarray | Series | None = None,
    X_holdout: DataFrame | None = None,
    y_holdout: ndarray | Series | None = None,
    holdout_basis: HoldoutBasis | None = None,
    random_state: int = 42,
    verbose: bool = False,
    allow_target_mask_as_y: bool = False
) -> RulesResult | MulticlassRulesResult

Extract one global ruleset from the stored model via rule extraction.

Runs a single extraction pass over the full (X, y) matrix and, when cluster labels are available, derives per-archetype rule- firing fingerprints from that ruleset (see :func:parleml_mli.archetypes.fingerprints.compute_archetype_fingerprints). Each archetype is then characterised by which of the global rules fire on its members — a single source of truth for rule logic, no duplicate LLM translation work, and a natural cross-archetype comparison surface.

Parameters:

Name Type Description Default
config RulesConfig | None

Typed :class:~parleml_mli.config.RulesConfig selecting the engines and their tuning knobs. None defaults to RulesConfig() (the engine="all" meta-engine — the positive and the sequential miners). X / y (data), allow_target_mask_as_y (orchestration) and random_state / verbose (runtime) are not part of RulesConfig and stay direct arguments.

:attr:RulesConfig.engine selects the mining engines:

  • "positive_miner" — :class:rules.PositiveRuleMiner (independent lift-gated rules with sibling alternatives).
  • "sequential" — :class:rules.PositiveSequentialRuleMiner (minimal operational portfolio; greedy covering, rules in greedy-pick order).
  • "all_class" — :class:rules.AllClassRuleClassifier lifted through :meth:RuleSetSummary.from_all_class.
  • "all" — run the positive and the sequential miners, storing per-engine results in :attr:RulesResult.by_engine (the all-class engine runs only when named).
  • a tuple of engine names — run each of them, stored like "all"; e.g. ("positive_miner", "sequential", "all_class") adds the all-class list (binary models only) to the default pair.

A fan-out ("all" or a tuple) records an engine that raised in :attr:RulesResult.engines_skipped and mines the others; a single engine's error propagates.

See :func:parleml_mli.rules.extract_rules for the full algorithmic contract and :class:parleml_mli.config.MinerConfig / :class:parleml_mli.config.AllClassConfig for the per-engine tuning surface (plus :attr:RulesConfig.max_trees for the tree budget).

None
X DataFrame | None

Full feature matrix (target + reference rows). Required — re-load the source dataset and pass it in; it is never stored on the result.

None
y ndarray | Series | None

Optional binary label vector aligned with X. Defaults to the stored _y_full (original labels) when present; falls back to the stored _target_mask only when allow_target_mask_as_y=True is set explicitly. Labels are binarised against the run's positive class (result.target_) before mining, so every engine mines the same {0, 1} indicator.

None
X_holdout DataFrame | None

Held-out rows — rows the rule search never saw, and ideally the model neither (holdout_basis says which) — in X's feature space, with labels like y (a label outside the run's classes counts against every rule; a row without a label is dropped with a warning). Every rule set gets its Test numbers there (:mod:parleml_mli.rules.scoring); None (default) leaves them unmeasured. When rules are mined on the stored _target_mask, y_holdout must be the same boolean mask over the held-out rows (True = the event); other labels raise :class:ValueError.

None
y_holdout DataFrame | None

Held-out rows — rows the rule search never saw, and ideally the model neither (holdout_basis says which) — in X's feature space, with labels like y (a label outside the run's classes counts against every rule; a row without a label is dropped with a warning). Every rule set gets its Test numbers there (:mod:parleml_mli.rules.scoring); None (default) leaves them unmeasured. When rules are mined on the stored _target_mask, y_holdout must be the same boolean mask over the held-out rows (True = the event); other labels raise :class:ValueError.

None
holdout_basis HoldoutBasis | None

Where the held-out rows come from, stamped on every rule set.

None
allow_target_mask_as_y bool

Opt-in to using the stored _target_mask as the y vector when no other label source is available. False by default because user-supplied masks (e.g. (y == 1) & condition) differ from the original labels and silently mining against the mask is a real correctness hazard. Emits a :class:UserWarning when the fallback is taken.

False
random_state int

Runtime knobs forwarded to :func:parleml_mli.rules.extract_rules (sourced from :class:parleml_mli.config.RuntimeConfig upstream).

42
verbose int

Runtime knobs forwarded to :func:parleml_mli.rules.extract_rules (sourced from :class:parleml_mli.config.RuntimeConfig upstream).

42

Returns:

Type Description
RulesResult | MulticlassRulesResult

A binary/regression run returns a :class:RulesResult; a multiclass run returns a :class:MulticlassRulesResult wrapping the per-class one-vs-rest rulesets (so result.rules_ carries a typed payload for predict_rule / generate_report).

Raises:

Type Description
ValueError

result._model is None (e.g. SHAP-only mode), or X / y are missing. (:class:~parleml_mli.config.RulesConfig refuses an unknown engine when it is built.)

TypeError

Model is not all-class-engine compatible.

ImportError

rules is not installed.

build_explanations_result

build_explanations_result(
    result: ProfilingResult,
    config: ExplanationsConfig | None = None,
    *,
    X: DataFrame | None = None,
    y: ndarray | Series | None = None,
    ovr_classes: Any | None = None,
    decision_threshold: (
        ResolvedThreshold | float | None
    ) = None
) -> ExplanationsResult

Compute every requested global-model interpretability artifact.

Wraps :func:parleml_mli.explanations.explain with result's stored model and full-population matrices, attaching per-archetype overlays when result.labels_ is available. Returns the global :class:ExplanationsResult; the caller assigns it to result.explanations_ (fit_profiler does this during the one-pass build). When ovr_classes is set, per-class slots on result.classes are additionally populated in place.

Parameters:

Name Type Description Default
config ExplanationsConfig | None

Typed :class:~parleml_mli.config.ExplanationsConfig selecting which artifacts to compute (presence of a section enables it). A bare ExplanationsConfig() resolves to the default trio via :func:parleml_mli.config.resolve_explanations_config. ovr_classes is read from config.ovr_classes when not passed explicitly.

None
X DataFrame | None

Full evaluation matrix (never stored on the result — re-load the source dataset and pass it in). Required.

None
y ndarray | Series | None

Optional label vector; defaults to result._y_full.

None
ovr_classes Any | None

When set on a multiclass classifier (len(model.classes_) > 2), fan out the per-class-supported artifacts (fairness / permutation_importance / error_tree / pdp / ale) once per OvR slice with target_class=<label> and store each result on result.classes[<class_key>]. Accepted values:

  • True — fan out for every class in model.classes_.
  • "all" — alias of True.
  • Sequence[Any] — explicit subset of class labels.
  • False / None (default) — run a single global pass. On binary classifiers True is effectively a no-op (the global pass already targets the positive class).

Compacity is also fanned out per-class when requested (it reuses each :class:parleml_mli.ClassProfile's own SHAP slice). The global pass still runs and is stored on result.explanations_.

None
decision_threshold ResolvedThreshold | float | None

Overrides the binary cut-off forwarded to the threshold-aware artifacts. None (default) reuses the run's result.threshold_ — never re-tuned here; a disabled run keeps the model's own decision.

None

Raises:

Type Description
ValueError
  • No fitted model is stored on this result (fit with include_model=True).
  • The caller did not pass X= (the full feature matrix is never stored on the result).

Returns:

Type Description
ExplanationsResult

The global artifacts; assign to result.explanations_. Also populates result.classes[<k>].{fairness, permutation_importance, error_tree, pdp, ale, compacity} when ovr_classes triggers the per-class fan-out.

build_eda_result

build_eda_result(
    result: ProfilingResult, **kw: Any
) -> "EDAResult"

Compute every requested EDA artifact for result.

Wraps :func:parleml_mli.eda.compute_eda with the supplied full feature matrix and optional ground truth. Returns the :class:EDAResult; the caller assigns it to result.eda_ (fit_profiler does this during the one-pass build).

Parameters:

Name Type Description Default
result ProfilingResult

The profiling result; only result._y_full is read (as the default ground truth when y= is not supplied).

required
**kw Any

Forwarded verbatim to :func:parleml_mli.eda.compute_eda. Caller must pass X= (the full evaluation matrix; it is never stored on the result — re-load the source dataset). y= / target= may also be overridden; y= defaults to result._y_full when present.

{}

Raises:

Type Description
ValueError

The caller did not pass X= (the full feature matrix is never stored on the result).

Returns:

Type Description
EDAResult

The EDA artifacts; assign to result.eda_.

build_model_evaluation_result

build_model_evaluation_result(
    result: ProfilingResult,
    *,
    splits: (
        Mapping[str, tuple[DataFrame, ndarray | Series]]
        | None
    ) = None,
    X_train: DataFrame | None = None,
    y_train: ndarray | Series | None = None,
    decision_threshold: (
        ResolvedThreshold | float | None
    ) = None,
    n_bootstrap: int = 0,
    ci_alpha: float = 0.05,
    multiclass_threshold_metric: str = "f1"
) -> "ModelEvaluationResult"

Score holdout splits and build the evaluation artifact for result.

Returns the :class:ModelEvaluationResult; the caller assigns it to result.model_evaluation_ (fit_profiler does this during the one-pass build).

Parameters:

Name Type Description Default
result ProfilingResult

The profiling result carrying the fitted model.

required
splits Mapping[str, tuple[DataFrame, ndarray | Series]] | None

Optional {name: (X, y)} holdout dict — typically {"valid": (...), "test": (...)}. When X_train / y_train are also supplied they are evaluated as the "train" split for side-by-side comparison.

None
X_train DataFrame | None

Training matrix used for profiling. Included in the evaluation bundle when holdout splits are provided.

None
y_train DataFrame | None

Training matrix used for profiling. Included in the evaluation bundle when holdout splits are provided.

None
decision_threshold ResolvedThreshold | float | None

Classification cut-off for fixed-threshold metrics. Defaults to the run's result.threshold_; a disabled run reports at the model's own decision (0.5 on the positive-class probability). Splits are binarised against the run's positive class (result.target_).

None
n_bootstrap int

Percentile-bootstrap confidence intervals for the headline metrics, populating each split's metric_cis. 0 (default) skips them.

0
ci_alpha int

Percentile-bootstrap confidence intervals for the headline metrics, populating each split's metric_cis. 0 (default) skips them.

0
multiclass_threshold_metric str

F-beta spec for the per-class OvR threshold tuning that happens on the first split ("train") and is frozen for the rest.

'f1'

build_stability_result

build_stability_result(
    result: ProfilingResult,
    *,
    n_bootstrap: int = DEFAULT_N_BOOTSTRAP,
    min_cluster_size: int | None = None,
    min_samples: int | None = None,
    cluster_selection_method: (
        ClusterSelectionMethod | None
    ) = None,
    n_jobs: int = -1,
    random_state: int = 42
) -> StabilityResult

Assess cluster stability via bootstrap resampling.

Resamples the target data with replacement n_bootstrap times, re-runs HDBSCAN with the same parameters, and measures the best-match Jaccard similarity between the original clusters and each bootstrap's clusters. Returns the :class:StabilityResult; the caller assigns it to result.stability_.

Parameters:

Name Type Description Default
result ProfilingResult

The profiling result whose clustering is re-sampled.

required
n_bootstrap int

Number of bootstrap iterations (default 20).

DEFAULT_N_BOOTSTRAP
min_cluster_size int | None

HDBSCAN min_cluster_size for bootstrap runs. Defaults to the value used in the original clustering (from diagnostics).

None
min_samples int | None

HDBSCAN min_samples. Defaults to the original value.

None
cluster_selection_method ClusterSelectionMethod | None

Defaults to the original clustering method.

None
n_jobs int

Parallel workers for HDBSCAN.

-1
random_state int

Seed for bootstrap sampling.

42

Returns:

Type Description
StabilityResult

Per-cluster Jaccard scores and an overall weighted mean.

build_error_analysis_result

build_error_analysis_result(
    result: ProfilingResult,
    *,
    X: DataFrame | None = None,
    target_mask: ndarray | Series | None = None,
    shap_full: ndarray | None = None,
    shap_cache_path: str | None = None,
    recompute_shap_on_cache_miss: bool = False,
    y_true: ndarray | Series | None = None,
    scores: ndarray | Series | None = None,
    archetype_labels: ndarray | None = None,
    threshold: (
        ResolvedThreshold | float | str | None
    ) = None,
    assignment: str = "territory"
) -> ErrorAnalysisResult

Compute per-archetype classification error metrics.

Three invocation modes are supported, listed from highest- to lowest-level:

Auto full-population mode (recommended) Pass X (and optionally target_mask) — same arrays you gave to :func:fit_profiler. The library handles everything: it loads SHAP from shap_cache_path (or auto-computes via the stored model), keeps each target row in its archetype (labels_), places every reference row on the archetypes by the assignment rule, uses the model to score reference rows, and combines with the stored target predictions. Produces a proper confusion matrix (TP/FP/TN/FN) per archetype at the run's resolved cut (result.threshold_.cut).

Manual full-population mode Pre-compute everything yourself and pass y_true, scores, and archetype_labels aligned to the complete dataset. Useful when scores or archetype labels come from a custom pipeline.

Detection-rate mode (zero-config fallback) Don't pass any extra data. Uses only the target-class cases stored on the result, at the same cut. Since FP = 0 by construction, the meaningful metric is recall (= "what fraction of this archetype does the model detect?"). Charts are labelled "Model Detection Rate" to make this explicit.

Parameters:

Name Type Description Default
result ProfilingResult

The profiling result carrying the fitted model, stored target predictions/labels, archetype labels, and space.

required
X DataFrame | None

Full feature matrix (target + reference rows) — same one passed to :func:fit_profiler. Triggers auto full-population mode. Required for full-population mode (re-load the source dataset; it is never stored on the result).

None
target_mask ndarray | Series | None

Boolean mask aligning X with target rows. Defaults to the stored _target_mask. Required only when X is the full dataset; ignored otherwise.

None
shap_full ndarray | None

Pre-computed SHAP values for X (full population). Optional — when None the library either loads shap_cache_path or auto-computes SHAP for reference rows via the stored model.

None
shap_cache_path str | None

Path to a .npy file produced by fit_profiler(config=ProfilingConfig(runtime=RuntimeConfig(shap_cache_path=...))). Loaded as the SHAP cache for X when shap_full is not provided. Validated against the stored space; on mismatch the call raises :class:ValueError rather than silently triggering a slow on-the-fly SHAP recomputation. Pass recompute_shap_on_cache_miss=True to opt into recomputation.

None
recompute_shap_on_cache_miss bool

When True, a stale SHAP cache (mismatched feature/row count) triggers an on-the-fly SHAP recomputation via the stored model with a warning. When False (default), stale caches raise :class:ValueError so the user can decide whether to proceed.

False
y_true ndarray | Series | None

Ground-truth labels (manual full-population mode), binarised against the run's positive class (result.target_).

  • 1-D array matching archetype_labels length — full-population mode (target + reference combined).
  • 1-D array of length len(labels_) — target-only with custom labels.
  • None — detection-rate mode (all targets treated as positive).
None
scores ndarray | Series | None

Continuous model probability scores (manual full-population mode). Provide aligned with y_true and archetype_labels. When None in detection-rate mode, falls back to stored _predictions_target.

None
archetype_labels ndarray | None

Archetype assignment for each case in the full population (manual full-population mode). -1 = no archetype.

Obtain with::

pred = result.predict_archetype(X_full)
archetype_labels = pred.labels
None
threshold ResolvedThreshold | float | str | None

The cut that binarises scores into predictions. None (default) applies the run's result.threshold_ as resolved at fit time — its :attr:~parleml_mli.core.threshold.ResolvedThreshold.cut, which is the model's own 0.5 decision when thresholding is disabled — so every artifact of the run labels the same rows positive. A float is an explicit cut; an F-beta spec ("f1" / "f<beta>") tunes a cut on the evaluated rows through :func:parleml_mli.core.threshold.resolve_threshold; a :class:~parleml_mli.core.threshold.ResolvedThreshold is used as given. The result's threshold_source is the resolved record's :data:~parleml_mli.core.threshold.ThresholdSource.

None
assignment str

How reference cases are placed on archetypes in auto full-population mode (see :mod:parleml_mli.archetypes.profiling.assignment). Target cases keep their clustering labels (labels_; HDBSCAN noise stays in the -1 bucket), so an archetype's tp + fn equals its n_target_cases in summary_df and contamination_df.

  • "territory" (default) — Voronoi: every reference case is assigned to its nearest medoid regardless of distance — the rule the contamination report's n_reference_assigned and purity use. Matches predict_archetype's nearest_cluster field.
  • "ball" — a reference case is assigned only when it falls inside its nearest archetype's radius; any other reference case is tagged -1. Matches predict_archetype's labels field.
'territory'

Returns:

Type Description
ErrorAnalysisResult

Assign to result.error_analysis_ for subsequent plot_error_analysis() calls (fit_profiler does this during the one-pass build).

Raises:

Type Description
ValueError

When the requested mode cannot be satisfied (missing model, inconsistent array lengths, missing target_mask, etc.).

build_compacity_result

build_compacity_result(
    result: ProfilingResult,
    *,
    approx: float = 0.9,
    fixed_k: int = 5
) -> CompacityResult

Compute per-archetype SHAP compacity from result's target SHAP.

"How few features does the model need to explain a typical row in each archetype?". Aggregates :func:parleml_mli.explanations.compacity.compute_compacity over the rows in result and stores the output on result.explanations_.compacity (creating result.explanations_ when absent), returning it too.

Parameters:

Name Type Description Default
result ProfilingResult

The profiling result carrying target SHAP values and labels.

required
approx float

Cumulative-fraction threshold in (0, 1]. Default 0.9 — i.e. "how many features cover 90% of |SHAP|?".

0.9
fixed_k int

Top-k headline used for the right-bar in :meth:ProfilingResult.plot_compacity.

5

Returns:

Type Description
CompacityResult

Also stored on result.explanations_.compacity for subsequent :meth:ProfilingResult.plot_compacity calls.

Raises:

Type Description
ValueError

When SHAP values are not available on this result.

Archetype output types

The typed artifacts hanging off a profiled result — the per-cluster signatures, bootstrap stability, error analysis, similar-case retrieval, and per-archetype rule-firing fingerprints. Error analysis cuts at the run's result.threshold_ (its threshold_source is that threshold's source) and counts a target row in the archetype its clustering label names, as summary_df and contamination_df do; only reference rows are placed by the nearest-medoid rule.

ClusterSignature dataclass

ClusterSignature(
    cluster_id: int,
    n_members: int,
    shap_profile: DataFrame,
    numeric_stats: DataFrame,
    categorical_stats: dict[str, DataFrame] = dict(),
    top_features: list[str] = list(),
    cluster_meta: dict[str, Any] = dict(),
    missing_in_target: list[str] = list(),
)

Archetype signature combining SHAP importance with actual feature distributions.

Attributes:

Name Type Description
cluster_id int

Cluster label (-1 = noise).

n_members int

Number of target cases in this cluster.

shap_profile DataFrame

Top features ranked by mean_abs_shap. Columns: feature, mean_shap, mean_abs_shap, std_shap. If SHAP is unavailable the columns are feature, z_score, abs_z_score (no std_shap).

numeric_stats DataFrame

Cluster vs all-target statistics for numeric top features. Columns: feature, cluster_median, global_median, cluster_mean, global_mean, cluster_std, global_std.

categorical_stats dict[str, DataFrame]

For each categorical top feature: DataFrame with columns value, cluster_pct, global_pct, cluster_n, global_n, lift (cluster_pct / global_pct), sorted by cluster_pct descending.

top_features list[str]

Ordered list of top feature names (same order as shap_profile).

cluster_meta dict[str, Any]

Optional cluster-level metadata dict populated by ProfilingResult. Keys may include total_target_cases, pct_target_cases, purity_at_threshold, coverage_at_threshold, odds_ratio.

missing_in_target list[str]

Top features whose target-population column is entirely NaN, so no distribution exists to describe. They still rank by importance — on a native-NaN model their missingness is what the model split on — and are rendered type="missing_in_target" rather than being silently dropped or crashing the builder (ARC-N1).

to_dict

to_dict() -> dict[str, Any]

JSON-serializable dict of the signature for programmatic use.

to_llm_text

to_llm_text() -> str

Structured plain-text summary designed for LLM prompt injection.

CombinedClusterSignature dataclass

CombinedClusterSignature(
    cluster_id: int,
    n_members: int,
    original_signature: ClusterSignature,
    engineered_signature: ClusterSignature,
    combined_top_features: list[str] = list(),
    feature_source_map: dict[str, str] = dict(),
    derivative_map: dict[str, list[str]] = dict(),
    cluster_meta: dict[str, Any] = dict(),
    representative_examples: DataFrame | None = None,
    shared_original_extras: list[str] = list(),
)

Archetype signature combining original and engineered feature views.

Attributes:

Name Type Description
cluster_id int

Cluster label.

n_members int

Number of target cases in this cluster.

original_signature ClusterSignature

Signature built from the original (clean, business-readable) dataset.

engineered_signature ClusterSignature

Signature built from the feature-engineered dataset.

combined_top_features list[str]

Ordered feature list for the combined plot: original features first (by aggregated SHAP), then engineered-only features. Shared features that are identical in both datasets appear once.

feature_source_map dict[str, str]

Maps each feature name in combined_top_features to "original" or "engineered". Original features that have engineered derivatives will be followed by those derivatives (tagged "engineered") in combined_top_features.

derivative_map dict[str, list[str]]

Maps each original feature name to the list of engineered features derived from it (via the encoding registry's origin mapping). Authoritative parent→derivative grouping — the compact combined plot uses it to collapse true derivatives under their parent instead of guessing from positional adjacency (which misattributed engineered-only features, ARC-3). Empty when built without a registry.

cluster_meta dict[str, Any]

Cluster-level metadata (prevalence, purity, coverage, odds ratio). Populated by ProfilingResult.

representative_examples DataFrame | None

Small DataFrame of sample cluster members' original feature values. None when not populated.

shared_original_extras list[str]

Features present identically in both frames that rank into the engineered top-N but not the original top-N. They are displayed once, tagged "original" (the column is an original column) — but only the engineered signature actually measured them, so their importance and distribution must be read from the engineered side (ARC-N3). Rescuing them without this routing rendered the model's strongest shared driver as shap 0.0 | wt 0.0 | type "unknown".

to_dict

to_dict() -> dict[str, Any]

JSON-serializable dict merging both signatures with source info.

to_llm_text

to_llm_text() -> str

Structured plain-text summary designed for LLM prompt injection.

StabilityResult dataclass

StabilityResult(
    per_cluster_jaccard: dict[int, float],
    overall_jaccard: float,
    n_bootstrap: int,
    cluster_sizes: dict[int, int] = dict(),
    n_failed: int = 0,
    n_success: int = 0,
)

Per-cluster stability scores from bootstrap resampling.

Attributes:

Name Type Description
per_cluster_jaccard dict[int, float]

Mean Jaccard similarity per original cluster (higher = more stable).

overall_jaccard float

Weighted mean Jaccard across all clusters.

n_bootstrap int

Number of bootstrap iterations requested.

cluster_sizes dict[int, int]

Original cluster sizes used for weighting.

n_failed int

Number of bootstrap iterations whose re-clustering raised and were excluded from the means.

n_success int

Number of bootstrap iterations that produced a clustering and contributed to per_cluster_jaccard / overall_jaccard.

ErrorAnalysisResult dataclass

ErrorAnalysisResult(
    by_archetype: DataFrame,
    n_evaluated: int,
    threshold: float,
    positive_label: Any = None,
    mode: ErrorAnalysisMode = "detection_rate",
    threshold_source: ThresholdSource = "explicit",
    overall_f1_at_threshold: float | None = None,
    scores_full: ndarray | None = None,
    archetype_labels_full: ndarray | None = None,
    schema_version: int = ERROR_ANALYSIS_SCHEMA_VERSION,
)

Per-archetype classification error metrics.

Attributes:

Name Type Description
by_archetype DataFrame

DataFrame with one row per cluster_id the evaluated cases carry (see :attr:archetype_labels_full); -1 gathers the cases on no archetype. Columns:

  • cluster_id — archetype identifier (-1 = on no archetype)
  • n_cases — number of cases evaluated in this archetype
  • tp, fp, tn, fn — confusion-matrix cell counts
  • precision, recall, f1 — standard classification metrics
  • fpr — false-positive rate = FP / (FP + TN)
  • accuracy — (TP + TN) / n_cases
  • mean_score — mean model probability score (NaN when no scores were available at compute time)
n_evaluated int

Total number of cases used in the computation.

threshold float

The cut on the positive-class score that derived y_pred (score >= threshold predicts positive, the served decision rule).

positive_label Any

Display label of the positive class (the run's result.target_.positive_label), or None when the positive cohort is a custom target mask. The confusion counts are computed on the {0, 1} indicator where 1 is that class.

mode ErrorAnalysisMode

"detection_rate" — only target-class cases used; precision is trivially 1.0, the meaningful metric is recall (fraction scored at or above threshold). "full_population" — target + reference cases included; all four confusion-matrix cells are populated and precision/recall/F1 are fully informative.

threshold_source ThresholdSource

Where threshold came from: the :data:~parleml_mli.core.threshold.ThresholdSource of the resolved cut — "served", "explicit", "tuned_in_sample", "degenerate" (a tune the rows could not support), "disabled" (the model's own 0.5 decision) or "not_applicable". A cut passed straight to :func:compute_error_analysis without a source is "explicit".

overall_f1_at_threshold float | None

Binary F1 of the positive class achieved on the full evaluated population at threshold. None when computed from a single-class subset.

scores_full ndarray | None

(n_evaluated,) float model scores aligned with the evaluation (same length and order as the labels / y_true arrays passed in at compute time). None when no scores were available or persist_full_arrays=False. Persisted so the predicted-vs-actual case picker can be rendered post-hoc without re-running predict_proba.

archetype_labels_full ndarray | None

(n_evaluated,) int cluster assignment in evaluation order (None with persist_full_arrays=False). Persisted alongside scores_full for the same reason. How rows are placed on archetypes — and so where -1 can appear — is the caller's (see :func:~parleml_mli.result_builders.build_error_analysis_result's assignment parameter).

schema_version int

:data:ERROR_ANALYSIS_SCHEMA_VERSION, checked when the result is unpickled.

has_scores property

has_scores: bool

True when mean_score was populated from model predictions.

is_full_population property

is_full_population: bool

True when FP/TN cells are populated (full population mode).

summary

summary() -> str

Return a human-readable per-archetype metric table.

RetrievalResult dataclass

RetrievalResult(
    nearest_cluster: int | None,
    is_novel: bool,
    nearest_indices: ndarray,
    nearest_distances: ndarray,
    distance_to_nearest_medoid: float,
    cluster_threshold: float,
    explanation: DataFrame | None = None,
    nearest_original_features: DataFrame | None = None,
)

Similar historical cases for one query, plus its archetype assignment.

nearest_cluster is the nearest archetype (None only when the profile has none); is_novel means the query lies outside that archetype's radius, or the archetype has no radius; cluster_threshold is that radius (NaN when none is stored). The assignment follows :mod:parleml_mli.archetypes.profiling.assignment's ball rule. retrieve_new_cases_original_space computes no assignment and leaves these fields at None / False / NaN.

FingerprintResult dataclass

FingerprintResult(
    rule_ids: list[int] = list(),
    cluster_ids: list[int] = list(),
    fire_rate: ndarray = (
        lambda: zeros((0, 0), dtype=float)
    )(),
    specificity: ndarray = (
        lambda: zeros((0, 0), dtype=float)
    )(),
    rule_total_matches: ndarray = (
        lambda: zeros((0,), dtype=int)
    )(),
    cluster_sizes: ndarray = (
        lambda: zeros((0,), dtype=int)
    )(),
    n_total: int = 0,
)

Rule-firing fingerprints across archetypes.

Attributes:

Name Type Description
rule_ids list[int]

rule_id from each rule in the global ruleset, in source order.

cluster_ids list[int]

Sorted cluster ids included in the fingerprint (drops -1 / noise by convention).

fire_rate ndarray

(n_clusters, n_rules) matrix. fire_rate[i, j] is the fraction of cluster cluster_ids[i]'s members that match rule_ids[j].

specificity ndarray

(n_clusters, n_rules) matrix. specificity[i, j] is the fraction of all rule-j matches that fall inside cluster cluster_ids[i].

rule_total_matches ndarray

(n_rules,) vector — the total number of rows where each rule fires across the entire X.

cluster_sizes ndarray

(n_clusters,) vector — number of rows assigned to each cluster (excluding noise).

n_total int

Total number of rows in X (including noise).

for_cluster

for_cluster(
    cluster_id: int,
    *,
    min_fire_rate: float = _DEFAULT_FIRE_RATE_THRESHOLD,
    min_specificity: float = 0.0,
    sort_by: str = "fire_rate"
) -> DataFrame

Return a per-rule DataFrame characterising one archetype.

Columns: rule_id, fire_rate, specificity, n_matches_in_cluster, rule_total_matches. Sorted by sort_by ("fire_rate" default; "specificity" to surface rules unique to this archetype).

for_rule

for_rule(
    rule_id: int,
    *,
    min_fire_rate: float = 0.0,
    min_specificity: float = _DEFAULT_SPECIFICITY_THRESHOLD,
    sort_by: str = "specificity"
) -> DataFrame

Return a per-cluster DataFrame for one rule.

Inverse of :meth:for_cluster — surfaces which archetypes a given rule characterises, sorted by specificity by default.

to_dict

to_dict() -> dict[str, Any]

Schema-versioned dict for report.json.

Result payload types

Returned by the retrieval / reporting surfaces — predict_archetype returns an ArchetypePrediction, generate_report returns a ReportResult.

ArchetypePrediction dataclass

ArchetypePrediction(
    labels: ndarray,
    nearest_cluster: ndarray,
    distance_ratio: ndarray,
    distances: ndarray,
)

Rich archetype assignment with soft confidence.

Attributes:

Name Type Description
labels ndarray

Assigned archetype id per case. -1 = novel: outside the nearest archetype's radius, that archetype has no radius, or the profile has no archetypes.

nearest_cluster ndarray

Nearest archetype per case regardless of radius; -1 only when the profile has no archetypes.

distance_ratio ndarray

distance_to_medoid / radius for the nearest archetype. Values <= 1 mean inside the ball (assigned); values > 1 mean outside (novel) — the further above 1, the less similar. A zero radius gives 0 on the medoid and inf off it; an infinite or missing radius gives NaN; no archetypes gives inf.

distances ndarray

Raw distance to the nearest medoid per case (inf with no archetypes).

ReportResult dataclass

ReportResult(
    output_dir: str,
    report_path: str,
    llm_report_path: str | None,
    report: dict[str, object],
    figure_paths: dict[str, str],
)

Return type for :meth:ProfilingResult.generate_report.

Per-class profile

The lightweight per-class slice exposed by result.classes[class_key] / result.profile_for(...) — binary runs carry one entry, multiclass runs one per class (the one-vs-rest fan-out).

ClassProfile dataclass

ClassProfile(
    class_label: Any,
    class_key: str,
    target_mask: ndarray,
    base_target_rate: float,
    synthetic: bool = False,
    labels_: ndarray | None = None,
    clustering_result: "ClusteringResult | None" = None,
    summary_df: DataFrame | None = None,
    contamination_df: DataFrame | None = None,
    medoids_df: DataFrame | None = None,
    characterization_: dict[int, DataFrame] = dict(),
    umap_embedding_: ndarray | None = None,
    cluster_thresholds_: dict[int, float] = dict(),
    stability_: "StabilityResult | None" = None,
    min_odds_ratio_: float = 3.0,
    min_stability_: float = DEFAULT_MIN_STABILITY,
    error_analysis_: "ErrorAnalysisResult | None" = None,
    rules: dict["RuleEngine", "RuleSetSummary"] = dict(),
    rule_fingerprints: dict["RuleEngine", Any] = dict(),
    fairness: dict[str, "FairnessResult"] = dict(),
    pdp: dict[str, "PDPResult"] = dict(),
    ale: dict[str, "ALEResult"] = dict(),
    permutation_importance: "PermutationImportanceResult | None" = None,
    error_tree: "ErrorTreeResult | None" = None,
    psi: "PSIResult | None" = None,
    compacity: "CompacityResult | None" = None,
    _space: "DistanceSpace | None" = None,
    _target_data_t: ndarray | None = None,
    _medoid_data_t: ndarray | None = None,
    _nn_index: Any | None = None,
    _nn_index_original: Any | None = None,
    _X_target: DataFrame | None = None,
    _X_target_orig: DataFrame | None = None,
    _predictions_target: ndarray | None = None,
    _target_shap_values: ndarray | None = None,
)

One slice of the supervised profiling output.

See mli/docs/multiclass-plan.md §3.2 for the full schema and §3.2.1 for the synthetic-mask path.

is_synthetic property

is_synthetic: bool

Alias for :attr:synthetic — easier to read at the call site.

cluster_ids

cluster_ids() -> list[int]

Sorted list of non-noise archetype cluster ids.

get_rules

get_rules(
    engine: "RuleEngine | str" = "positive_miner",
) -> "RuleSetSummary | None"

Convenience accessor: profile.get_rules("positive_miner").

Per-case justification

Bind a model with ProfilingResult.justifier and call justify per row, or justify_batch to explain a whole frame at the cost of one batch — see the decision-support guide.

Justifier

Justifier(
    result: "ProfilingResult",
    *,
    pipeline: object | None = None
)

Model-bound justification engine — see :meth:ProfilingResult.justifier.

Obtain one via result.justifier(pipeline) rather than constructing it directly. The adapter and the active per-class view are resolved once at construction; :meth:justify is then cheap to call per row, and :meth:justify_batch explains many rows for the fixed cost of one.

justify

justify(
    x_raw: DataFrame,
    *,
    calibrated: bool = True,
    active_class: Any = None,
    top_k_shap: int = 10,
    top_k_neighbors: int = 5,
    top_k_rules: int = 5,
    rule_sort: str = "confidence",
    include_shap: bool = True,
    include_rules: bool = True,
    include_neighbors: bool = True,
    include_ice: bool = False,
    include_loco: bool = False,
    include_counterfactual: bool = False,
    top_k_ice: int = 3,
    top_k_loco: int = 10,
    counterfactual_target_archetype: int | None = None
) -> "CaseExplanation"

Justify a single prediction → a :class:parleml_mli._dsd_types.CaseExplanation.

Composes the predict / SHAP / archetype / rules / nearest-neighbour primitives into a structured decision-support payload — the cold-path object an analyst-facing DB / viewer consumes after the production hot path has already returned a fast score.

Parameters:

Name Type Description Default
x_raw DataFrame

Single-row raw input frame (1 × p). For sklearn-mode this must already be in engineered-feature shape; for pipeline-mode it can be the raw frame the bound pipeline transforms internally.

required
calibrated bool

When True, request the calibrated probability flavour from the pipeline. Ignored for sklearn-mode (no calibrator) and for regression. The returned CaseExplanation records calibration_active honestly (True only when the bound pipeline actually carries a fitted calibrator).

True
active_class Any

Class label whose probability column drives prediction labelling and SHAP slicing — as published on classes_, of any dtype (2, 1.0, "decline"), never a column index. Defaults to the label of the class the bound per-class view is locked on (the positive class for binary). None for regression.

None
top_k_shap int

See :class:parleml_mli._dsd_types.CaseExplanation.

10
top_k_neighbors int

See :class:parleml_mli._dsd_types.CaseExplanation.

10
top_k_rules int

Max fired rules to keep after ranking by rule_sort (<= 0 keeps all). The pre-cap count is on CaseExplanation.n_fired_rules_total.

5
rule_sort str

Which metric orders fired rules before the top_k_rules cap: "confidence" (default), "support", "lift" or "coverage" — see :func:parleml_mli._case_explanation.build_case_explanation.

'confidence'
include_shap bool

Gating flags. The hot path normally calls this with everything on; a worker that has already paid for SHAP elsewhere can pass include_shap=False (or any other flag) to skip the stage. Disabling SHAP on a SHAP-space result also short-circuits archetype + neighbour retrieval — they would otherwise raise inside the index.

True
include_rules bool

Gating flags. The hot path normally calls this with everything on; a worker that has already paid for SHAP elsewhere can pass include_shap=False (or any other flag) to skip the stage. Disabling SHAP on a SHAP-space result also short-circuits archetype + neighbour retrieval — they would otherwise raise inside the index.

True
include_neighbors bool

Gating flags. The hot path normally calls this with everything on; a worker that has already paid for SHAP elsewhere can pass include_shap=False (or any other flag) to skip the stage. Disabling SHAP on a SHAP-space result also short-circuits archetype + neighbour retrieval — they would otherwise raise inside the index.

True
include_ice bool

Stage-2 sensitivity flags (see mli/docs/local-explanations-plan.md). All three default off — the cold-path worker opts in; the viewer enables ICE / counterfactual lazily on expander open and LOCO automatically on every render.

False
include_loco bool

Stage-2 sensitivity flags (see mli/docs/local-explanations-plan.md). All three default off — the cold-path worker opts in; the viewer enables ICE / counterfactual lazily on expander open and LOCO automatically on every render.

False
include_counterfactual bool

Stage-2 sensitivity flags (see mli/docs/local-explanations-plan.md). All three default off — the cold-path worker opts in; the viewer enables ICE / counterfactual lazily on expander open and LOCO automatically on every render.

False
top_k_ice int

Trim the corresponding outputs.

3
top_k_loco int

Trim the corresponding outputs.

3
counterfactual_target_archetype int | None

Explicit override for the safer-archetype picker.

None

justify_batch

justify_batch(
    x_raw: DataFrame,
    *,
    calibrated: bool = True,
    active_class: Any = None,
    top_k_shap: int = 10,
    top_k_rules: int = 5,
    rule_sort: str = "confidence",
    include_shap: bool = True,
    include_rules: bool = True
) -> list["CaseExplanation"]

Justify every row of x_raw at once → one :class:parleml_mli._dsd_types.CaseExplanation per row.

Row i carries what justify(x_raw.iloc[[i]]) returns with the same arguments for the prediction, probabilities, decision threshold, SHAP drivers and fired rules. The difference is cost: the feature pipeline, the model and SHAP each run once over the frame and every rule is evaluated once over it, so the fixed cost of each stage (encoders, vectorisers, the SHAP kernel's setup) is paid once per batch rather than once per row. This is the path for explaining a dataset — e.g. per-row reason codes for a batch-scoring job. One exception keeps the row equality honest: a SHAP kernel that explains a row differently depending on the rows passed with it (CatBoost's, for a model trained with monotone constraints) is detected on the batch's first row and then run row by row, so a row's drivers never depend on its batch-mates.

The stages that place one case stay on :meth:justify: archetype assignment (an archetype space can place a query relative to the other queries it is transformed with), nearest neighbours and the Stage-2 sensitivity primitives (ICE / LOCO / counterfactual). Their fields carry the not-placed default (nearest_cluster=-1, matched_archetype=None, no neighbours), and each stage time in elapsed_ms is the batch's, amortised per row.

A SHAP or rules failure over the batch is retried row by row, so it only costs the rows that cause it (they come back without drivers / without rules, as a single-row justify returns them). A failure to encode or score the batch raises — the caller decides how to isolate the offending row (e.g. by justifying that batch's rows one at a time).

Parameters are those of :meth:justify; x_raw may hold any number of rows (none returns []).

lookalikes

lookalikes(
    x_raw: DataFrame, *, top_k: int = 5
) -> list["RetrievalResult"]

Raw-feature nearest neighbours for x_raw (model-free).

A thin passthrough to :meth:ProfilingResult.retrieve_new_cases_original_space — the "lookalike cases by raw feature values" lens. Needs no model call, so it works even for a model-less profile that was saved with the original feature matrix.

Per-case payload types

Returned by Justifier.justify — see the decision-support guide.

CaseExplanation dataclass

CaseExplanation(
    x_raw: DataFrame,
    x_encoded: DataFrame,
    proba_calibrated: ndarray | None,
    proba_raw: ndarray | None,
    prediction: Any,
    decision_threshold: float | None,
    shap_drivers: list[ShapDriver],
    matched_archetype: int | None,
    nearest_cluster: int,
    distance_to_medoid: float,
    distance_ratio: float,
    is_novel: bool,
    archetype_name: str | None,
    archetype_severity: str | None,
    fired_rules: list[FiredRule],
    nearest_indices: ndarray,
    nearest_distances: ndarray,
    nearest_original_features: DataFrame | None,
    calibration_active: bool,
    inference_mode: Literal["sklearn", "pipeline"],
    active_class: Any,
    elapsed_ms: dict[str, float | str] = dict(),
    ice_curves: list["IceCurve"] = list(),
    loco_scores: list["LocoScore"] = list(),
    archetype_counterfactual: "ArchetypeCounterfactual | None" = None,
    n_fired_rules_total: int = 0,
)

Everything justify produces for a single row.

Construction is driven by :meth:parleml_mli._justifier.Justifier.justify and :mod:parleml_mli._case_explanation; consumers should treat this object as read-only.

See mli/docs/inference-and-dsd-plan.md §5.1 for the field contract and §5.2 for the body sketch that derives each field.

ShapDriver dataclass

ShapDriver(
    feature: str,
    origin_feature: str,
    kind: str,
    value: float | str | None,
    shap: float,
    abs_shap_value: float,
)

One row of the top-K SHAP driver table.

Field names match the inference-and-dsd-plan §5.1 wire contract (origin_feature + shap); kind and abs_shap_value are additive viewer hints not required by the contract.

Attributes:

Name Type Description
feature str

Engineered feature name as the algorithm sees it.

origin_feature str

Business-readable origin column from the encoding registry; falls back to feature for raw / unknown encodings.

kind str

Encoding kind ("RAW", "WE", "CN", …) from the registry. "RAW" when the registry has no record.

value float | str | None

The encoded value used at scoring time.

shap float

Signed SHAP attribution.

abs_shap_value float

abs(shap) — what the top-K is sorted by.

FiredRule dataclass

FiredRule(
    rule_id: str,
    engine: str,
    antecedent: str,
    antecedent_components: list[dict[str, Any]],
    predicted_class: int,
    confidence: float,
    support: float,
    coverage: float,
    lift: float | None = None,
    precision_holdout: float | None = None,
    precision_lower_holdout: float | None = None,
    holds_up: HoldsUp | None = None,
)

A single rule that fired on the case.

Carries the rule-chip shape the backend projects to the UI so the Inference page renders the rule with the same chip widgets used everywhere else (consumers read antecedent_components).

confidence (precision), support, coverage and lift are the rule's Train numbers — measured on the rows it was found on. precision_holdout and holds_up are its Test side: its precision on held-out rows and the verdict on them (see :func:parleml_mli.rules.scoring.holds_up).

Next