Source code for modeva.data.feature_engineering.tree_features

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 FERFProximity: def __init__(self, dataset): self.dataset = dataset self.key = "data_fe_rf_proximity" 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", n_estimators: int = 100, max_depth: int = 6, n_landmarks: int = 50): """Generates RF leaf co-occurrence proximity features via Nystrom approximation. Trains a random forest and uses its leaf co-occurrence as a similarity kernel. The Nystrom method computes a fixed-width feature map from landmark points. 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. n_estimators : int, default=100 Number of trees in the forest. max_depth : int, default=6 Maximum tree depth. n_landmarks : int, default=50 Number of Nystrom anchor points. """ from .gaml.tree_ops import RFProximityTransformer 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) # Get target for supervised fitting y_idx = self.dataset.target_feature_index y = data[:, y_idx].astype(np.float64).ravel() self.gaml_transformer_ = RFProximityTransformer(n_estimators=n_estimators, max_depth=max_depth, n_landmarks=n_landmarks) 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
[docs] class FESpectralProximity: def __init__(self, dataset): self.dataset = dataset self.key = "data_fe_spectral_proximity" 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", n_estimators: int = 100, max_depth: int = 6, n_components: int = 10): """Generates spectral embedding features from RF proximity kernel. Builds the RF proximity matrix, computes the normalized graph Laplacian, and extracts the smoothest eigenvectors as features. Uses Nystrom extension at transform time. 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. n_estimators : int, default=100 Number of trees in the forest. max_depth : int, default=6 Maximum tree depth. n_components : int, default=10 Number of spectral components. """ from .gaml.tree_ops import SpectralProximityTransformer 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) # Get target for supervised fitting y_idx = self.dataset.target_feature_index y = data[:, y_idx].astype(np.float64).ravel() self.gaml_transformer_ = SpectralProximityTransformer(n_estimators=n_estimators, max_depth=max_depth, n_components=n_components) 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
[docs] class FEDirectRS: def __init__(self, dataset): self.dataset = dataset self.key = "data_fe_direct_rs" 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", n_estimators: int = 50, max_depth: int = 4, regularization: float = 1e-8): """Generates DirectRS stretch features from random forest leaf geometry. Builds a weighted outer-product matrix from per-leaf centroids and variances, decomposes via eigendecomposition, and applies the resulting stretch matrix. 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. n_estimators : int, default=50 Number of trees in the forest. max_depth : int, default=4 Maximum tree depth. regularization : float, default=1e-8 Regularization added to diagonal of G before eigendecomposition. """ from .gaml.tree_ops import DirectRSTransformer 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) # Get target for supervised fitting y_idx = self.dataset.target_feature_index y = data[:, y_idx].astype(np.float64).ravel() self.gaml_transformer_ = DirectRSTransformer(n_estimators=n_estimators, max_depth=max_depth, 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