Skip to content

metrics

Validation metrics: reconciliation, channel mass, bootstrap stability.

bootstrap_stability

bootstrap_stability(
    fit_fn: Callable[[DataFrame, ndarray, int], Any],
    X: DataFrame,
    y: ndarray,
    X_eval: DataFrame,
    *,
    n_boot: int = 10,
    seed: int = 0
) -> pd.DataFrame

Across-refit SD of per-feature mean channel values (H3 protocol, spec 10.2).

fit_fn(X_boot, y_boot, boot_seed) must return a model consumable by :class:triadxai.local.TriadExplainer.

Source code in src/triadxai/metrics.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
def bootstrap_stability(
    fit_fn: Callable[[pd.DataFrame, np.ndarray, int], Any],
    X: pd.DataFrame,
    y: np.ndarray,
    X_eval: pd.DataFrame,
    *,
    n_boot: int = 10,
    seed: int = 0,
) -> pd.DataFrame:
    """Across-refit SD of per-feature mean channel values (H3 protocol, spec 10.2).

    ``fit_fn(X_boot, y_boot, boot_seed)`` must return a model consumable by
    :class:`triadxai.local.TriadExplainer`.
    """
    rng = np.random.default_rng(seed)
    y = np.asarray(y)
    per_boot = []
    for boot in range(n_boot):
        idx = rng.integers(0, len(X), size=len(X))
        X_boot = X.iloc[idx].reset_index(drop=True)
        model = fit_fn(X_boot, y[idx], seed * 10_000 + boot + 1)
        explanation = TriadExplainer(model).explain(X_eval)
        per_boot.append(explanation.channels.groupby("feature")[["I", "D", "M"]].mean())
    stacked = pd.concat(per_boot, keys=range(n_boot))
    return stacked.groupby(level=1).std(ddof=1)

channel_mass

channel_mass(explanation: TriadExplanation) -> pd.Series

Portfolio share of mean |I|, |D|, |M| (sums to 1).

Source code in src/triadxai/metrics.py
33
34
35
36
37
38
39
def channel_mass(explanation: TriadExplanation) -> pd.Series:
    """Portfolio share of mean |I|, |D|, |M| (sums to 1)."""
    mass = explanation.channels[["I", "D", "M"]].abs().mean()
    total = mass.sum()
    if total == 0:
        return mass
    return mass / total

reconciliation_error

reconciliation_error(
    explanation: TriadExplanation, model: Any, X: DataFrame
) -> float

Max |sum of channels + intercept - model raw score| (C2; 0 by construction).

Source code in src/triadxai/metrics.py
27
28
29
30
def reconciliation_error(explanation: TriadExplanation, model: Any, X: pd.DataFrame) -> float:
    """Max |sum of channels + intercept - model raw score| (C2; 0 by construction)."""
    totals = explanation.channels.groupby("instance")[["I", "D", "M"]].sum().sum(axis=1).to_numpy()
    return float(np.max(np.abs(totals + explanation.intercept - _raw_score(model, X))))