Skip to content

Constructed

physXAI.preprocessing.constructed

Attributes

CONSTRUCTED_CLASS_REGISTRY: dict[str, Type[FeatureBase]] = dict() module-attribute

Classes

FeatureBase

Bases: ABC

Abstract Base Class for all feature engineering components. Each feature object represents a column (or a transformation that results in a column) in a Pandas DataFrame. It supports arithmetic operations to combine features.

Source code in physXAI/preprocessing/constructed.py
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
class FeatureBase(ABC):
    """
    Abstract Base Class for all feature engineering components.
    Each feature object represents a column (or a transformation that results in a column)
    in a Pandas DataFrame. It supports arithmetic operations to combine features.
    """

    def __init__(self, name: str, **kwargs):
        """
        Initializes a FeatureBase instance.

        Args:
            name (str): The name of the feature. This will be the column name in the DataFrame.
            **kwargs: Catches any additional keyword arguments.
        """

        self.feature: str = name

        # Automatically registers the newly created feature instance with the FeatureConstruction manager
        FeatureConstruction.append(self)

    def rename(self, name: str):
        """
        Renames the feature.

        Args:
            name (str): The new name for the feature.
        """

        self.feature = name

    def process(self, df: DataFrame) -> Series:
        """
         Processes the DataFrame to return the Series corresponding to this feature.
         For a base feature that already exists in the DataFrame, it simply returns the column.
         For derived features, this method would compute the feature if it doesn't exist.

         Args:
             df (DataFrame): The input DataFrame.

         Returns:
             Series: The Pandas Series representing this feature.
         """

        return df[self.feature]

    # --- Operator Overloading for Feature Arithmetic ---
    # These methods allow FeatureBase objects to be combined using standard arithmetic operators,
    # creating new composite feature objects (e.g., FeatureAdd, FeatureSub).
    def __add__(self, other):
        return FeatureAdd(self, other)

    def __radd__(self, other):
        return FeatureAdd(other, self)

    def __sub__(self, other):
        return FeatureSub(self, other)

    def __rsub__(self, other):
        return FeatureSub(other, self)

    def __mul__(self, other):
        return FeatureMul(self, other)

    def __rmul__(self, other):
        return FeatureMul(other, self)

    def __truediv__(self, other):
        return FeatureTrueDiv(self, other)

    def __rtruediv__(self, other):
        return FeatureTrueDiv(other, self)

    def __pow__(self, other):
        return FeaturePow(self, other)

    def exp(self):
        """Creates a new feature representing e^(self)."""
        return FeatureExp(self)

    def sin(self):
        """Creates a new feature representing sin(self)."""
        return FeatureSin(self)

    def cos(self):
        """Creates a new feature representing cos(self)."""
        return FeatureCos(self)

    def lag(self, lag: int, previous: bool = True):
        """
           Creates a lagged version of this feature.

           Args:
               lag (int): The number of time steps to lag by.
               previous (bool): If True and lag_value > 1, returns a list of FeatureLag objects
                                for all lags from 1 up to lag_value. Otherwise, returns a single
                                FeatureLag object for the specified lag_value.

           Returns:
               FeatureLag or List[FeatureLag]: A single lagged feature or a list of lagged features.
        """

        if previous and lag > 1:
            lg = list()
            for i in range(1, lag + 1):
                lg.append(FeatureLag(self, i))
            return lg
        else:
            return FeatureLag(self, lag)

    def get_config(self) -> dict:
        return {'class_name': self.__class__.__name__,
                'name': self.feature}

    @classmethod
    def from_config(cls, config: dict) -> 'FeatureBase':
        return cls(**config)
Attributes
feature: str = name instance-attribute
Functions
__init__(name: str, **kwargs)

Initializes a FeatureBase instance.

Parameters:

Name Type Description Default
name str

The name of the feature. This will be the column name in the DataFrame.

required
**kwargs

Catches any additional keyword arguments.

{}
Source code in physXAI/preprocessing/constructed.py
14
15
16
17
18
19
20
21
22
23
24
25
26
def __init__(self, name: str, **kwargs):
    """
    Initializes a FeatureBase instance.

    Args:
        name (str): The name of the feature. This will be the column name in the DataFrame.
        **kwargs: Catches any additional keyword arguments.
    """

    self.feature: str = name

    # Automatically registers the newly created feature instance with the FeatureConstruction manager
    FeatureConstruction.append(self)
rename(name: str)

Renames the feature.

Parameters:

Name Type Description Default
name str

The new name for the feature.

required
Source code in physXAI/preprocessing/constructed.py
28
29
30
31
32
33
34
35
36
def rename(self, name: str):
    """
    Renames the feature.

    Args:
        name (str): The new name for the feature.
    """

    self.feature = name
process(df: DataFrame) -> Series

Processes the DataFrame to return the Series corresponding to this feature. For a base feature that already exists in the DataFrame, it simply returns the column. For derived features, this method would compute the feature if it doesn't exist.

Parameters:

Name Type Description Default
df DataFrame

The input DataFrame.

required

Returns:

Name Type Description
Series Series

The Pandas Series representing this feature.

