custom_feature.py
1 from typing import Callable 2 from typing import Tuple 3 4 import pandas as pd 5 6 from evidently._pydantic_compat import Field 7 from evidently.legacy.base_metric import ColumnName 8 from evidently.legacy.core import ColumnType 9 from evidently.legacy.core import new_id 10 from evidently.legacy.features.generated_features import FeatureTypeFieldMixin 11 from evidently.legacy.features.generated_features import GeneratedFeature 12 from evidently.legacy.utils.data_preprocessing import DataDefinition 13 from evidently.pydantic_utils import FingerprintPart 14 15 16 class CustomFeature(FeatureTypeFieldMixin, GeneratedFeature): 17 class Config: 18 type_alias = "evidently:feature:CustomFeature" 19 20 display_name: str 21 name: str = Field(default_factory=lambda: str(new_id())) 22 func: Callable[[pd.DataFrame, DataDefinition], pd.Series] 23 feature_type: ColumnType = ColumnType.Numerical 24 25 def generate_feature(self, data: pd.DataFrame, data_definition: DataDefinition) -> pd.DataFrame: 26 result = self.func(data, data_definition) 27 return pd.DataFrame({self.name: result}) 28 29 def _as_column(self) -> "ColumnName": 30 return self._create_column(self.name) 31 32 33 class CustomSingleColumnFeature(FeatureTypeFieldMixin, GeneratedFeature): 34 class Config: 35 type_alias = "evidently:feature:CustomSingleColumnFeature" 36 37 display_name: str 38 func: Callable[[pd.Series], pd.Series] 39 name: str = Field(default_factory=lambda: str(new_id())) 40 column_name: str 41 feature_type: ColumnType = ColumnType.Numerical 42 43 def generate_feature(self, data: pd.DataFrame, data_definition: DataDefinition) -> pd.DataFrame: 44 result = self.func(data[self.column_name]) 45 return pd.DataFrame({self.name: result}, index=data.index) 46 47 def _as_column(self) -> "ColumnName": 48 return self._create_column(self.name) 49 50 def get_fingerprint_parts(self) -> Tuple[FingerprintPart, ...]: 51 return tuple( 52 (name, self.get_field_fingerprint(name)) 53 for name, field in sorted(self.__fields__.items()) 54 if (field.required or getattr(self, name) != field.get_default()) and field.name != "func" 55 ) 56 57 58 class CustomPairColumnFeature(FeatureTypeFieldMixin, GeneratedFeature): 59 class Config: 60 type_alias = "evidently:feature:CustomPairColumnFeature" 61 62 display_name: str 63 func: Callable[[pd.Series, pd.Series], pd.Series] 64 name: str = Field(default_factory=lambda: str(new_id())) 65 first_column: str 66 second_column: str 67 feature_type: ColumnType = ColumnType.Numerical 68 69 def generate_feature(self, data: pd.DataFrame, data_definition: DataDefinition) -> pd.DataFrame: 70 result = self.func(data[self.first_column], data[self.second_column]) 71 return pd.DataFrame({self.name: result}) 72 73 def _as_column(self) -> "ColumnName": 74 return self._create_column(self.name)