Source robust scaler
neureptrace.decoding.source_robust_scaler implements source-only robust feature scaling.
The protocol is Category 1 / strict source-only. Location and scale statistics are estimated from source rows only. Evaluation rows are transformed with the fitted statistics but are not used to fit them.
Typical usage:
from neureptrace.decoding.source_robust_scaler import fit_source_robust_scaler
result = fit_source_robust_scaler(
source_features=X_source,
test_features=X_target,
config={"center": "median", "scale": "iqr"},
)
X_source_scaled = result.train_features
X_target_scaled = result.test_features
Supported center modes: median, mean, none.
Supported scale modes: iqr, mad, std, none.
neureptrace.decoding.source_robust_scaler
Source-only robust feature scaling for cross-subject decoding.
This module fits feature-wise location and scale statistics from source rows only
and applies the fitted transform to source and evaluation feature matrices. The
implementation is a strict Protocol-1 preprocessing helper: evaluation rows are
transformed but never used to fit the scaler.
SourceRobustScalerConfig
dataclass
Configuration for source-only robust feature scaling.
Source code in src/neureptrace/decoding/source_robust_scaler.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 | @dataclass(frozen=True, slots=True)
class SourceRobustScalerConfig:
"""Configuration for source-only robust feature scaling."""
center: str = "median"
scale: str = "iqr"
lower_quantile: float = DEFAULT_LOWER_QUANTILE
upper_quantile: float = DEFAULT_UPPER_QUANTILE
epsilon: float = DEFAULT_EPSILON
def __post_init__(self) -> None:
"""Validate direct dataclass construction as strictly as the public helper."""
lower = _unit_interval_float(self.lower_quantile, name="lower_quantile")
upper = _unit_interval_float(self.upper_quantile, name="upper_quantile")
if lower >= upper:
raise ValueError("lower_quantile must be smaller than upper_quantile.")
object.__setattr__(self, "center", normalize_center_mode(self.center))
object.__setattr__(self, "scale", normalize_scale_mode(self.scale))
object.__setattr__(self, "lower_quantile", lower)
object.__setattr__(self, "upper_quantile", upper)
object.__setattr__(self, "epsilon", _positive_float(self.epsilon, name="epsilon"))
|
__post_init__()
Validate direct dataclass construction as strictly as the public helper.
Source code in src/neureptrace/decoding/source_robust_scaler.py
36
37
38
39
40
41
42
43
44
45
46
47 | def __post_init__(self) -> None:
"""Validate direct dataclass construction as strictly as the public helper."""
lower = _unit_interval_float(self.lower_quantile, name="lower_quantile")
upper = _unit_interval_float(self.upper_quantile, name="upper_quantile")
if lower >= upper:
raise ValueError("lower_quantile must be smaller than upper_quantile.")
object.__setattr__(self, "center", normalize_center_mode(self.center))
object.__setattr__(self, "scale", normalize_scale_mode(self.scale))
object.__setattr__(self, "lower_quantile", lower)
object.__setattr__(self, "upper_quantile", upper)
object.__setattr__(self, "epsilon", _positive_float(self.epsilon, name="epsilon"))
|
SourceRobustScalerStats
dataclass
Fitted source-only scaler statistics.
Source code in src/neureptrace/decoding/source_robust_scaler.py
| @dataclass(frozen=True, slots=True)
class SourceRobustScalerStats:
"""Fitted source-only scaler statistics."""
location: np.ndarray
scale: np.ndarray
n_rows: int
|
SourceRobustScalerResult
dataclass
Scaled feature matrices and fitted source-only statistics.
Source code in src/neureptrace/decoding/source_robust_scaler.py
| @dataclass(frozen=True, slots=True)
class SourceRobustScalerResult:
"""Scaled feature matrices and fitted source-only statistics."""
train_features: np.ndarray
test_features: np.ndarray
stats: SourceRobustScalerStats
metadata: dict[str, Any] = field(default_factory=dict)
|
fit_source_robust_scaler(*, source_features, test_features, config=None)
Fit robust scaler statistics on source rows and transform matrices.
Source code in src/neureptrace/decoding/source_robust_scaler.py
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109 | def fit_source_robust_scaler(
*,
source_features: Sequence[Sequence[float]] | np.ndarray,
test_features: Sequence[Sequence[float]] | np.ndarray,
config: SourceRobustScalerConfig | Mapping[str, Any] | None = None,
) -> SourceRobustScalerResult:
"""Fit robust scaler statistics on source rows and transform matrices."""
cfg = source_robust_scaler_config() if config is None else _coerce_config(config)
source = _feature_matrix(source_features, name="source_features")
test = _feature_matrix(test_features, name="test_features")
if source.shape[1] != test.shape[1]:
raise ValueError(f"source_features and test_features must have the same feature width: {source.shape[1]} != {test.shape[1]}.")
stats = fit_source_robust_scaler_stats(source, config=cfg)
train = apply_source_robust_scaler(source, stats=stats)
test_scaled = apply_source_robust_scaler(test, stats=stats)
metadata = {
"source_robust_scaler": True,
"source_robust_scaler_protocol": SOURCE_ROBUST_SCALER_PROTOCOL,
"source_robust_scaler_protocol_category": SOURCE_ROBUST_SCALER_CATEGORY,
"source_robust_scaler_uses_source_features": True,
"source_robust_scaler_uses_source_labels": False,
"source_robust_scaler_uses_test_features_for_fitting": False,
"source_robust_scaler_uses_test_labels": False,
"source_robust_scaler_valid_for_strict_source_only": True,
"source_robust_scaler_valid_for_benchmark": True,
"source_robust_scaler_n_source_rows": int(source.shape[0]),
"source_robust_scaler_n_test_rows": int(test.shape[0]),
"source_robust_scaler_feature_dim": int(source.shape[1]),
"source_robust_scaler_center": cfg.center,
"source_robust_scaler_scale": cfg.scale,
"source_robust_scaler_lower_quantile": float(cfg.lower_quantile),
"source_robust_scaler_upper_quantile": float(cfg.upper_quantile),
"source_robust_scaler_epsilon": float(cfg.epsilon),
}
return SourceRobustScalerResult(
train_features=train.astype(np.float32, copy=False),
test_features=test_scaled.astype(np.float32, copy=False),
stats=stats,
metadata=metadata,
)
|
source_robust_scaler_config(*, center='median', scale='iqr', lower_quantile=DEFAULT_LOWER_QUANTILE, upper_quantile=DEFAULT_UPPER_QUANTILE, epsilon=DEFAULT_EPSILON)
Normalize public robust-scaler options.
Source code in src/neureptrace/decoding/source_robust_scaler.py
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132 | def source_robust_scaler_config(
*,
center: str | None = "median",
scale: str | None = "iqr",
lower_quantile: float | str = DEFAULT_LOWER_QUANTILE,
upper_quantile: float | str = DEFAULT_UPPER_QUANTILE,
epsilon: float | str = DEFAULT_EPSILON,
) -> SourceRobustScalerConfig:
"""Normalize public robust-scaler options."""
lower = _unit_interval_float(lower_quantile, name="lower_quantile")
upper = _unit_interval_float(upper_quantile, name="upper_quantile")
if lower >= upper:
raise ValueError("lower_quantile must be smaller than upper_quantile.")
return SourceRobustScalerConfig(
center=normalize_center_mode(center),
scale=normalize_scale_mode(scale),
lower_quantile=lower,
upper_quantile=upper,
epsilon=_positive_float(epsilon, name="epsilon"),
)
|
fit_source_robust_scaler_stats(source_features, *, config=None)
Fit source-only location and scale vectors.
Source code in src/neureptrace/decoding/source_robust_scaler.py
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167 | def fit_source_robust_scaler_stats(
source_features: Sequence[Sequence[float]] | np.ndarray,
*,
config: SourceRobustScalerConfig | Mapping[str, Any] | None = None,
) -> SourceRobustScalerStats:
"""Fit source-only location and scale vectors."""
cfg = source_robust_scaler_config() if config is None else _coerce_config(config)
source = _feature_matrix(source_features, name="source_features")
if cfg.center == "median":
location = np.median(source, axis=0)
elif cfg.center == "mean":
location = np.mean(source, axis=0)
elif cfg.center == "none":
location = np.zeros(source.shape[1], dtype=float)
else: # pragma: no cover - guarded by normalization
raise ValueError(f"Unhandled center mode {cfg.center!r}.")
if cfg.scale == "iqr":
upper = np.quantile(source, cfg.upper_quantile, axis=0)
lower = np.quantile(source, cfg.lower_quantile, axis=0)
scale = upper - lower
elif cfg.scale == "mad":
med = np.median(source, axis=0)
scale = 1.4826 * np.median(np.abs(source - med), axis=0)
elif cfg.scale == "std":
scale = np.std(source - np.mean(source, axis=0, keepdims=True), axis=0, ddof=1 if source.shape[0] > 1 else 0)
elif cfg.scale == "none":
scale = np.ones(source.shape[1], dtype=float)
else: # pragma: no cover - guarded by normalization
raise ValueError(f"Unhandled scale mode {cfg.scale!r}.")
scale = np.maximum(np.asarray(scale, dtype=float), cfg.epsilon)
return SourceRobustScalerStats(location=np.asarray(location, dtype=float), scale=scale, n_rows=int(source.shape[0]))
|
apply_source_robust_scaler(features, *, stats)
Apply fitted source-only robust scaler statistics.
Source code in src/neureptrace/decoding/source_robust_scaler.py
170
171
172
173
174
175
176 | def apply_source_robust_scaler(features: Sequence[Sequence[float]] | np.ndarray, *, stats: SourceRobustScalerStats) -> np.ndarray:
"""Apply fitted source-only robust scaler statistics."""
matrix = _feature_matrix(features, name="features")
if matrix.shape[1] != stats.location.shape[0] or matrix.shape[1] != stats.scale.shape[0]:
raise ValueError("features width must match fitted scaler statistics.")
return (matrix - stats.location) / stats.scale
|
normalize_center_mode(value)
Normalize center-mode aliases.
Source code in src/neureptrace/decoding/source_robust_scaler.py
179
180
181
182
183
184
185
186 | def normalize_center_mode(value: str | None) -> str:
"""Normalize center-mode aliases."""
normalized = "median" if value is None else str(value).strip().lower().replace("-", "_")
normalized = {"med": "median", "average": "mean", "avg": "mean", "off": "none", "false": "none", "no": "none"}.get(normalized, normalized)
if normalized not in CENTER_MODES:
raise ValueError(f"Unknown center mode {value!r}. Available modes: {', '.join(CENTER_MODES)}.")
return normalized
|
normalize_scale_mode(value)
Normalize scale-mode aliases.
Source code in src/neureptrace/decoding/source_robust_scaler.py
189
190
191
192
193
194
195
196 | def normalize_scale_mode(value: str | None) -> str:
"""Normalize scale-mode aliases."""
normalized = "iqr" if value is None else str(value).strip().lower().replace("-", "_")
normalized = {"interquartile": "iqr", "inter_quartile": "iqr", "median_absolute_deviation": "mad", "sd": "std", "off": "none", "false": "none", "no": "none"}.get(normalized, normalized)
if normalized not in SCALE_MODES:
raise ValueError(f"Unknown scale mode {value!r}. Available modes: {', '.join(SCALE_MODES)}.")
return normalized
|