Source code in physXAI/preprocessing/constructed.py
38
39
40
41
42
43
44
45
46
47
48
49
50
51
def process(self, df: DataFrame) -> Series:
    """
     Processes the DataFrame to return the Series corresponding to this feature.
     For a base feature that already exists in the DataFrame, it simply returns the column.
     For derived features, this method would compute the feature if it doesn't exist.

     Args:
         df (DataFrame): The input DataFrame.

     Returns:
         Series: The Pandas Series representing this feature.
     """

    return df[self.feature]
exp()

Creates a new feature representing e^(self).

Source code in physXAI/preprocessing/constructed.py
83
84
85
def exp(self):
    """Creates a new feature representing e^(self)."""
    return FeatureExp(self)
sin()

Creates a new feature representing sin(self).

Source code in physXAI/preprocessing/constructed.py
87
88
89
def sin(self):
    """Creates a new feature representing sin(self)."""
    return FeatureSin(self)
cos()

Creates a new feature representing cos(self).

Source code in physXAI/preprocessing/constructed.py
91
92
93
def cos(self):
    """Creates a new feature representing cos(self)."""
    return FeatureCos(self)
lag(lag: int, previous: bool = True)

Creates a lagged version of this feature.

Parameters:

Name Type Description Default
lag int

The number of time steps to lag by.

required
previous bool

If True and lag_value > 1, returns a list of FeatureLag objects for all lags from 1 up to lag_value. Otherwise, returns a single FeatureLag object for the specified lag_value.

True

Returns:

Type Description

FeatureLag or List[FeatureLag]: A single lagged feature or a list of lagged features.

Source code in physXAI/preprocessing/constructed.py
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
def lag(self, lag: int, previous: bool = True):
    """
       Creates a lagged version of this feature.

       Args:
           lag (int): The number of time steps to lag by.
           previous (bool): If True and lag_value > 1, returns a list of FeatureLag objects
                            for all lags from 1 up to lag_value. Otherwise, returns a single
                            FeatureLag object for the specified lag_value.

       Returns:
           FeatureLag or List[FeatureLag]: A single lagged feature or a list of lagged features.
    """

    if previous and lag > 1:
        lg = list()
        for i in range(1, lag + 1):
            lg.append(FeatureLag(self, i))
        return lg
    else:
        return FeatureLag(self, lag)
get_config() -> dict
Source code in physXAI/preprocessing/constructed.py
117
118
119
def get_config(self) -> dict:
    return {'class_name': self.__class__.__name__,
            'name': self.feature}
from_config(config: dict) -> FeatureBase classmethod
Source code in physXAI/preprocessing/constructed.py
121
122
123
@classmethod
def from_config(cls, config: dict) -> 'FeatureBase':
    return cls(**config)

Feature

Bases: FeatureBase

Represents a basic feature that is assumed to exist directly in the input DataFrame. Its process method simply retrieves the column by its name.

Source code in physXAI/preprocessing/constructed.py
163
164
165
166
167
168
169
@register_feature
class Feature(FeatureBase):
    """
    Represents a basic feature that is assumed to exist directly in the input DataFrame.
    Its `process` method simply retrieves the column by its name.
    """
    pass

FeatureLag

Bases: FeatureBase

Represents a lagged version of another feature. Calculates df[original_feature_name].shift(lag_steps).

Source code in physXAI/preprocessing/constructed.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
@register_feature
class FeatureLag(FeatureBase):
    """
    Represents a lagged version of another feature.
    Calculates `df[original_feature_name].shift(lag_steps)`.
    """

    def __init__(self, f: FeatureBase or str, lag: int, name: str = None, **kwargs):
        """
        Initializes a FeatureLag instance.

        Args:
            f (FeatureBase or str): The original feature object or its name.
            lag (int): The number of time steps to lag by.
            name (str, optional): The name for this lagged feature. If None, it's auto-generated
                                  as "{original_name}_lag{X}".
            **kwargs: Catches any additional keyword arguments.
        """
        if isinstance(f, FeatureBase):
            self.origf: str = f.feature
        else:
            self.origf: str = f
        if name is None:
            name = f.feature + f'_lag{lag}'
        super().__init__(name)
        self.lag: int = lag

    def process(self, df: DataFrame) -> Series:
        if self.feature not in df.columns:
            df[self.feature] = df[self.origf].shift(self.lag)
        return super().process(df)

    def get_config(self) -> dict:
        config = super().get_config()
        config.update({'lag': self.lag, 'f': self.origf})
        return config
Attributes
origf: str = f.feature instance-attribute
lag: int = lag instance-attribute
Functions
__init__(f: FeatureBase or str, lag: int, name: str = None, **kwargs)

Initializes a FeatureLag instance.

Parameters:

Name Type Description Default
f FeatureBase or str

The original feature object or its name.

required
lag int

The number of time steps to lag by.

required
name str

The name for this lagged feature. If None, it's auto-generated as "{original_name}_lag{X}".

None
**kwargs

Catches any additional keyword arguments.

{}
Source code in physXAI/preprocessing/constructed.py
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
def __init__(self, f: FeatureBase or str, lag: int, name: str = None, **kwargs):
    """
    Initializes a FeatureLag instance.

    Args:
        f (FeatureBase or str): The original feature object or its name.
        lag (int): The number of time steps to lag by.
        name (str, optional): The name for this lagged feature. If None, it's auto-generated
                              as "{original_name}_lag{X}".
        **kwargs: Catches any additional keyword arguments.
    """
    if isinstance(f, FeatureBase):
        self.origf: str = f.feature
    else:
        self.origf: str = f
    if name is None:
        name = f.feature + f'_lag{lag}'
    super().__init__(name)
    self.lag: int = lag
