Source code for modeva.data.feature_engineering.shape

import copy
from typing import Tuple, Union, List

import numpy as np
import pandas as pd

from ...utils.constants import NUMERICAL
from ...utils.results import ValidationResult


[docs] class FEGradeRatio: def __init__(self, dataset): self.dataset = dataset self.key = "data_fe_grade_ratio" self.fitted_ = False
[docs] def transform(self, data): if isinstance(data, pd.DataFrame): X = data[self.feature_names_in_].values.astype(np.float32) else: data = pd.DataFrame(data, columns=self.all_feature_names_in_) X = data[self.feature_names_in_].values.astype(np.float32) result = self.gaml_transformer_.transform(X) return pd.DataFrame(result, columns=self.feature_names_out_)
[docs] def run(self, features: Union[str, Tuple] = None, dataset: str = "main", max_triplets: int = 50, regularization: float = 1e-8): """Generates grade-ratio shape feature measuring 3-way vs 2-way interaction complexity. After whitening, computes ratio E3/E2 where E2 is summed pairwise blade energy and E3 is summed triplet blade energy. High ratio = dominant 3-body structure. Parameters ---------- features : str or tuple, default=None Features to use. If None, all numerical features are used. dataset : {"main", "train", "test"}, default="main" Dataset used to fit the transformer. max_triplets : int, default=50 Maximum number of sampled triplets for E3 estimation. regularization : float, default=1e-8 Regularization for the internal whitening step. """ from .gaml.shape import GradeRatioShape 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_ = GradeRatioShape(max_triplets=max_triplets, regularization=regularization) self.gaml_transformer_.fit(X) self.feature_names_out_ = self.gaml_transformer_.get_feature_names() 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 FEMultiGradeShape: def __init__(self, dataset): self.dataset = dataset self.key = "data_fe_multi_grade_shape" self.fitted_ = False
[docs] def transform(self, data): if isinstance(data, pd.DataFrame): X = data[self.feature_names_in_].values.astype(np.float32) else: data = pd.DataFrame(data, columns=self.all_feature_names_in_) X = data[self.feature_names_in_].values.astype(np.float32) result = self.gaml_transformer_.transform(X) return pd.DataFrame(result, columns=self.feature_names_out_)
[docs] def run(self, features: Union[str, Tuple] = None, dataset: str = "main", grades: List[int] = None, n_bins: int = 5, regularization: float = 1e-8): """Generates multi-grade shape features (context distances using grades 2-4). Computes Mahalanobis (grade 2), skewness-weighted (grade 3), and kurtosis-weighted (grade 4) distances to class or quantile-bin centroids. Requires a target variable. Parameters ---------- features : str or tuple, default=None Features to use. If None, all numerical features are used. dataset : {"main", "train", "test"}, default="main" Dataset used to fit the transformer. grades : list of int, default=None Which grades to include (2, 3, and/or 4). Defaults to [2]. n_bins : int, default=5 Number of bins for regression (uses quantiles). regularization : float, default=1e-8 Regularization for covariance inversion. """ from .gaml.shape import MultiGradeShape inputs = locals() inputs.pop('self', None) if grades is None: grades = [2] 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) # Get target for supervised fitting y_idx = self.dataset.target_feature_index y = data[:, y_idx].astype(np.float64).ravel() self.gaml_transformer_ = MultiGradeShape(grades=grades, n_bins=n_bins, regularization=regularization) self.gaml_transformer_.fit(X, y) self.feature_names_out_ = self.gaml_transformer_.get_feature_names() 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