classifier-toolkit 0.1.4__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- classifier_toolkit-0.2.1/LICENSE +21 -0
- {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/PKG-INFO +2 -1
- {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/eda/eda_toolkit.py +50 -62
- {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/eda/feature_engineering.py +194 -37
- {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/eda/univariate_analysis.py +176 -238
- {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/eda/visualizations.py +139 -2
- {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/feature_selection/__init__.py +18 -4
- {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/feature_selection/base.py +141 -13
- {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/feature_selection/embedded_methods/__init__.py +2 -2
- classifier_toolkit-0.2.1/classifier_toolkit/feature_selection/embedded_methods/elastic_net.py +297 -0
- classifier_toolkit-0.2.1/classifier_toolkit/feature_selection/feature_stability.py +246 -0
- classifier_toolkit-0.2.1/classifier_toolkit/feature_selection/meta_selector.py +323 -0
- classifier_toolkit-0.2.1/classifier_toolkit/feature_selection/utils/data_handling.py +287 -0
- classifier_toolkit-0.2.1/classifier_toolkit/feature_selection/utils/plottings.py +209 -0
- classifier_toolkit-0.2.1/classifier_toolkit/feature_selection/utils/scoring.py +121 -0
- classifier_toolkit-0.2.1/classifier_toolkit/feature_selection/wrapper_methods/__init__.py +23 -0
- classifier_toolkit-0.2.1/classifier_toolkit/feature_selection/wrapper_methods/bayesian_search.py +265 -0
- classifier_toolkit-0.2.1/classifier_toolkit/feature_selection/wrapper_methods/boruta.py +900 -0
- classifier_toolkit-0.2.1/classifier_toolkit/feature_selection/wrapper_methods/rfe.py +461 -0
- classifier_toolkit-0.2.1/classifier_toolkit/feature_selection/wrapper_methods/rfe_catboost.py +337 -0
- {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/feature_selection/wrapper_methods/sequential_selection.py +179 -51
- {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/pyproject.toml +11 -1
- classifier_toolkit-0.1.4/classifier_toolkit/feature_selection/embedded_methods/elastic_net.py +0 -178
- classifier_toolkit-0.1.4/classifier_toolkit/feature_selection/meta_selector.py +0 -329
- classifier_toolkit-0.1.4/classifier_toolkit/feature_selection/utils/plottings.py +0 -65
- classifier_toolkit-0.1.4/classifier_toolkit/feature_selection/utils/scoring.py +0 -86
- classifier_toolkit-0.1.4/classifier_toolkit/feature_selection/wrapper_methods/__init__.py +0 -11
- classifier_toolkit-0.1.4/classifier_toolkit/feature_selection/wrapper_methods/rfe.py +0 -345
- {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/README.md +0 -0
- {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/eda/__init__.py +0 -0
- {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/eda/bivariate_analysis.py +0 -0
- {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/eda/first_glance.py +0 -0
- {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/eda/warnings/__init__.py +0 -0
- {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/eda/warnings/automated_warnings.py +0 -0
- {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/eda/warnings/default_warnings.py +0 -0
- {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/feature_selection/utils/__init__.py +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Olinda SAS
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: classifier-toolkit
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary:
|
|
5
5
|
Author: gauthier.marquand
|
|
6
6
|
Author-email: gauthier.marquand@qonto.com
|
|
@@ -19,6 +19,7 @@ Requires-Dist: numpy (<2.0)
|
|
|
19
19
|
Requires-Dist: optuna (>=3.5.0,<4.0.0)
|
|
20
20
|
Requires-Dist: pandas (>=2.2.0,<3.0.0)
|
|
21
21
|
Requires-Dist: polars (>=1.2.1,<2.0.0)
|
|
22
|
+
Requires-Dist: pyarrow (>=18.0.0,<19.0.0)
|
|
22
23
|
Requires-Dist: scikit-learn (>=1.4.0,<2.0.0)
|
|
23
24
|
Requires-Dist: scipy (<1.14)
|
|
24
25
|
Requires-Dist: shap (>=0.44.1,<0.45.0)
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from datetime import date, datetime
|
|
2
1
|
from pathlib import Path
|
|
3
2
|
from typing import Any, Dict, List, Literal, Optional, Union
|
|
4
3
|
|
|
@@ -35,8 +34,8 @@ class EDAToolkit:
|
|
|
35
34
|
"Please make sure to specify the path in the constants file."
|
|
36
35
|
)
|
|
37
36
|
else:
|
|
38
|
-
path_to_save = f"{path}/df_{fname}.
|
|
39
|
-
interm.
|
|
37
|
+
path_to_save = f"{path}/df_{fname}.parquet"
|
|
38
|
+
interm.to_parquet(path_to_save, index=False)
|
|
40
39
|
|
|
41
40
|
def __init__(
|
|
42
41
|
self,
|
|
@@ -88,6 +87,9 @@ class EDAToolkit:
|
|
|
88
87
|
# Initialize an empty list for transformations
|
|
89
88
|
self.transformations: TransformationType = []
|
|
90
89
|
|
|
90
|
+
# Create the empty transformer object for normality
|
|
91
|
+
self.normality_transformer = None
|
|
92
|
+
|
|
91
93
|
# Initialize the warning system
|
|
92
94
|
default_warning_system = get_default_warnings(
|
|
93
95
|
df=self.dataframe,
|
|
@@ -153,6 +155,17 @@ class EDAToolkit:
|
|
|
153
155
|
]
|
|
154
156
|
return self.transformations
|
|
155
157
|
|
|
158
|
+
def get_normality_transformer(self):
|
|
159
|
+
"""
|
|
160
|
+
Get the normality transformer object.
|
|
161
|
+
|
|
162
|
+
Returns
|
|
163
|
+
-------
|
|
164
|
+
Any
|
|
165
|
+
The normality transformer object.
|
|
166
|
+
"""
|
|
167
|
+
return self.normality_transformer
|
|
168
|
+
|
|
156
169
|
def register_custom_warning(self, name: str, warning_obj: EDAWarning):
|
|
157
170
|
"""
|
|
158
171
|
Register a custom warning function.
|
|
@@ -280,74 +293,33 @@ class EDAToolkit:
|
|
|
280
293
|
time_column, number_of_partitions
|
|
281
294
|
)
|
|
282
295
|
|
|
283
|
-
def
|
|
296
|
+
def plot_psi(
|
|
284
297
|
self,
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
start_date: Union[str, datetime, date],
|
|
291
|
-
test_data: Optional[pd.DataFrame] = None,
|
|
298
|
+
train: pd.DataFrame,
|
|
299
|
+
test: pd.DataFrame,
|
|
300
|
+
feature_cols: List[str],
|
|
301
|
+
buckets: int = 10,
|
|
302
|
+
bucket_type: Literal["quantile", "fixed_width"] = "quantile",
|
|
292
303
|
):
|
|
293
304
|
"""
|
|
294
305
|
Print Population Stability Index (PSI) for numerical features.
|
|
295
306
|
|
|
296
307
|
Parameters
|
|
297
308
|
----------
|
|
309
|
+
train : pd.DataFrame
|
|
310
|
+
Training dataset.
|
|
311
|
+
test : pd.DataFrame
|
|
312
|
+
Testing dataset.
|
|
313
|
+
feature_cols : List[str]
|
|
314
|
+
List of feature columns to analyze.
|
|
298
315
|
buckets : int
|
|
299
|
-
Number of buckets for binning.
|
|
316
|
+
Number of buckets to use for binning. Default is 10.
|
|
300
317
|
bucket_type : Literal["quantile", "fixed_width"]
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
Name of the time column.
|
|
304
|
-
time_expected : Union[str, datetime, date]
|
|
305
|
-
Expected time for PSI calculation.
|
|
306
|
-
end_date : Union[str, datetime, date]
|
|
307
|
-
End date for PSI calculation.
|
|
308
|
-
start_date : Union[str, datetime, date]
|
|
309
|
-
Start date for PSI calculation.
|
|
310
|
-
test_data : Optional[pd.DataFrame], optional
|
|
311
|
-
Test dataset for PSI calculation, by default None.
|
|
312
|
-
"""
|
|
313
|
-
print("Calculating PSI with the following parameters:")
|
|
314
|
-
print(f"Time column: {time_column}")
|
|
315
|
-
print(f"Start date: {start_date}")
|
|
316
|
-
print(f"End date: {end_date}")
|
|
317
|
-
print(f"Expected time (split point): {time_expected}")
|
|
318
|
-
print(f"Number of buckets: {buckets}")
|
|
319
|
-
print(f"Bucket type: {bucket_type}")
|
|
320
|
-
print(f"Using separate test data: {'Yes' if test_data is not None else 'No'}")
|
|
321
|
-
print("=" * 60)
|
|
322
|
-
|
|
323
|
-
# Print data info before PSI calculation
|
|
324
|
-
print(f"Data shape before PSI calculation: {self.dataframe.shape}")
|
|
325
|
-
print(
|
|
326
|
-
f"Date range in data: {self.dataframe[time_column].min()} to {self.dataframe[time_column].max()}"
|
|
327
|
-
)
|
|
328
|
-
|
|
329
|
-
if test_data is not None:
|
|
330
|
-
print(f"Test data shape: {test_data.shape}")
|
|
331
|
-
print(
|
|
332
|
-
f"Date range in test data: {test_data[time_column].min()} to {test_data[time_column].max()}"
|
|
333
|
-
)
|
|
334
|
-
|
|
335
|
-
self.univariate_analysis.compute_psi(
|
|
336
|
-
time_column,
|
|
337
|
-
time_expected,
|
|
338
|
-
start_date,
|
|
339
|
-
end_date,
|
|
340
|
-
test_data,
|
|
341
|
-
buckets,
|
|
342
|
-
bucket_type,
|
|
343
|
-
)
|
|
318
|
+
Type of bucketing to use. Default is "quantile".
|
|
319
|
+
"""
|
|
344
320
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
f"Data shape after PSI calculation: {self.univariate_analysis.data.shape}"
|
|
348
|
-
)
|
|
349
|
-
print(
|
|
350
|
-
f"Date range in filtered data: {self.univariate_analysis.data[time_column].min()} to {self.univariate_analysis.data[time_column].max()}"
|
|
321
|
+
self.univariate_analysis.visualize_psi_for_features(
|
|
322
|
+
train, test, feature_cols, buckets, bucket_type
|
|
351
323
|
)
|
|
352
324
|
|
|
353
325
|
def plot_num_cat_anova(self):
|
|
@@ -425,7 +397,8 @@ class EDAToolkit:
|
|
|
425
397
|
transformations=transforms,
|
|
426
398
|
)
|
|
427
399
|
|
|
428
|
-
self.feature_engineering.diagnose_normality_transform()
|
|
400
|
+
_, transformer = self.feature_engineering.diagnose_normality_transform()
|
|
401
|
+
self.normality_transformer = transformer
|
|
429
402
|
|
|
430
403
|
def normalize_numericals(self, to_transformed):
|
|
431
404
|
"""
|
|
@@ -521,6 +494,21 @@ class EDAToolkit:
|
|
|
521
494
|
"""
|
|
522
495
|
self.visualizations.plot_numerical_target_frequency_line(scale=scaling)
|
|
523
496
|
|
|
497
|
+
def plot_boxplots(self):
|
|
498
|
+
"""
|
|
499
|
+
Plot boxplots for numerical columns.
|
|
500
|
+
|
|
501
|
+
Parameters
|
|
502
|
+
----------
|
|
503
|
+
num_rows : int
|
|
504
|
+
Number of rows to display in the output.
|
|
505
|
+
scale : bool
|
|
506
|
+
If True, apply scaling to the columns before plotting.
|
|
507
|
+
log_scale : bool
|
|
508
|
+
If True, apply log scaling to the columns before plotting.
|
|
509
|
+
"""
|
|
510
|
+
self.visualizations.plot_boxplots()
|
|
511
|
+
|
|
524
512
|
def print_crosstab_freqs(self, num_rows):
|
|
525
513
|
"""
|
|
526
514
|
Print cross-tabulation frequencies for categorical columns.
|
{classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/eda/feature_engineering.py
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import math
|
|
2
|
-
from typing import List, Literal, NamedTuple, Optional
|
|
2
|
+
from typing import List, Literal, NamedTuple, Optional, Union
|
|
3
3
|
|
|
4
4
|
import matplotlib.pyplot as plt
|
|
5
5
|
import numpy as np
|
|
@@ -228,7 +228,7 @@ class FeatureEngineering:
|
|
|
228
228
|
method: Literal["power", "standard"] = "power",
|
|
229
229
|
alpha: float = 0.05,
|
|
230
230
|
test_mode: bool = False,
|
|
231
|
-
) -> dict:
|
|
231
|
+
) -> tuple[dict, Union[PowerTransformer, StandardScaler]]:
|
|
232
232
|
transformed_data = self.data.copy()
|
|
233
233
|
results = {}
|
|
234
234
|
for column in self.numerical_columns:
|
|
@@ -332,7 +332,7 @@ class FeatureEngineering:
|
|
|
332
332
|
self._log_transformation(
|
|
333
333
|
"normality_transform", {"method": method, "alpha": alpha}
|
|
334
334
|
)
|
|
335
|
-
return results
|
|
335
|
+
return results, transformer
|
|
336
336
|
|
|
337
337
|
def apply_normality_transformations(
|
|
338
338
|
self, columns_to_keep: List[str]
|
|
@@ -937,66 +937,156 @@ class FeatureEngineering:
|
|
|
937
937
|
def encoding_categorical(
|
|
938
938
|
self,
|
|
939
939
|
method: Literal["one_hot", "distribution", "woe", "catboost"] = "one_hot",
|
|
940
|
+
fit: bool = True,
|
|
941
|
+
data: Optional[pd.DataFrame] = None,
|
|
942
|
+
verbose: bool = False,
|
|
940
943
|
) -> pd.DataFrame:
|
|
941
944
|
"""
|
|
942
945
|
Perform encoding for the specified categorical columns using the chosen method.
|
|
943
946
|
|
|
944
|
-
The columns to be encoded are specified during the initialization of the FeatureEngineering class.
|
|
945
|
-
|
|
946
947
|
Parameters
|
|
947
948
|
----------
|
|
948
949
|
method : {'one_hot', 'distribution', 'woe', 'catboost'}, optional
|
|
949
950
|
The encoding method to use, by default 'one_hot'.
|
|
951
|
+
fit : bool, optional
|
|
952
|
+
Whether to fit the encoder (True) or just transform (False), by default True.
|
|
953
|
+
data : Optional[pd.DataFrame], optional
|
|
954
|
+
The data to encode. If None, uses self.data, by default None.
|
|
955
|
+
|
|
956
|
+
Returns
|
|
957
|
+
-------
|
|
958
|
+
pd.DataFrame
|
|
959
|
+
DataFrame with encoded columns.
|
|
950
960
|
"""
|
|
951
961
|
if self.encode_list is None:
|
|
952
962
|
print("No columns specified for encoding.")
|
|
963
|
+
return self.data if data is None else data
|
|
953
964
|
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
]
|
|
965
|
+
data = self.data.copy() if data is None else data.copy()
|
|
966
|
+
|
|
967
|
+
missing_columns = [col for col in self.encode_list if col not in data.columns]
|
|
957
968
|
if missing_columns:
|
|
958
969
|
raise ValueError(
|
|
959
970
|
f"The following columns are not in the DataFrame: {missing_columns}"
|
|
960
971
|
)
|
|
961
972
|
|
|
962
|
-
data = self.data.copy()
|
|
963
|
-
|
|
964
973
|
if method == "one_hot":
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
974
|
+
if fit:
|
|
975
|
+
print("Fitting OneHotEncoder...") if verbose else None
|
|
976
|
+
self.encoder = OneHotEncoder(
|
|
977
|
+
sparse_output=False, drop="first", handle_unknown="ignore"
|
|
978
|
+
)
|
|
979
|
+
encoded_array = self.encoder.fit_transform(data[self.encode_list])
|
|
980
|
+
else:
|
|
981
|
+
print(
|
|
982
|
+
"Transforming data with OneHotEncoder fit before..."
|
|
983
|
+
) if verbose else None
|
|
984
|
+
encoded_array = self.encoder.transform(data[self.encode_list])
|
|
985
|
+
|
|
986
|
+
encoded_df = pd.DataFrame(
|
|
987
|
+
encoded_array, # type: ignore
|
|
988
|
+
columns=self.encoder.get_feature_names_out(self.encode_list),
|
|
989
|
+
index=data.index,
|
|
990
|
+
) # type: ignore
|
|
991
|
+
|
|
992
|
+
data = data.drop(columns=self.encode_list)
|
|
993
|
+
data = pd.concat([data, encoded_df], axis=1)
|
|
994
|
+
|
|
973
995
|
elif method == "distribution":
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
996
|
+
if fit:
|
|
997
|
+
print("Fitting distribution maps...") if verbose else None
|
|
998
|
+
self.distribution_maps = {
|
|
999
|
+
col: data[col].value_counts(normalize=True)
|
|
1000
|
+
for col in self.encode_list
|
|
1001
|
+
}
|
|
1002
|
+
for column in self.encode_list:
|
|
1003
|
+
data[column] = (
|
|
1004
|
+
data[column].map(self.distribution_maps[column]).fillna(0)
|
|
1005
|
+
)
|
|
1006
|
+
else:
|
|
1007
|
+
print(
|
|
1008
|
+
"Transforming data with distribution maps fit before..."
|
|
1009
|
+
) if verbose else None
|
|
1010
|
+
if self.distribution_maps:
|
|
1011
|
+
for column in self.encode_list:
|
|
1012
|
+
data[column] = (
|
|
1013
|
+
data[column].map(self.distribution_maps[column]).fillna(0)
|
|
1014
|
+
)
|
|
1015
|
+
else:
|
|
1016
|
+
raise ValueError(
|
|
1017
|
+
"Distribution maps not found. Please fit the encoder first."
|
|
1018
|
+
)
|
|
1019
|
+
|
|
978
1020
|
elif method == "woe":
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
1021
|
+
if fit:
|
|
1022
|
+
print("Fitting WOE maps...") if verbose else None
|
|
1023
|
+
self.woe_maps = {}
|
|
1024
|
+
for column in self.encode_list:
|
|
1025
|
+
target = data[self.target_column]
|
|
1026
|
+
df = pd.DataFrame({column: data[column], "target": target})
|
|
1027
|
+
total_good = df["target"].sum()
|
|
1028
|
+
total_bad = df["target"].count() - total_good
|
|
1029
|
+
grouped = df.groupby(column, observed=False)["target"].agg(
|
|
1030
|
+
["sum", "count"]
|
|
1031
|
+
)
|
|
1032
|
+
grouped["good"] = grouped["sum"]
|
|
1033
|
+
grouped["bad"] = grouped["count"] - grouped["sum"]
|
|
1034
|
+
epsilon = 1e-10
|
|
1035
|
+
grouped["good_pct"] = (grouped["good"] + epsilon) / (
|
|
1036
|
+
total_good + epsilon
|
|
1037
|
+
)
|
|
1038
|
+
grouped["bad_pct"] = (grouped["bad"] + epsilon) / (
|
|
1039
|
+
total_bad + epsilon
|
|
1040
|
+
)
|
|
1041
|
+
grouped["woe"] = np.log(grouped["good_pct"] / grouped["bad_pct"])
|
|
1042
|
+
self.woe_maps[column] = grouped["woe"].to_dict()
|
|
1043
|
+
|
|
1044
|
+
for column in self.encode_list:
|
|
1045
|
+
data[column] = (
|
|
1046
|
+
data[column].astype(str).map(self.woe_maps[column]).fillna(0)
|
|
1047
|
+
)
|
|
1048
|
+
else:
|
|
1049
|
+
print(
|
|
1050
|
+
"Transforming data with WOE maps fit before..."
|
|
1051
|
+
) if verbose else None
|
|
1052
|
+
if self.woe_maps:
|
|
1053
|
+
for column in self.encode_list:
|
|
1054
|
+
data[column] = (
|
|
1055
|
+
data[column]
|
|
1056
|
+
.astype(str)
|
|
1057
|
+
.map(self.woe_maps[column])
|
|
1058
|
+
.fillna(0)
|
|
1059
|
+
)
|
|
1060
|
+
else:
|
|
1061
|
+
raise ValueError(
|
|
1062
|
+
"WOE maps not found. Please fit the encoder first."
|
|
1063
|
+
)
|
|
1064
|
+
|
|
983
1065
|
elif method == "catboost":
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1066
|
+
if fit:
|
|
1067
|
+
print("Fitting CatBoostEncoder...") if verbose else None
|
|
1068
|
+
self.encoder = CatBoostEncoder(cols=self.encode_list)
|
|
1069
|
+
data[self.encode_list] = self.encoder.fit_transform(
|
|
1070
|
+
data[self.encode_list], data[self.target_column]
|
|
1071
|
+
)
|
|
1072
|
+
else:
|
|
1073
|
+
print(
|
|
1074
|
+
"Transforming data with CatBoostEncoder fit before..."
|
|
1075
|
+
) if verbose else None
|
|
1076
|
+
data[self.encode_list] = self.encoder.transform(data[self.encode_list])
|
|
1077
|
+
|
|
988
1078
|
else:
|
|
989
1079
|
raise ValueError(
|
|
990
1080
|
f"Encoding method '{method}' is not supported. Choose from 'one_hot', 'distribution', 'woe', or 'catboost'."
|
|
991
1081
|
)
|
|
992
1082
|
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
return
|
|
1083
|
+
if fit:
|
|
1084
|
+
self._log_transformation(
|
|
1085
|
+
"encoding_categorical",
|
|
1086
|
+
{"method": method, "selected_columns": self.encode_list},
|
|
1087
|
+
)
|
|
1088
|
+
|
|
1089
|
+
return data
|
|
1000
1090
|
|
|
1001
1091
|
####### FEATURE ENGINEERING: OPTIONAL FUNCTIONS #######
|
|
1002
1092
|
def search_pca_components(self):
|
|
@@ -1269,3 +1359,70 @@ class FeatureEngineering:
|
|
|
1269
1359
|
return self.data
|
|
1270
1360
|
|
|
1271
1361
|
percentile_capping = apply_winsorization
|
|
1362
|
+
|
|
1363
|
+
|
|
1364
|
+
'''
|
|
1365
|
+
def encoding_categorical(
|
|
1366
|
+
self,
|
|
1367
|
+
method: Literal["one_hot", "distribution", "woe", "catboost"] = "one_hot",
|
|
1368
|
+
) -> pd.DataFrame:
|
|
1369
|
+
"""
|
|
1370
|
+
Perform encoding for the specified categorical columns using the chosen method.
|
|
1371
|
+
|
|
1372
|
+
The columns to be encoded are specified during the initialization of the FeatureEngineering class.
|
|
1373
|
+
|
|
1374
|
+
Parameters
|
|
1375
|
+
----------
|
|
1376
|
+
method : {'one_hot', 'distribution', 'woe', 'catboost'}, optional
|
|
1377
|
+
The encoding method to use, by default 'one_hot'.
|
|
1378
|
+
"""
|
|
1379
|
+
if self.encode_list is None:
|
|
1380
|
+
print("No columns specified for encoding.")
|
|
1381
|
+
|
|
1382
|
+
missing_columns = [
|
|
1383
|
+
col for col in self.encode_list if col not in self.data.columns
|
|
1384
|
+
]
|
|
1385
|
+
if missing_columns:
|
|
1386
|
+
raise ValueError(
|
|
1387
|
+
f"The following columns are not in the DataFrame: {missing_columns}"
|
|
1388
|
+
)
|
|
1389
|
+
|
|
1390
|
+
data = self.data.copy()
|
|
1391
|
+
|
|
1392
|
+
if method == "one_hot":
|
|
1393
|
+
data = self._encoding_categorical_one_hot(data)
|
|
1394
|
+
print(f"One-hot encoding performed on columns: {self.encode_list}")
|
|
1395
|
+
# Get the names of the new encoded columns
|
|
1396
|
+
new_columns = [
|
|
1397
|
+
col for col in data.columns if col.startswith(tuple(self.encode_list))
|
|
1398
|
+
]
|
|
1399
|
+
print("\nHead of encoded columns:")
|
|
1400
|
+
print(data[new_columns].head())
|
|
1401
|
+
elif method == "distribution":
|
|
1402
|
+
data = self._encoding_categorical_distribution(data)
|
|
1403
|
+
print(f"Distribution encoding performed on columns: {self.encode_list}")
|
|
1404
|
+
print("\nHead of encoded columns:")
|
|
1405
|
+
print(data[self.encode_list].head())
|
|
1406
|
+
elif method == "woe":
|
|
1407
|
+
data = self._encoding_categorical_woe(data)
|
|
1408
|
+
print(f"WOE encoding performed on columns: {self.encode_list}")
|
|
1409
|
+
print("\nHead of encoded columns:")
|
|
1410
|
+
print(data[self.encode_list].head())
|
|
1411
|
+
elif method == "catboost":
|
|
1412
|
+
data = self._encoding_categorical_catboost(data)
|
|
1413
|
+
print(f"CatBoost encoding performed on columns: {self.encode_list}")
|
|
1414
|
+
print("\nHead of encoded columns:")
|
|
1415
|
+
print(data[self.encode_list].head())
|
|
1416
|
+
else:
|
|
1417
|
+
raise ValueError(
|
|
1418
|
+
f"Encoding method '{method}' is not supported. Choose from 'one_hot', 'distribution', 'woe', or 'catboost'."
|
|
1419
|
+
)
|
|
1420
|
+
|
|
1421
|
+
self.data = data
|
|
1422
|
+
self.data.info()
|
|
1423
|
+
self._log_transformation(
|
|
1424
|
+
"encoding_categorical",
|
|
1425
|
+
{"method": method, "selected_columns": self.encode_list},
|
|
1426
|
+
)
|
|
1427
|
+
return self.data
|
|
1428
|
+
'''
|