process(df: DataFrame) -> Series
Source code in physXAI/preprocessing/constructed.py
199
200
201
202
def process(self, df: DataFrame) -> Series:
    if self.feature not in df.columns:
        df[self.feature] = df[self.origf].shift(self.lag)
    return super().process(df)
get_config() -> dict
Source code in physXAI/preprocessing/constructed.py
204
205
206
207
def get_config(self) -> dict:
    config = super().get_config()
    config.update({'lag': self.lag, 'f': self.origf})
    return config

FeatureTwo

Bases: FeatureBase, ABC

Abstract Base Class for features derived from two other features (or constants). Examples: FeatureAdd (f1 + f2), FeatureSub (f1 - f2).

Source code in physXAI/preprocessing/constructed.py
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
class FeatureTwo(FeatureBase, ABC):
    """
    Abstract Base Class for features derived from two other features (or constants).
    Examples: FeatureAdd (f1 + f2), FeatureSub (f1 - f2).
    """

    def __init__(self, feature1: FeatureBase or int or float, feature2: FeatureBase or int or float, name: str = None,
                 **kwargs):
        """
        Initializes a FeatureTwo instance.

        Args:
            feature1 (FeatureBase or int or float): The first operand.
            feature2 (FeatureBase or int or float): The second operand.
            name (str, optional): Name for the derived feature. If None, it's auto-generated
                                  by the `name` abstract method of the subclass.
            **kwargs: Catches any additional keyword arguments.
        """

        if isinstance(feature1, FeatureBase):
            f1n = feature1.feature
        else:
            f1n = str(feature1)
        if isinstance(feature2, FeatureBase):
            f2n = feature2.feature
        else:
            f2n = str(feature2)
        if name is None:
            name = self.name(f1n, f2n)
        super().__init__(name)
        self.feature1 = feature1
        self.feature2 = feature2

    def process(self, df: DataFrame) -> Series:
        """
        Calculates and returns the derived feature Series.
        If the column doesn't exist, it processes the operand features (if they are FeatureBase objects)
        or uses the constant values, then applies the `calc` method.

        Args:
            df (DataFrame): The input DataFrame.

        Returns:
            Series: The derived feature Series.
        """

        if self.feature not in df.columns:
            if isinstance(self.feature1, FeatureBase):
                f1 = self.feature1.process(df)
            else:
                f1 = self.feature1
            if isinstance(self.feature2, FeatureBase):
                f2 = self.feature2.process(df)
            else:
                f2 = self.feature2
            df[self.feature] = self.calc(f1, f2)
        return super().process(df)

    @abstractmethod
    def calc(self, f1, f2):
        """
        Abstract method to perform the actual calculation between the two processed operands.
        To be implemented by subclasses (e.g., addition, subtraction).

        Args:
            f1: The processed first operand (either a Series or a scalar).
            f2: The processed second operand (either a Series or a scalar).

        Returns:
            Series: The result of the calculation.
        """
        pass

    @abstractmethod
    def name(self, f1: str, f2: str) -> str:
        """
        Abstract method to generate a descriptive name for the derived feature,
        based on the names of its operands.

        Args:
            f1 (str): Name of the first operand.
            f2 (str): Name of the second operand.

        Returns:
            str: The auto-generated name for this feature.
        """
        pass

    def get_config(self) -> dict:
        """
        Returns the configuration for FeatureTwo.
        Includes configurations of its operand features if they are FeatureBase objects,
        or the constant values otherwise.
        """

        config = super().get_config()
        if isinstance(self.feature1, FeatureBase):
            f1n = self.feature1.feature
        else:
            f1n = self.feature1
        if isinstance(self.feature2, FeatureBase):
            f2n = self.feature2.feature
        else:
            f2n = self.feature2
        config.update({'feature1': f1n, 'feature2': f2n})
        return config

    @classmethod
    def from_config(cls, config: dict) -> 'FeatureTwo':
        """
        Creates a FeatureTwo instance (or its subclass) from a configuration dictionary.
        Handles reconstruction of operand features if they were FeatureBase objects.

        Args:
            config (dict): Configuration dictionary. Must contain 'feature1' and 'feature2'.

        Returns:
            FeatureTwo: An instance of the specific FeatureTwo subclass.
        """

        # Reconstruct feature 1
        if isinstance(config['feature1'], dict):
            item_conf = config['feature1']
            # Check if feature already exists
            f1n = FeatureConstruction.get_feature(item_conf['name'])
            if f1n is None:
                f1n = feature_from_config(item_conf)
        elif isinstance(config['feature1'], str):
            f1n = FeatureConstruction.get_feature(config['feature1'])
        else:
            f1n = config['feature1']
        config['feature1'] = f1n

        # Reconstruct feature 2
        if isinstance(config['feature2'], dict):
            item_conf = config['feature2']
            # Check if feature already exists
            f2n = FeatureConstruction.get_feature(item_conf['name'])
            if f2n is None:
                f2n = feature_from_config(item_conf)
        elif isinstance(config['feature2'], str):
            f2n = FeatureConstruction.get_feature(config['feature2'])
        else:
            f2n = config['feature2']
        config['feature2'] = f2n

        return cls(**config)
