classifier-toolkit 0.1.0__py3-none-any.whl

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 (26) hide show
  1. classifier_toolkit/eda/__init__.py +19 -0
  2. classifier_toolkit/eda/bivariate_analysis.py +413 -0
  3. classifier_toolkit/eda/eda_toolkit.py +549 -0
  4. classifier_toolkit/eda/feature_engineering.py +1310 -0
  5. classifier_toolkit/eda/first_glance.py +253 -0
  6. classifier_toolkit/eda/univariate_analysis.py +778 -0
  7. classifier_toolkit/eda/visualizations.py +1248 -0
  8. classifier_toolkit/eda/warnings/__init__.py +4 -0
  9. classifier_toolkit/eda/warnings/automated_warnings.py +20 -0
  10. classifier_toolkit/eda/warnings/default_warnings.py +286 -0
  11. classifier_toolkit/feature_selection/__init__.py +33 -0
  12. classifier_toolkit/feature_selection/base.py +182 -0
  13. classifier_toolkit/feature_selection/embedded_methods/__init__.py +7 -0
  14. classifier_toolkit/feature_selection/embedded_methods/elastic_net.py +178 -0
  15. classifier_toolkit/feature_selection/meta_selector.py +329 -0
  16. classifier_toolkit/feature_selection/utils/__init__.py +17 -0
  17. classifier_toolkit/feature_selection/utils/plottings.py +65 -0
  18. classifier_toolkit/feature_selection/utils/scoring.py +86 -0
  19. classifier_toolkit/feature_selection/wrapper_methods/__init__.py +11 -0
  20. classifier_toolkit/feature_selection/wrapper_methods/rfe.py +345 -0
  21. classifier_toolkit/feature_selection/wrapper_methods/sequential_selection.py +566 -0
  22. classifier_toolkit/model_fitting/__init__.py +0 -0
  23. classifier_toolkit/model_fitting/model_search.py +3 -0
  24. classifier_toolkit-0.1.0.dist-info/METADATA +99 -0
  25. classifier_toolkit-0.1.0.dist-info/RECORD +26 -0
  26. classifier_toolkit-0.1.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,345 @@
