import copy
from typing import Tuple, Union
import numpy as np
import pandas as pd
from ...utils.constants import NUMERICAL
from ...utils.results import ValidationResult
[docs]
class FEWhitening:
def __init__(self, dataset):
self.dataset = dataset
self.key = "data_fe_whitening"
self.fitted_ = False
[docs]
def run(self,
features: Union[str, Tuple] = None,
dataset: str = "main",
regularization: float = 1e-8):
"""Applies rotor whitening (GA Mahalanobis transformation).
Aligns data with principal axes using SVD-based whitening:
X' = (X - mu) @ V @ diag(1/sigma)
Parameters
----------
features : str or tuple, default=None
Features to whiten. If None, all numerical features are used.
dataset : {"main", "train", "test"}, default="main"
Dataset used to fit the transformer.
regularization : float, default=1e-8
Regularization for variance computation.
"""
from .gaml.rotation import RotorWhitening
inputs = locals()
inputs.pop('self', None)
if features is None:
features = [fn for fn, ft in zip(self.dataset.all_feature_names,
self.dataset.all_feature_types)
if ft == NUMERICAL]
if isinstance(features, str):
features = [features]
self.all_feature_names_in_ = copy.copy(self.dataset.all_feature_names)
self.feature_names_in_ = list(features)
data = self.dataset.get_data(dataset=dataset)
feature_indices = [self.dataset.all_feature_names.index(fn) for fn in features]
X = data[:, feature_indices].astype(np.float32)
self.gaml_transformer_ = RotorWhitening(regularization=regularization)
self.gaml_transformer_.fit(X)
self.feature_names_out_ = [f"wh_{fn}" for fn in features]
self.fitted_ = True
result = ValidationResult(key=self.key,
data=self.dataset.name,
inputs=inputs,
value={"n_features_out": len(self.feature_names_out_),
"feature_names_out": self.feature_names_out_})
return result
[docs]
class FEShear:
def __init__(self, dataset):
self.dataset = dataset
self.key = "data_fe_shear"
self.fitted_ = False
[docs]
def run(self,
features: Union[str, Tuple] = None,
dataset: str = "main",
threshold: float = 0.0):
"""Applies pairwise shear (decorrelation via residualization).
For each feature pair (i, j): X_i' = X_i - beta_ij * X_j,
where beta_ij = cov(X_i, X_j) / var(X_j).
Parameters
----------
features : str or tuple, default=None
Features to decorrelate. If None, all numerical features are used.
dataset : {"main", "train", "test"}, default="main"
Dataset used to fit the transformer.
threshold : float, default=0.0
Only apply shear if |beta_ij| > threshold.
"""
from .gaml.shear import PairwiseShear
inputs = locals()
inputs.pop('self', None)
if features is None:
features = [fn for fn, ft in zip(self.dataset.all_feature_names,
self.dataset.all_feature_types)
if ft == NUMERICAL]
if isinstance(features, str):
features = [features]
self.all_feature_names_in_ = copy.copy(self.dataset.all_feature_names)
self.feature_names_in_ = list(features)
data = self.dataset.get_data(dataset=dataset)
feature_indices = [self.dataset.all_feature_names.index(fn) for fn in features]
X = data[:, feature_indices].astype(np.float32)
self.gaml_transformer_ = PairwiseShear(threshold=threshold)
self.gaml_transformer_.fit(X)
self.feature_names_out_ = [f"sh_{fn}" for fn in features]
self.fitted_ = True
result = ValidationResult(key=self.key,
data=self.dataset.name,
inputs=inputs,
value={"n_features_out": len(self.feature_names_out_),
"feature_names_out": self.feature_names_out_})
return result