Attributes
feature1 = feature1 instance-attribute
feature2 = feature2 instance-attribute
Functions
__init__(feature1: FeatureBase or int or float, feature2: FeatureBase or int or float, name: str = None, **kwargs)

Initializes a FeatureTwo instance.

Parameters:

Name Type Description Default
feature1 FeatureBase or int or float

The first operand.

required
feature2 FeatureBase or int or float

The second operand.

required
name str

Name for the derived feature. If None, it's auto-generated by the name abstract method of the subclass.

None
**kwargs

Catches any additional keyword arguments.

{}
Source code in physXAI/preprocessing/constructed.py
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
def __init__(self, feature1: FeatureBase or int or float, feature2: FeatureBase or int or float, name: str = None,
             **kwargs):
    """
    Initializes a FeatureTwo instance.

    Args:
        feature1 (FeatureBase or int or float): The first operand.
        feature2 (FeatureBase or int or float): The second operand.
        name (str, optional): Name for the derived feature. If None, it's auto-generated
                              by the `name` abstract method of the subclass.
        **kwargs: Catches any additional keyword arguments.
    """

    if isinstance(feature1, FeatureBase):
        f1n = feature1.feature
    else:
        f1n = str(feature1)
    if isinstance(feature2, FeatureBase):
        f2n = feature2.feature
    else:
        f2n = str(feature2)
    if name is None:
        name = self.name(f1n, f2n)
    super().__init__(name)
    self.feature1 = feature1
    self.feature2 = feature2
process(df: DataFrame) -> Series

Calculates and returns the derived feature Series. If the column doesn't exist, it processes the operand features (if they are FeatureBase objects) or uses the constant values, then applies the calc method.

Parameters:

Name Type Description Default
df DataFrame

The input DataFrame.

required

Returns:

Name Type Description
Series Series

The derived feature Series.

Source code in physXAI/preprocessing/constructed.py
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
def process(self, df: DataFrame) -> Series:
    """
    Calculates and returns the derived feature Series.
    If the column doesn't exist, it processes the operand features (if they are FeatureBase objects)
    or uses the constant values, then applies the `calc` method.

    Args:
        df (DataFrame): The input DataFrame.

    Returns:
        Series: The derived feature Series.
    """

    if self.feature not in df.columns:
        if isinstance(self.feature1, FeatureBase):
            f1 = self.feature1.process(df)
        else:
            f1 = self.feature1
        if isinstance(self.feature2, FeatureBase):
            f2 = self.feature2.process(df)
        else:
            f2 = self.feature2
        df[self.feature] = self.calc(f1, f2)
    return super().process(df)
calc(f1, f2) abstractmethod

Abstract method to perform the actual calculation between the two processed operands. To be implemented by subclasses (e.g., addition, subtraction).

Parameters:

Name Type Description Default
f1

The processed first operand (either a Series or a scalar).

required
f2

The processed second operand (either a Series or a scalar).

required

Returns:

Name Type Description
Series

The result of the calculation.

Source code in physXAI/preprocessing/constructed.py
268
269
270
271
272
273
274
275
276
277
278
279
280
281
@abstractmethod
def calc(self, f1, f2):
    """
    Abstract method to perform the actual calculation between the two processed operands.
    To be implemented by subclasses (e.g., addition, subtraction).

    Args:
        f1: The processed first operand (either a Series or a scalar).
        f2: The processed second operand (either a Series or a scalar).

    Returns:
        Series: The result of the calculation.
    """
    pass
name(f1: str, f2: str) -> str abstractmethod

Abstract method to generate a descriptive name for the derived feature, based on the names of its operands.

Parameters:

Name Type Description Default
f1 str

Name of the first operand.

required
f2 str

Name of the second operand.

required

Returns:

Name Type Description
str str

The auto-generated name for this feature.

Source code in physXAI/preprocessing/constructed.py
283
284
285
286
287
288
289
290
291
292
293
294
295
296
@abstractmethod
def name(self, f1: str, f2: str) -> str:
    """
    Abstract method to generate a descriptive name for the derived feature,
    based on the names of its operands.

    Args:
        f1 (str): Name of the first operand.
        f2 (str): Name of the second operand.

    Returns:
        str: The auto-generated name for this feature.
    """
    pass
get_config() -> dict

Returns the configuration for FeatureTwo. Includes configurations of its operand features if they are FeatureBase objects, or the constant values otherwise.

Source code in physXAI/preprocessing/constructed.py
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
def get_config(self) -> dict:
    """
    Returns the configuration for FeatureTwo.
    Includes configurations of its operand features if they are FeatureBase objects,
    or the constant values otherwise.
    """

    config = super().get_config()
    if isinstance(self.feature1, FeatureBase):
        f1n = self.feature1.feature
    else:
        f1n = self.feature1
    if isinstance(self.feature2, FeatureBase):
        f2n = self.feature2.feature
    else:
        f2n = self.feature2
    config.update({'feature1': f1n, 'feature2': f2n})
    return config
from_config(config: dict) -> FeatureTwo classmethod

Creates a FeatureTwo instance (or its subclass) from a configuration dictionary. Handles reconstruction of operand features if they were FeatureBase objects.

Parameters:

Name Type Description Default
config dict

Configuration dictionary. Must contain 'feature1' and 'feature2'.

required

Returns:

Name Type Description
FeatureTwo FeatureTwo

An instance of the specific FeatureTwo subclass.

