Source code for elbow_helper.config

"""Configuration for the robust knee detector.

All positional thresholds are expressed in **normalized x-range units** (the
x-axis is scaled to ``[0, 1]`` during preprocessing), so they are independent
of the absolute scale of the caller's data.

The defaults follow the "first practical prototype" scope: modest replicate
counts so the full pipeline runs in seconds. For validation-grade runs raise
``bootstrap_replicates`` to 500 and ``null_replicates`` to 1000.

Author
------
Warith Harchaoui, <warith.harchaoui@deraison.ai>
"""

from __future__ import annotations

from dataclasses import dataclass, replace
from typing import Optional, Tuple


[docs] @dataclass(frozen=True) class RobustKneeConfig: """Immutable bundle of thresholds and search settings. Use :meth:`with_` to derive a tweaked copy (the dataclass is frozen). """ # --- data adequacy --- min_samples: int = 20 # --- scale-space search --- smoothing_fractions: Tuple[float, ...] = ( 0.0, 0.02, 0.03, 0.05, 0.08, 0.12, 0.18, 0.25, ) sensitivity_fractions: Tuple[float, ...] = (0.0, 0.01, 0.02, 0.05) # --- global shape compatibility --- min_spearman_abs: float = 0.60 max_direction_violation_rate: float = 0.25 # --- candidate basic filters --- boundary_margin: float = 0.10 min_side_points: int = 5 min_prominence: float = 0.05 min_noise_prominence_ratio: float = 4.0 # --- persistence clustering --- # cluster_tolerance and max_neighbor_shift are calibrated (ยง19) slightly # above the plan's 0.05 to absorb the one-to-two-sample locator # discretization jitter seen at modest sample sizes (n ~ 60-100). cluster_tolerance: float = 0.06 min_consecutive_scales: int = 3 min_sensitivity_support: float = 0.70 max_cluster_mad: float = 0.03 max_neighbor_shift: float = 0.07 # --- uniqueness --- secondary_support_frac: float = 0.30 min_dominance_ratio: float = 2.0 # --- slope / model confirmation --- slope_left_window: Tuple[float, float] = (0.15, 0.03) # (far, near) offsets slope_right_window: Tuple[float, float] = (0.03, 0.15) # (near, far) offsets min_slope_contrast: float = 0.30 min_cv_improvement: float = 0.10 min_bic_improvement: float = 10.0 cv_folds: int = 5 # --- bootstrap robustness --- bootstrap_replicates: int = 100 min_bootstrap_detection_rate: float = 0.90 max_ci90_width: float = 0.10 min_primary_cluster_rate: float = 0.80 max_secondary_cluster_rate: float = 0.15 max_bootstrap_median_shift: float = 0.03 # --- no-knee null test --- null_replicates: int = 200 max_null_p_value: float = 0.01 # --- reproducibility --- random_seed: Optional[int] = None
[docs] def with_(self, **changes) -> "RobustKneeConfig": """Return a copy of this config with ``changes`` applied. Parameters ---------- **changes Field overrides, e.g. ``config.with_(bootstrap_replicates=500)``. Returns ------- RobustKneeConfig A new, independent configuration. """ return replace(self, **changes)
[docs] @dataclass(frozen=True) class RobustKneesConfig: """Immutable settings for :func:`elbow_helper.robust_knees` (plural). The multi-knee search ships the combination validated in ``research/multiknee/RESULTS.md``: dynamic-program search, the subtractive-sign modified BIC as the selection criterion, and a Bonferroni-gated sequential permutation test layered on top by default, matching this package's design priority of minimising false-positive knees. Use :meth:`with_` to derive a tweaked copy. """ # --- data adequacy --- min_samples: int = 20 # --- search --- k_max: int = 4 min_seg_fraction: float = 0.08 # --- false-positive control --- fwer_alpha: float = 0.05 fwer_permutations: int = 200 require_fwer_confirmation: bool = True # --- reproducibility --- random_seed: Optional[int] = None
[docs] def with_(self, **changes) -> "RobustKneesConfig": """Return a copy of this config with ``changes`` applied.""" return replace(self, **changes)