1
+ from typing import List, Literal, Optional
2
+
3
+ import pandas as pd
4
+ from catboost import (
5
+ CatBoostClassifier,
6
+ EFeaturesSelectionAlgorithm,
7
+ EShapCalcType,
8
+ Pool,
9
+ )
10
+ from lightgbm import LGBMClassifier
11
+ from sklearn.ensemble import RandomForestClassifier
12
+ from sklearn.feature_selection import RFECV
13
+ from xgboost import XGBClassifier
14
+
15
+ from classifier_toolkit.feature_selection.base import (
16
+ BaseFeatureSelector,
17
+ FeatureSelectionError,
18
+ )
19
+ from classifier_toolkit.feature_selection.utils.plottings import plot_rfecv_results
20
+
21
+
22
+ class EnsembleFeatureSelector(BaseFeatureSelector):
23
+ """
24
+ A feature selector that uses ensemble methods for feature selection.
25
+
26
+ This class implements feature selection using various ensemble methods such as
27
+ LightGBM, XGBoost, Random Forest, and CatBoost. It supports both Recursive Feature
28
+ Elimination (RFE) and CatBoost's built-in feature selection.
29
+
30
+ Parameters
31
+ ----------
32
+ estimator : {"xgboost", "random_forest", "lightgbm", "catboost"}, default="lightgbm"
33
+ The estimator to use for feature selection.
34
+ n_features_to_select : int, optional
35
+ The number of features to select. If None, half of the features are selected.
36
+ scoring : {"accuracy", "f1", "precision", "recall", "roc_auc"}, default="roc_auc"
37
+ The scoring metric to use for feature selection.
38
+ cv : int, default=5
39
+ The number of cross-validation folds.
40
+ verbose : int, default=0
41
+ Controls the verbosity of the feature selection process.
42
+ method : str, default="rfe"
43
+ The feature selection method to use. Either "rfe" or "catboost".
44
+ step : int, default=1
45
+ The number of features to remove at each iteration for RFE.
46
+ n_jobs : int, default=-1
47
+ The number of jobs to run in parallel. -1 means using all processors.
48
+ catboost_params : dict, optional
49
+ Additional parameters for CatBoost.
50
+ min_features_to_select : int, default=1
51
+ The minimum number of features to select.
52
+
53
+ Attributes
54
+ ----------
55
+ estimator_name : str
56
+ The name of the estimator used.
57
+ estimator : object
58
+ The estimator object.
59
+ selected_features_ : list
60
+ The list of selected feature names after fitting.
61
+ feature_importances_ : pd.Series
62
+ The importance scores for each feature after fitting.
63
+ rfecv : RFECV
64
+ The RFECV object if RFE method is used.
65
+
66
+ Methods
67
+ -------
68
+ fit(X, y)
69
+ Fit the feature selector to the data.
70
+ transform(X)
71
+ Transform the data to include only selected features.
72
+ get_feature_importances()
73
+ Get the importance scores for each feature.
74
+ print_feature_importances()
75
+ Print the feature importances in descending order.
76
+ """
77
+
78
+ def __init__(
79
+ self,
80
+ estimator: Literal[
81
+ "xgboost", "random_forest", "lightgbm", "catboost"
82
+ ] = "lightgbm",
83
+ n_features_to_select: int = 1,
84
+ scoring: Literal[
85
+ "accuracy", "f1", "precision", "recall", "roc_auc"
86
+ ] = "roc_auc",
87
+ cv: int = 5,
88
+ verbose: int = 0,
89
+ method: str = "rfe",
90
+ step: int = 1,
91
+ n_jobs: int = -1,
92
+ catboost_params: Optional[dict] = None,
93
+ min_features_to_select: int = 1,
94
+ **kwargs,
95
+ ) -> None:
96
+ self.catboost_params = catboost_params or {}
97
+ self.estimator_name = estimator
98
+ self.estimator = self._get_estimator(estimator) # type: ignore
99
+ super().__init__(
100
+ estimator=self.estimator,
101
+ n_features_to_select=n_features_to_select,
102
+ scoring=scoring,
103
+ cv=cv,
104
+ verbose=verbose,
105
+ **kwargs,
106
+ )
107
+ self.method = method
108
+ self.step = step
109
+ self.n_jobs = n_jobs
110
+ self.min_features_to_select = min_features_to_select
111
+
112
+ def _get_estimator(self, estimator_name):
113
+ """
114
+ Get the estimator based on the provided name.
115
+
116
+ Parameters
117
+ ----------
118
+ estimator_name : str
119
+ The name of the estimator to use.
120
+
121
+ Returns
122
+ -------
123
+ object
124
+ The initialized estimator.
125
+
126
+ Raises
127
+ ------
128
+ ValueError
129
+ If the estimator name is not recognized.
130
+ """
131
+ if estimator_name == "lightgbm":
132
+ return LGBMClassifier()
133
+ elif estimator_name == "xgboost":
134
+ return XGBClassifier()
135
+ elif estimator_name == "random_forest":
136
+ return RandomForestClassifier()
137
+ elif estimator_name == "catboost":
138
+ return CatBoostClassifier(**self.catboost_params)
139
+ else:
140
+ raise ValueError(f"Unknown estimator: {estimator_name}")
141
+
142
+ def fit(self, X: pd.DataFrame, y: pd.Series) -> "EnsembleFeatureSelector":
143
+ """
144
+ Fit the feature selector to the data.
145
+
146
+ Parameters
147
+ ----------
148
+ X : pd.DataFrame
149
+ The input features.
150
+ y : pd.Series
151
+ The target variable.
152
+
153
+ Returns
154
+ -------
155
+ EnsembleFeatureSelector
156
+ The fitted feature selector.
157
+ """
158
+ if self.estimator_name == "catboost":
159
+ self._fit_catboost(X, y)
160
+ elif self.method == "rfe":
161
+ self._fit_rfe(X, y)
162
+ else:
163
+ raise ValueError(
164
+ "Invalid method. Choose 'rfe' or use 'catboost' estimator."
165
+ )
166
+
167
+ return self
168
+
169
+ def _fit_rfe(self, X: pd.DataFrame, y: pd.Series) -> None:
170
+ """
171
+ Fit the RFE method to the data.
172
+
173
+ Parameters
174
+ ----------
175
+ X : pd.DataFrame
176
+ The input features.
177
+ y : pd.Series
178
+ The target variable.
179
+
180
+ Raises
181
+ ------
182
+ ValueError
183
+ If the estimator is not provided.
184
+ """
185
+ if self.estimator is None:
186
+ raise ValueError("Estimator must be provided for RFE method")
187
+ self.rfecv = RFECV(
188
+ estimator=self.estimator,
189
+ step=self.step,
190
+ cv=self.cv,
191
+ scoring=self.scoring,
192
+ n_jobs=self.n_jobs,
193
+ verbose=self.verbose,
194
+ min_features_to_select=self.min_features_to_select,
195
+ )
196
+ self.rfecv.fit(X, y)
197
+ self.selected_features_ = X.columns[self.rfecv.support_].tolist()
198
+ self.feature_importances_ = pd.Series(self.rfecv.ranking_, index=X.columns)
199
+
200
+ def _fit_catboost(self, X: pd.DataFrame, y: pd.Series) -> None:
201
+ """
202
+ Fit the CatBoost method to the data.
203
+
204
+ Parameters
205
+ ----------
206
+ X : pd.DataFrame
207
+ The input features.
208
+ y : pd.Series
209
+ The target variable.
210
+ """
211
+ model = CatBoostClassifier(**self.catboost_params)
212
+
213
+ # Create CatBoost Pool objects
214
+ train_pool = Pool(X, y)
215
+
216
+ # Calculate the number of features to select
217
+ nb_features = X.shape[1]
218
+ features_select = (
219
+ nb_features - 1
220
+ if self.n_features_to_select is None
221
+ else self.n_features_to_select
222
+ )
223
+
224
+ # Run feature selection
225
+ summary = model.select_features(
226
+ train_pool,
227
+ eval_set=None, # We're not using a separate evaluation set here
228
+ features_for_select=f"0-{nb_features - 1}",
229
+ num_features_to_select=features_select,
230
+ algorithm=EFeaturesSelectionAlgorithm.RecursiveByShapValues,
231
+ shap_calc_type=EShapCalcType.Regular,
232
+ train_final_model=False,
233
+ logging_level="Silent",
234
+ plot=True,
235
+ )
236
+
237
+ # Extract selected features and their importances
238
+ self.selected_features_ = [X.columns[i] for i in summary["selected_features"]]
239
+
240
+ print(summary)
241
+
242
+ def get_feature_importances(self) -> pd.Series:
243
+ """
244
+ Get the feature importances (scores) from the fitted model.
245
+
246
+ Returns
247
+ -------
248
+ pd.Series
249
+ The feature importances.
250
+
251
+ Raises
252
+ ------
253
+ FeatureSelectionError
254
+ If the selector has not been fitted yet.
255
+ """
256
+ if self.feature_importances_ is None:
257
+ raise FeatureSelectionError(
258
+ "Selector has not been fitted yet. Call 'fit' first."
259
+ )
260
+ return self.feature_importances_
261
+
262
+ def _get_score(self, X: pd.DataFrame, y: pd.Series, features: List[int]) -> float:
263
+ """
264
+ Calculate the cross-validation score for a subset of features.
265
+
266
+ Parameters
267
+ ----------
268
+ X : pd.DataFrame
269
+ The input features.
270
+ y : pd.Series
271
+ The target variable.
272
+ features : List[int]
273
+ The indices of features to use.
274
+
275
+ Returns
276
+ -------
277
+ float
278
+ The mean cross-validation score.
279
+ """
280
+ X_subset = X.iloc[:, features]
281
+ self.estimator.fit(X_subset, y) # type: ignore
282
+ return self.scorer(self.estimator, X_subset, y)
283
+
284
+ def transform(self, X: pd.DataFrame) -> pd.DataFrame:
285
+ """
286
+ Transform the input data by selecting the chosen features.
287
+
288
+ Parameters
289
+ ----------
290
+ X : pd.DataFrame
291
+ The input features.
292
+
293
+ Returns
294
+ -------
295
+ pd.DataFrame
296
+ The transformed data containing only the selected features.
297
+
298
+ Raises
299
+ ------
300
+ FeatureSelectionError
301
+ If the selector has not been fitted yet.
302
+ """
303
+ if self.selected_features_ is None:
304
+ raise FeatureSelectionError(
305
+ "Selector has not been fitted yet. Call 'fit' first."
306
+ )
307
+ return X[self.selected_features_]
308
+
309
+ def plot_results(self) -> None:
310
+ """
311
+ Plot the results of the RFECV feature selection process.
312
+
313
+ Raises
314
+ ------
315
+ ValueError
316
+ If the RFECV results are not available.
317
+ """
318
+ try:
319
+ plot_rfecv_results(self)
320
+ except ValueError as e:
321
+ print(f"Error plotting RFECV results: {e}")
322
+
323
+ def print_feature_importances(self):
324
+ """
325
+ Print the feature importances (scores) in descending order.
326
+
327
+ Raises
328
+ ------
329
+ FeatureSelectionError
330
+ If feature importances are not available (i.e., fit hasn't been called).
331
+ """
332
+ if self.feature_importances_ is None:
333
+ raise FeatureSelectionError(
334
+ "Feature importances are not available. Call 'fit' first."
335
+ )
336
+
337
+ sorted_importances = sorted(
338
+ zip(self.feature_importances_.index, self.feature_importances_),
339
+ key=lambda x: x[1],
340
+ reverse=True,
341
+ )
342
+
343
+ print("Feature importances (in descending order):")
344
+ for feature, importance in sorted_importances:
345
+ print(f"('{feature}', {importance:.6f})")