Source code in physXAI/preprocessing/constructed.py
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
@classmethod
def from_config(cls, config: dict) -> 'FeatureTwo':
    """
    Creates a FeatureTwo instance (or its subclass) from a configuration dictionary.
    Handles reconstruction of operand features if they were FeatureBase objects.

    Args:
        config (dict): Configuration dictionary. Must contain 'feature1' and 'feature2'.

    Returns:
        FeatureTwo: An instance of the specific FeatureTwo subclass.
    """

    # Reconstruct feature 1
    if isinstance(config['feature1'], dict):
        item_conf = config['feature1']
        # Check if feature already exists
        f1n = FeatureConstruction.get_feature(item_conf['name'])
        if f1n is None:
            f1n = feature_from_config(item_conf)
    elif isinstance(config['feature1'], str):
        f1n = FeatureConstruction.get_feature(config['feature1'])
    else:
        f1n = config['feature1']
    config['feature1'] = f1n

    # Reconstruct feature 2
    if isinstance(config['feature2'], dict):
        item_conf = config['feature2']
        # Check if feature already exists
        f2n = FeatureConstruction.get_feature(item_conf['name'])
        if f2n is None:
            f2n = feature_from_config(item_conf)
    elif isinstance(config['feature2'], str):
        f2n = FeatureConstruction.get_feature(config['feature2'])
    else:
        f2n = config['feature2']
    config['feature2'] = f2n

    return cls(**config)

FeatureAdd

Bases: FeatureTwo

Source code in physXAI/preprocessing/constructed.py
359
360
361
362
363
364
365
366
@register_feature
class FeatureAdd(FeatureTwo):

    def calc(self, f1, f2):
        return f1 + f2

    def name(self, f1: str, f2: str) -> str:
        return '(' + f1 + '+' + f2 + ')'
Functions
calc(f1, f2)
Source code in physXAI/preprocessing/constructed.py
362
363
def calc(self, f1, f2):
    return f1 + f2
name(f1: str, f2: str) -> str
Source code in physXAI/preprocessing/constructed.py
365
366
def name(self, f1: str, f2: str) -> str:
    return '(' + f1 + '+' + f2 + ')'

FeatureSub

Bases: FeatureTwo

Source code in physXAI/preprocessing/constructed.py
369
370
371
372
373
374
375
376
@register_feature
class FeatureSub(FeatureTwo):

    def calc(self, f1, f2):
        return f1 - f2

    def name(self, f1: str, f2: str) -> str:
        return '(' + f1 + '-' + f2 + ')'
Functions
calc(f1, f2)
Source code in physXAI/preprocessing/constructed.py
372
373
def calc(self, f1, f2):
    return f1 - f2
name(f1: str, f2: str) -> str
Source code in physXAI/preprocessing/constructed.py
375
376
def name(self, f1: str, f2: str) -> str:
    return '(' + f1 + '-' + f2 + ')'

FeatureMul

Bases: FeatureTwo

Source code in physXAI/preprocessing/constructed.py
379
380
381
382
383
384
385
386
@register_feature
class FeatureMul(FeatureTwo):

    def calc(self, f1, f2):
        return f1 * f2

    def name(self, f1: str, f2: str) -> str:
        return '(' + f1 + '*' + f2 + ')'
Functions
calc(f1, f2)
Source code in physXAI/preprocessing/constructed.py
382
383
def calc(self, f1, f2):
    return f1 * f2
name(f1: str, f2: str) -> str
Source code in physXAI/preprocessing/constructed.py
385
386
def name(self, f1: str, f2: str) -> str:
    return '(' + f1 + '*' + f2 + ')'

FeatureTrueDiv

Bases: FeatureTwo

Source code in physXAI/preprocessing/constructed.py
389
390
391
392
393
394
395
396
@register_feature
class FeatureTrueDiv(FeatureTwo):

    def calc(self, f1, f2):
        return f1 / f2

    def name(self, f1: str, f2: str) -> str:
        return '(' + f1 + '/' + f2 + ')'
Functions
calc(f1, f2)
Source code in physXAI/preprocessing/constructed.py
392
393
def calc(self, f1, f2):
    return f1 / f2
name(f1: str, f2: str) -> str
Source code in physXAI/preprocessing/constructed.py
395
396
def name(self, f1: str, f2: str) -> str:
    return '(' + f1 + '/' + f2 + ')'

FeaturePow

Bases: FeatureTwo

Source code in physXAI/preprocessing/constructed.py
399
400
401
402
403
404
405
406
@register_feature
class FeaturePow(FeatureTwo):

    def calc(self, f1, f2):
        return f1 ** f2

    def name(self, f1: str, f2: str) -> str:
        return '(' + f1 + '**' + f2 + ')'
Functions
calc(f1, f2)
Source code in physXAI/preprocessing/constructed.py
402
403
def calc(self, f1, f2):
    return f1 ** f2
name(f1: str, f2: str) -> str
Source code in physXAI/preprocessing/constructed.py
405
406
def name(self, f1: str, f2: str) -> str:
    return '(' + f1 + '**' + f2 + ')'

FeatureExp

Bases: FeatureBase

Feature representing e^(feature).

