Skip to content

Getting started

Install

pip install triadxai            # core: numpy, pandas, scipy, lightgbm
pip install triadxai[viz]       # + matplotlib waterfalls
pip install triadxai[ebm]       # + InterpretML EBM adapter

Exact mode: additive LightGBM

fit_lgbm_gam fits a LightGBM booster constrained so every tree splits on a single feature — an additive model — plus K bootstrap replicas that provide per-bin epistemic variance:

import numpy as np
from triadxai import TriadExplainer, fit_lgbm_gam
from triadxai.synthetic import make_missingness

data = make_missingness(n=5000, mechanism="MNAR", missing_effect=1.0, seed=0)
result = fit_lgbm_gam(data.X, data.y, n_bags=8, seed=0)

explainer = TriadExplainer(result)
exp = explainer.explain(data.X.head(100))

exp.channels          # long frame: (instance, feature) x [I, D, M, w, oov]
exp.epistemic_score() # ES, ES_signed, KR per instance
exp.reasons(k=4)      # ranked, group-tagged adverse-action reasons
exp.plot_waterfall(0) # three-channel waterfall (D hatched)

Thin-file rows (many missing values) show low knowledge ratio KR and Group B ("insufficient information") reasons; rows with well-supported derogatory signal show Group A reasons carried by the I channel.

The audit payload required by spec §4.4 is available as explainer.shrinkage (per-term τ², σ², k) plus the per-feature w column in the channels frame.

Approximate mode: any LightGBM booster

For an unconstrained (interacting) booster, TRIAD reallocates native TreeSHAP attributions instead. Provide bag replicas for the variance signal:

import lightgbm as lgb
from triadxai.bagging import fit_bagged

def fit_fn(X, y, seed):
    return lgb.train(
        {"objective": "binary", "verbosity": -1, "seed": seed},
        lgb.Dataset(X, label=y), num_boost_round=300,
    )

booster = fit_fn(data.X, data.y, 0)
bags = fit_bagged(fit_fn, data.X, data.y, n_bags=8, seed=0)

explainer = TriadExplainer(booster, bag_boosters=bags, X_ref=data.X)
exp = explainer.explain(data.X.head(100))
exp.approximate   # True — waterfalls carry an "approximate" badge

EBM (optional extra)

from interpret.glassbox import ExplainableBoostingClassifier

ebm = ExplainableBoostingClassifier(interactions=2, outer_bags=8)
ebm.fit(X_train, y_train)
exp = TriadExplainer(ebm).explain(X_test)   # exact mode; outer-bag SDs as v