Distribution Drift Detection
stats has two-sample tests for A/B experiment outcomes (proportions and
means), but no Population Stability Index and no Kolmogorov-Smirnov two-sample
test for the canonical “is today’s data shaped like the baseline” check. This
adds PSI, KS, and a categorical-drift summary that pair with data_profile.
Pure standard library (math / bisect / collections + reuse of
stats.percentile); imports no PySide6. Every function is pure
(sequences in, dict/float out), so it is fully deterministic in CI.
Headless API
from je_auto_control import psi, ks_two_sample, categorical_drift, detect_drift
score = psi(reference, current) # Population Stability Index
ks = ks_two_sample(reference, current) # {statistic, p_value}
report = detect_drift(reference, current) # {psi, drifted, ks}
cat = categorical_drift(ref_labels, cur_labels)
# {chi_square, total_variation, categories}
psi bins current against reference quantile edges and sums the
log-ratio contribution per bin (0 for identical distributions, growing as they
diverge). ks_two_sample returns the maximum empirical-CDF gap and a p-value
from the Kolmogorov distribution. categorical_drift compares label
frequencies via a chi-square statistic and the total-variation distance.
detect_drift wraps the numeric path into one report with a drifted
verdict at threshold (default 0.25).
Executor commands
AC_detect_drift takes reference / current numeric lists (and
optional threshold / bins) and returns {psi, drifted, ks}.
AC_categorical_drift returns the categorical summary. Both are exposed as
MCP tools (ac_detect_drift / ac_categorical_drift) and as Script Builder
commands under Data.