Source code in physXAI/preprocessing/constructed.py
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
@register_feature
class FeatureExp(FeatureBase):
    """Feature representing e^(feature)."""

    def __init__(self, f1: FeatureBase, name: str = None, **kwargs):
        self.f1: FeatureBase = f1
        if name is None:
            name = 'exp(' + f1.feature + ')'
        super().__init__(name)

    def process(self, df: DataFrame) -> Series:
        if self.feature not in df.columns:
            df[self.feature] = np.exp(self.f1.process(df))
        return super().process(df)

    def get_config(self) -> dict:
        config = super().get_config()
        config.update({'f1': self.f1.feature})
        return config

    @classmethod
    def from_config(cls, config: dict) -> 'FeatureExp':
        item_conf = config['f1']
        f1n = FeatureConstruction.get_feature(item_conf)
        if f1n is None:
            f1n = feature_from_config(item_conf)
        config['f1'] = f1n
        return cls(**config)
Attributes
f1: FeatureBase = f1 instance-attribute
Functions
__init__(f1: FeatureBase, name: str = None, **kwargs)
Source code in physXAI/preprocessing/constructed.py
413
414
415
416
417
def __init__(self, f1: FeatureBase, name: str = None, **kwargs):
    self.f1: FeatureBase = f1
    if name is None:
        name = 'exp(' + f1.feature + ')'
    super().__init__(name)
process(df: DataFrame) -> Series
Source code in physXAI/preprocessing/constructed.py
419
420
421
422
def process(self, df: DataFrame) -> Series:
    if self.feature not in df.columns:
        df[self.feature] = np.exp(self.f1.process(df))
    return super().process(df)
get_config() -> dict
Source code in physXAI/preprocessing/constructed.py
424
425
426
427
def get_config(self) -> dict:
    config = super().get_config()
    config.update({'f1': self.f1.feature})
    return config
from_config(config: dict) -> FeatureExp classmethod
Source code in physXAI/preprocessing/constructed.py
429
430
431
432
433
434
435
436
@classmethod
def from_config(cls, config: dict) -> 'FeatureExp':
    item_conf = config['f1']
    f1n = FeatureConstruction.get_feature(item_conf)
    if f1n is None:
        f1n = feature_from_config(item_conf)
    config['f1'] = f1n
    return cls(**config)

FeatureSin

Bases: FeatureBase

Feature representing sin(feature).

Source code in physXAI/preprocessing/constructed.py
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
@register_feature
class FeatureSin(FeatureBase):
    """Feature representing sin(feature)."""

    def __init__(self, f1: FeatureBase, name: str = None, **kwargs):
        self.f1: FeatureBase = f1
        if name is None:
            name = 'sin(' + f1.feature + ')'
        super().__init__(name)

    def process(self, df: DataFrame) -> Series:
        if self.feature not in df.columns:
            df[self.feature] = np.sin(self.f1.process(df))
        return super().process(df)

    def get_config(self) -> dict:
        config = super().get_config()
        config.update({'f1': self.f1.feature})
        return config

    @classmethod
    def from_config(cls, config: dict) -> 'FeatureSin':
        item_conf = config['f1']
        f1n = FeatureConstruction.get_feature(item_conf)
        if f1n is None:
            f1n = feature_from_config(item_conf)
        config['f1'] = f1n
        return cls(**config)
Attributes
f1: FeatureBase = f1 instance-attribute
Functions
__init__(f1: FeatureBase, name: str = None, **kwargs)
Source code in physXAI/preprocessing/constructed.py
443
444
445
446
447
def __init__(self, f1: FeatureBase, name: str = None, **kwargs):
    self.f1: FeatureBase = f1
    if name is None:
        name = 'sin(' + f1.feature + ')'
    super().__init__(name)
process(df: DataFrame) -> Series
Source code in physXAI/preprocessing/constructed.py
449
450
451
452
def process(self, df: DataFrame) -> Series:
    if self.feature not in df.columns:
        df[self.feature] = np.sin(self.f1.process(df))
    return super().process(df)
get_config() -> dict
Source code in physXAI/preprocessing/constructed.py
454
455
456
457
def get_config(self) -> dict:
    config = super().get_config()
    config.update({'f1': self.f1.feature})
    return config
from_config(config: dict) -> FeatureSin classmethod
Source code in physXAI/preprocessing/constructed.py
459
460
461
462
463
464
465
466
@classmethod
def from_config(cls, config: dict) -> 'FeatureSin':
    item_conf = config['f1']
    f1n = FeatureConstruction.get_feature(item_conf)
    if f1n is None:
        f1n = feature_from_config(item_conf)
    config['f1'] = f1n
    return cls(**config)

FeatureCos

Bases: FeatureBase

Feature representing cos(feature).

Source code in physXAI/preprocessing/constructed.py
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
@register_feature
class FeatureCos(FeatureBase):
    """Feature representing cos(feature)."""

    def __init__(self, f1: FeatureBase, name: str = None, **kwargs):
        self.f1: FeatureBase = f1
        if name is None:
            name = 'cos(' + f1.feature + ')'
        super().__init__(name)

    def process(self, df: DataFrame) -> Series:
        if self.feature not in df.columns:
            df[self.feature] = np.cos(self.f1.process(df))
        return super().process(df)

    def get_config(self) -> dict:
        config = super().get_config()
        config.update({'f1': self.f1.feature})
        return config

    @classmethod
    def from_config(cls, config: dict) -> 'FeatureCos':
        item_conf = config['f1']
        f1n = FeatureConstruction.get_feature(item_conf)
        if f1n is None:
            f1n = feature_from_config(item_conf)
        config['f1'] = f1n
        return cls(**config)
