Source code for egttools.numerical.indicators
"""
Indicator result types and statistics helpers for PairwiseComparisonNumerical.
The high-level ``estimate_stationary_indicators`` method is implemented as a
pybind11 binding directly on the C++ class (see ``cpp/src/pybind11_files/methods.cpp``).
This module supplies:
* ``StationaryIndicatorResult`` — the dataclass returned by that method.
* ``_bootstrap_ci`` — the non-parametric bootstrap helper called from the
C++ binding (imported at runtime to avoid circular-import issues).
"""
from __future__ import annotations
import warnings
from dataclasses import dataclass, field
import numpy as np
__all__ = ["StationaryIndicatorResult"]
[docs]
@dataclass
class StationaryIndicatorResult:
"""Result of ``PairwiseComparisonNumerical.estimate_stationary_indicators``.
Attributes
----------
mean : np.ndarray
Grand mean across all completed runs, shape ``(nb_indicators,)``.
confidence_interval : tuple[np.ndarray, np.ndarray]
``(low, high)`` non-parametric bootstrap confidence interval at the
requested confidence level, each of shape ``(nb_indicators,)``.
No normality assumption is made; this is appropriate for skewed or
bimodal indicator distributions (e.g. rare-event group success).
nb_runs_used : int
Number of simulation runs actually completed. Less than ``nb_runs``
when tolerance-based early stopping triggered.
converged : bool
``True`` if the simulation stopped early because the L1 norm of the
change in column-means between consecutive batches fell below
``tolerance``.
per_run_values : np.ndarray or None
Raw per-run means of shape ``(nb_runs_used, nb_indicators)`` when
``verbose=True``, otherwise ``None``. Use this for custom downstream
statistics (quantiles, KDE, empirical CDF, etc.).
"""
mean: np.ndarray
confidence_interval: tuple[np.ndarray, np.ndarray]
nb_runs_used: int
converged: bool
per_run_values: np.ndarray | None = field(default=None, repr=False)
def _bootstrap_ci(
per_run: np.ndarray,
confidence: float,
n_bootstrap: int,
) -> tuple[np.ndarray, np.ndarray]:
"""Non-parametric percentile bootstrap CI on column means.
Called from the pybind11 binding of
``PairwiseComparisonNumerical.estimate_stationary_indicators``.
Parameters
----------
per_run : (nb_runs, nb_indicators)
confidence : float in (0, 1)
n_bootstrap : int
Returns
-------
(ci_low, ci_high) each of shape (nb_indicators,)
"""
alpha = 1.0 - confidence
n = per_run.shape[0]
if n < 2:
warnings.warn(
f"Too few simulation runs (nb_runs_used={n}) to compute a bootstrap "
"confidence interval. Returning NaN for CI bounds.",
RuntimeWarning,
stacklevel=4,
)
nan = np.full(per_run.shape[1], np.nan)
return nan, nan
rng = np.random.default_rng()
indices = rng.integers(0, n, size=(n_bootstrap, n))
boot_means = per_run[indices].mean(axis=1) # (n_bootstrap, nb_indicators)
ci_low = np.percentile(boot_means, 100.0 * alpha / 2.0, axis=0)
ci_high = np.percentile(boot_means, 100.0 * (1.0 - alpha / 2.0), axis=0)
return ci_low, ci_high