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.
Files changed (36) hide show
  1. classifier_toolkit-0.2.1/LICENSE +21 -0
  2. {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/PKG-INFO +2 -1
  3. {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/eda/eda_toolkit.py +50 -62
  4. {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/eda/feature_engineering.py +194 -37
  5. {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/eda/univariate_analysis.py +176 -238
  6. {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/eda/visualizations.py +139 -2
  7. {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/feature_selection/__init__.py +18 -4
  8. {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/feature_selection/base.py +141 -13
  9. {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/feature_selection/embedded_methods/__init__.py +2 -2
  10. classifier_toolkit-0.2.1/classifier_toolkit/feature_selection/embedded_methods/elastic_net.py +297 -0
  11. classifier_toolkit-0.2.1/classifier_toolkit/feature_selection/feature_stability.py +246 -0
  12. classifier_toolkit-0.2.1/classifier_toolkit/feature_selection/meta_selector.py +323 -0
  13. classifier_toolkit-0.2.1/classifier_toolkit/feature_selection/utils/data_handling.py +287 -0
  14. classifier_toolkit-0.2.1/classifier_toolkit/feature_selection/utils/plottings.py +209 -0
  15. classifier_toolkit-0.2.1/classifier_toolkit/feature_selection/utils/scoring.py +121 -0
  16. classifier_toolkit-0.2.1/classifier_toolkit/feature_selection/wrapper_methods/__init__.py +23 -0
  17. classifier_toolkit-0.2.1/classifier_toolkit/feature_selection/wrapper_methods/bayesian_search.py +265 -0
  18. classifier_toolkit-0.2.1/classifier_toolkit/feature_selection/wrapper_methods/boruta.py +900 -0
  19. classifier_toolkit-0.2.1/classifier_toolkit/feature_selection/wrapper_methods/rfe.py +461 -0
  20. classifier_toolkit-0.2.1/classifier_toolkit/feature_selection/wrapper_methods/rfe_catboost.py +337 -0
  21. {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/feature_selection/wrapper_methods/sequential_selection.py +179 -51
  22. {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/pyproject.toml +11 -1
  23. classifier_toolkit-0.1.4/classifier_toolkit/feature_selection/embedded_methods/elastic_net.py +0 -178
  24. classifier_toolkit-0.1.4/classifier_toolkit/feature_selection/meta_selector.py +0 -329
  25. classifier_toolkit-0.1.4/classifier_toolkit/feature_selection/utils/plottings.py +0 -65
  26. classifier_toolkit-0.1.4/classifier_toolkit/feature_selection/utils/scoring.py +0 -86
  27. classifier_toolkit-0.1.4/classifier_toolkit/feature_selection/wrapper_methods/__init__.py +0 -11
  28. classifier_toolkit-0.1.4/classifier_toolkit/feature_selection/wrapper_methods/rfe.py +0 -345
  29. {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/README.md +0 -0
  30. {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/eda/__init__.py +0 -0
  31. {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/eda/bivariate_analysis.py +0 -0
  32. {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/eda/first_glance.py +0 -0
  33. {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/eda/warnings/__init__.py +0 -0
  34. {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/eda/warnings/automated_warnings.py +0 -0
  35. {classifier_toolkit-0.1.4 → classifier_toolkit-0.2.1}/classifier_toolkit/eda/warnings/default_warnings.py +0 -0
  36. {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.4
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}.csv"
39
- interm.to_csv(path_to_save, index=False)
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 print_psi(
296
+ def plot_psi(
284
297
  self,
285
- buckets: int,
286
- bucket_type: Literal["quantile", "fixed_width"],
287
- time_column: str,
288
- time_expected: Union[str, datetime, date],
289
- end_date: Union[str, datetime, date],
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
- The type of binning to use.
302
- time_column : str
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
- # Print data info after PSI calculation
346
- print(
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.
@@ -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
- missing_columns = [
955
- col for col in self.encode_list if col not in self.data.columns
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
- data = self._encoding_categorical_one_hot(data)
966
- print(f"One-hot encoding performed on columns: {self.encode_list}")
967
- # Get the names of the new encoded columns
968
- new_columns = [
969
- col for col in data.columns if col.startswith(tuple(self.encode_list))
970
- ]
971
- print("\nHead of encoded columns:")
972
- print(data[new_columns].head())
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
- data = self._encoding_categorical_distribution(data)
975
- print(f"Distribution encoding performed on columns: {self.encode_list}")
976
- print("\nHead of encoded columns:")
977
- print(data[self.encode_list].head())
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
- data = self._encoding_categorical_woe(data)
980
- print(f"WOE encoding performed on columns: {self.encode_list}")
981
- print("\nHead of encoded columns:")
982
- print(data[self.encode_list].head())
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
- data = self._encoding_categorical_catboost(data)
985
- print(f"CatBoost encoding performed on columns: {self.encode_list}")
986
- print("\nHead of encoded columns:")
987
- print(data[self.encode_list].head())
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
- self.data = data
994
- self.data.info()
995
- self._log_transformation(
996
- "encoding_categorical",
997
- {"method": method, "selected_columns": self.encode_list},
998
- )
999
- return self.data
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
+ '''