Attributes
f1: FeatureBase = f1 instance-attribute
Functions
__init__(f1: FeatureBase, name: str = None, **kwargs)
Source code in physXAI/preprocessing/constructed.py
473
474
475
476
477
def __init__(self, f1: FeatureBase, name: str = None, **kwargs):
    self.f1: FeatureBase = f1
    if name is None:
        name = 'cos(' + f1.feature + ')'
    super().__init__(name)
process(df: DataFrame) -> Series
Source code in physXAI/preprocessing/constructed.py
479
480
481
482
def process(self, df: DataFrame) -> Series:
    if self.feature not in df.columns:
        df[self.feature] = np.cos(self.f1.process(df))
    return super().process(df)
get_config() -> dict
Source code in physXAI/preprocessing/constructed.py
484
485
486
487
def get_config(self) -> dict:
    config = super().get_config()
    config.update({'f1': self.f1.feature})
    return config
from_config(config: dict) -> FeatureCos classmethod
Source code in physXAI/preprocessing/constructed.py
489
490
491
492
493
494
495
496
@classmethod
def from_config(cls, config: dict) -> 'FeatureCos':
    item_conf = config['f1']
    f1n = FeatureConstruction.get_feature(item_conf)
    if f1n is None:
        f1n = feature_from_config(item_conf)
    config['f1'] = f1n
    return cls(**config)

FeatureConstant

Bases: FeatureBase

Represents a feature that is a constant value across all rows.

Source code in physXAI/preprocessing/constructed.py
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
@register_feature
class FeatureConstant(FeatureBase):
    """
    Represents a feature that is a constant value across all rows.
    """

    def __init__(self, c: float, name: str, **kwargs):
        self.c = c
        super().__init__(name)

    def process(self, df: DataFrame) -> Series:
        if self.feature not in df.columns:
            df[self.feature] = self.c
        return super().process(df)

    def get_config(self) -> dict:
        config = super().get_config()
        config.update({'c': self.c})
        return config
Attributes
c = c instance-attribute
Functions
__init__(c: float, name: str, **kwargs)
Source code in physXAI/preprocessing/constructed.py
505
506
507
def __init__(self, c: float, name: str, **kwargs):
    self.c = c
    super().__init__(name)
process(df: DataFrame) -> Series
Source code in physXAI/preprocessing/constructed.py
509
510
511
512
def process(self, df: DataFrame) -> Series:
    if self.feature not in df.columns:
        df[self.feature] = self.c
    return super().process(df)
get_config() -> dict
Source code in physXAI/preprocessing/constructed.py
514
515
516
517
def get_config(self) -> dict:
    config = super().get_config()
    config.update({'c': self.c})
    return config

FeatureConstruction

Manages a collection of feature engineering objects (subclasses of FeatureBase). Provides methods to process a DataFrame to generate all registered features, and to save/load the feature engineering pipeline configuration.

Source code in physXAI/preprocessing/constructed.py
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
class FeatureConstruction:
    """
    Manages a collection of feature engineering objects (subclasses of FeatureBase).
    Provides methods to process a DataFrame to generate all registered features,
    and to save/load the feature engineering pipeline configuration.
    """

    features = list[FeatureBase]()

    @staticmethod
    def append(f: FeatureBase):
        """
        Adds a feature object to the list of managed features.
        Called automatically from FeatureBase.__init__.

        Args:
            f (FeatureBase): The feature object to add.
        """
        if FeatureConstruction.get_feature(f.feature) is None:
            FeatureConstruction.features.append(f)

    @staticmethod
    def get_feature(name: str) -> FeatureBase or None:
        """
        Retrieves a feature object by its name from the managed list.

        Args:
            name (str): The name of the feature to retrieve.

        Returns:
            FeatureBase or None: The found feature object, or None if not found.
        """
        for f in FeatureConstruction.features:
            if f.feature == name:
                return f
        return None

    @staticmethod
    def process(df: DataFrame):
        """
        Processes the input DataFrame by applying all registered feature transformations in order.
        Each feature's `process` method is called, which typically adds a new column to `df`
        if it doesn't already exist.

        Args:
            df (DataFrame): The DataFrame to process and add features to.
        """

        for f in FeatureConstruction.features:
            f.process(df)

    @staticmethod
    def get_config() -> list:
        """
        Returns a list of configuration dictionaries for all managed features.
        This list can be serialized (e.g., to JSON) to save the feature pipeline.
        """

        item_configs = [item.get_config() for item in FeatureConstruction.features]
        return item_configs

    @staticmethod
    def from_config(config: list):
        """
        Reconstructs the feature engineering pipeline from a list of configuration dictionaries.
        Clears any existing features and populates `FeatureConstruction.features` with
        newly created feature objects based on the provided configurations.

        Args:
            config (List[dict]): A list where each dictionary is the configuration
                                      for a single feature object.
        """

        FeatureConstruction.features = list[FeatureBase]()
        for item_conf in config:
            f = FeatureConstruction.get_feature(item_conf['name'])
            if f is None:
                feature_from_config(item_conf)
Attributes
features = list[FeatureBase]() class-attribute instance-attribute
Functions
append(f: FeatureBase) staticmethod

Adds a feature object to the list of managed features. Called automatically from FeatureBase.init.

Parameters:

Name Type Description Default
f FeatureBase

The feature object to add.

required
Source code in physXAI/preprocessing/constructed.py
529
530
531
532
533
534
535
536
537
538
539
@staticmethod
def append(f: FeatureBase):
    """
    Adds a feature object to the list of managed features.
    Called automatically from FeatureBase.__init__.

    Args:
        f (FeatureBase): The feature object to add.
    """
    if FeatureConstruction.get_feature(f.feature) is None:
        FeatureConstruction.features.append(f)
get_feature(name: str) -> FeatureBase or None staticmethod

Retrieves a feature object by its name from the managed list.

Parameters:

Name Type Description Default
name str

The name of the feature to retrieve.

required

Returns:

Type Description
FeatureBase or None

FeatureBase or None: The found feature object, or None if not found.

Source code in physXAI/preprocessing/constructed.py
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
@staticmethod
def get_feature(name: str) -> FeatureBase or None:
    """
    Retrieves a feature object by its name from the managed list.

    Args:
        name (str): The name of the feature to retrieve.

    Returns:
        FeatureBase or None: The found feature object, or None if not found.
    """
    for f in FeatureConstruction.features:
        if f.feature == name:
            return f
    return None
process(df: DataFrame) staticmethod

Processes the input DataFrame by applying all registered feature transformations in order. Each feature's process method is called, which typically adds a new column to df if it doesn't already exist.

Parameters:

Name Type Description Default
df DataFrame

The DataFrame to process and add features to.

required
Source code in physXAI/preprocessing/constructed.py
557
558
559
560
561
562
563
564
565
566
567
568
569
@staticmethod
def process(df: DataFrame):
    """
    Processes the input DataFrame by applying all registered feature transformations in order.
    Each feature's `process` method is called, which typically adds a new column to `df`
    if it doesn't already exist.

    Args:
        df (DataFrame): The DataFrame to process and add features to.
    """

    for f in FeatureConstruction.features:
        f.process(df)
get_config() -> list staticmethod

Returns a list of configuration dictionaries for all managed features. This list can be serialized (e.g., to JSON) to save the feature pipeline.

Source code in physXAI/preprocessing/constructed.py
571
572
573
574
575
576
577
578
579
@staticmethod
def get_config() -> list:
    """
    Returns a list of configuration dictionaries for all managed features.
    This list can be serialized (e.g., to JSON) to save the feature pipeline.
    """

    item_configs = [item.get_config() for item in FeatureConstruction.features]
    return item_configs
from_config(config: list) staticmethod

Reconstructs the feature engineering pipeline from a list of configuration dictionaries. Clears any existing features and populates FeatureConstruction.features with newly created feature objects based on the provided configurations.

Parameters:

Name Type Description Default
config List[dict]

A list where each dictionary is the configuration for a single feature object.

required
Source code in physXAI/preprocessing/constructed.py
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
@staticmethod
def from_config(config: list):
    """
    Reconstructs the feature engineering pipeline from a list of configuration dictionaries.
    Clears any existing features and populates `FeatureConstruction.features` with
    newly created feature objects based on the provided configurations.

    Args:
        config (List[dict]): A list where each dictionary is the configuration
                                  for a single feature object.
    """

    FeatureConstruction.features = list[FeatureBase]()
    for item_conf in config:
        f = FeatureConstruction.get_feature(item_conf['name'])
        if f is None:
            feature_from_config(item_conf)

Functions

register_feature(cls)

A class decorator that registers the decorated class in the CONSTRUCTED_CLASS_REGISTRY. The class is registered using its name.

Source code in physXAI/preprocessing/constructed.py
132
133
134
135
136
137
138
139
140
def register_feature(cls):
    """
    A class decorator that registers the decorated class in the CONSTRUCTED_CLASS_REGISTRY.
    The class is registered using its __name__.
    """
    if cls.__name__ in CONSTRUCTED_CLASS_REGISTRY:  # pragma: no cover
        print(f"Warning: Class '{cls.__name__}' is already registered. Overwriting.")  # pragma: no cover
    CONSTRUCTED_CLASS_REGISTRY[cls.__name__] = cls
    return cls  # Decorators must return the class (or a replacement)

feature_from_config(item_conf: dict) -> FeatureBase

Factory function to create a feature object from its configuration dictionary.

Parameters:

Name Type Description Default
item_conf dict

The configuration dictionary for a single feature. Must contain 'class_name' and other necessary parameters.

required

Returns:

Name Type Description
FeatureBase FeatureBase

An instance of the appropriate feature subclass.

Raises:

Type Description
KeyError

If 'class_name' is not in item_conf or if the class_name is not in ITEM_CLASS_REGISTRY.

Source code in physXAI/preprocessing/constructed.py
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
def feature_from_config(item_conf: dict) -> 'FeatureBase':
    """
    Factory function to create a feature object from its configuration dictionary.

    Args:
        item_conf (dict): The configuration dictionary for a single feature.
                          Must contain 'class_name' and other necessary parameters.

    Returns:
        FeatureBase: An instance of the appropriate feature subclass.

    Raises:
        KeyError: If 'class_name' is not in `item_conf` or if the class_name is not in `ITEM_CLASS_REGISTRY`.
    """
    class_name = item_conf['class_name']
    feature_class = CONSTRUCTED_CLASS_REGISTRY[class_name]
    f1f = feature_class.from_config(item_conf)
    return f1f