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,178 @@
1
+ from typing import List, Optional
2
+
3
+ import numpy as np
4
+ import pandas as pd
5
+ from sklearn.base import BaseEstimator
6
+ from sklearn.linear_model import ElasticNetCV, LogisticRegression
7
+ from sklearn.model_selection import cross_val_score
8
+
9
+ from classifier_toolkit.feature_selection.base import (
10
+ BaseFeatureSelector,
11
+ FeatureSelectionError,
12
+ )
13
+
14
+
15
+ class ElasticNetSelector(BaseFeatureSelector):
16
+ """
17
+ Feature selector based on Elastic Net regularization.
18
+
19
+ This class implements feature selection using Elastic Net, which combines
20
+ L1 and L2 regularization.
21
+
22
+ Parameters
23
+ ----------
24
+ estimator : Optional[BaseEstimator], optional
25
+ The estimator to use, by default None.
26
+ l1_ratio : float, optional
27
+ The ElasticNet mixing parameter, by default 0.5.
28
+ max_iter : int, optional
29
+ Maximum number of iterations, by default 10000.
30
+ **kwargs
31
+ Additional keyword arguments to be passed to the BaseFeatureSelector.
32
+
33
+ Attributes
34
+ ----------
35
+ l1_ratio : float
36
+ The ElasticNet mixing parameter.
37
+ max_iter : int
38
+ Maximum number of iterations.
39
+ model : Optional[ElasticNetCV]
40
+ The fitted ElasticNetCV model.
41
+ feature_importances_ : pd.Series
42
+ The feature importances (coefficients) from the fitted model.
43
+ selected_features_ : List[str]
44
+ The list of selected features.
45
+ """
46
+
47
+ def __init__(
48
+ self,
49
+ estimator: Optional[BaseEstimator] = None,
50
+ l1_ratio: float = 0.5,
51
+ max_iter: int = 10000,
52
+ **kwargs,
53
+ ) -> None:
54
+ # Use LogisticRegression as the default estimator if none is provided
55
+ if estimator is None:
56
+ estimator = LogisticRegression(random_state=42)
57
+ super().__init__(estimator=estimator, **kwargs)
58
+ self.l1_ratio = l1_ratio
59
+ self.max_iter = max_iter
60
+ self.model: Optional[ElasticNetCV] = None
61
+
62
+ def fit(self, X: pd.DataFrame, y: pd.Series) -> "ElasticNetSelector":
63
+ """
64
+ Fit the ElasticNetSelector to the data.
65
+
66
+ Parameters
67
+ ----------
68
+ X : pd.DataFrame
69
+ The input features.
70
+ y : pd.Series
71
+ The target variable.
72
+
73
+ Returns
74
+ -------
75
+ ElasticNetSelector
76
+ The fitted ElasticNetSelector instance.
77
+
78
+ Raises
79
+ ------
80
+ ValueError
81
+ If model fitting fails or coefficients are not available.
82
+ """
83
+ self.model = ElasticNetCV(
84
+ l1_ratio=self.l1_ratio, cv=self.cv, max_iter=self.max_iter
85
+ ).fit(X, y) # type: ignore
86
+ if self.model is not None and hasattr(self.model, "coef_"):
87
+ self.feature_importances_ = pd.Series(
88
+ abs(self.model.coef_), index=X.columns
89
+ )
90
+ if self.feature_importances_ is not None:
91
+ self.selected_features_ = self.feature_importances_[
92
+ self.feature_importances_ > 0
93
+ ].index.tolist()
94
+ else:
95
+ raise ValueError("Feature importances are None after fitting.")
96
+ else:
97
+ raise ValueError("Model fitting failed or coefficients are not available.")
98
+ return self
99
+
100
+ def get_feature_importances(self) -> pd.Series:
101
+ """
102
+ Get the feature importances (coefficients) from the fitted model.
103
+
104
+ Returns
105
+ -------
106
+ pd.Series
107
+ The feature importances.
108
+
109
+ Raises
110
+ ------
111
+ FeatureSelectionError
112
+ If the selector has not been fitted yet.
113
+ """
114
+ if self.feature_importances_ is None:
115
+ raise FeatureSelectionError(
116
+ "Selector has not been fitted yet. Call 'fit' first."
117
+ )
118
+ return self.feature_importances_
119
+
120
+ def _get_score(self, X: pd.DataFrame, y: pd.Series, features: List[int]) -> float:
121
+ """
122
+ Calculate the cross-validation score for a subset of features.
123
+
124
+ Parameters
125
+ ----------
126
+ X : pd.DataFrame
127
+ The input features.
128
+ y : pd.Series
129
+ The target variable.
130
+ features : List[int]
131
+ The indices of features to use.
132
+
133
+ Returns
134
+ -------
135
+ float
136
+ The mean cross-validation score.
137
+
138
+ Raises
139
+ ------
140
+ ValueError
141
+ If the estimator is not set.
142
+ """
143
+ if self.estimator is None:
144
+ raise ValueError("Estimator is not set. Please provide an estimator.")
145
+
146
+ X_subset = X.iloc[:, features]
147
+ scores = cross_val_score(
148
+ self.estimator,
149
+ X_subset,
150
+ y,
151
+ scoring=self.scorer,
152
+ cv=self.cv,
153
+ n_jobs=-1,
154
+ error_score="raise",
155
+ )
156
+ return float(np.mean(scores))
157
+
158
+ def return_feature_importances(self):
159
+ """
160
+ Print the feature importances (coefficients) in descending order of absolute value.
161
+
162
+ Raises
163
+ ------
164
+ FeatureSelectionError
165
+ If feature importances are not available (i.e., fit hasn't been called).
166
+ """
167
+ if self.feature_importances_ is None:
168
+ raise FeatureSelectionError(
169
+ "Feature importances are not available. Call 'fit' first."
170
+ )
171
+
172
+ sorted_importances = sorted(
173
+ zip(self.feature_importances_.index, self.feature_importances_),
174
+ key=lambda x: abs(x[1]), # Sort by absolute value of coefficients
175
+ reverse=True,
176
+ )
177
+
178
+ return sorted_importances
@@ -0,0 +1,329 @@
1
+ from typing import List, Literal, Optional, Union
2
+
3
+ import pandas as pd
4
+ from tqdm import tqdm
5
+
6
+ from classifier_toolkit.feature_selection.base import (
7
+ BaseFeatureSelector,
8
+ FeatureSelectionError,
9
+ )
10
+ from classifier_toolkit.feature_selection.wrapper_methods.rfe import (
11
+ EnsembleFeatureSelector,
12
+ )
13
+ from classifier_toolkit.feature_selection.wrapper_methods.sequential_selection import (
14
+ SequentialSelector,
15
+ )
16
+
17
+
18
+ class MetaSelector(BaseFeatureSelector):
19
+ """
20
+ A meta-selector that combines multiple feature selection methods.
21
+
22
+ This class allows for the combination of multiple feature selection methods
23
+ using different voting strategies.
24
+
25
+ Parameters
26
+ ----------
27
+ methods : Optional[List[BaseFeatureSelector]], optional
28
+ List of feature selection methods to be combined, by default None.
29
+ voting : str, optional
30
+ The voting strategy to use for combining methods, by default "majority".
31
+ Options are "majority", "union", or "intersection".
32
+ n_features_to_select : Optional[int], optional
33
+ The number of features to select, by default None.
34
+ scoring : str, optional
35
+ The scoring metric to use, by default "accuracy".
36
+ cv : int, optional
37
+ The number of cross-validation folds, by default 5.
38
+ verbose : int, optional
39
+ Verbosity level, by default 0.
40
+
41
+ Attributes
42
+ ----------
43
+ methods : List[BaseFeatureSelector]
44
+ The list of feature selection methods.
45
+ voting : str
46
+ The voting strategy used.
47
+ feature_importances_ : pd.Series
48
+ The combined feature importances.
49
+ selected_features_ : List[str]
50
+ The list of selected features.
51
+
52
+ Raises
53
+ ------
54
+ ValueError
55
+ If any of the provided methods is not an instance of BaseFeatureSelector.
56
+ """
57
+
58
+ def __init__(
59
+ self,
60
+ methods: Optional[List[BaseFeatureSelector]] = None,
61
+ voting: Literal["majority", "union", "intersection"] = "majority",
62
+ n_features_to_select: int = 1,
63
+ scoring: Literal[
64
+ "accuracy", "f1", "precision", "recall", "roc_auc"
65
+ ] = "accuracy",
66
+ cv: int = 5,
67
+ verbose: int = 0,
68
+ ):
69
+ super().__init__(
70
+ estimator=None, # MetaSelector doesn't need its own estimator
71
+ n_features_to_select=n_features_to_select,
72
+ scoring=scoring,
73
+ cv=cv,
74
+ verbose=verbose,
75
+ )
76
+ self.methods = methods or [
77
+ EnsembleFeatureSelector(
78
+ estimator="random_forest",
79
+ n_features_to_select=self.n_features_to_select,
80
+ ),
81
+ SequentialSelector(
82
+ method="forward",
83
+ estimator_name="lightgbm",
84
+ estimator_params={"n_estimators": 100},
85
+ n_features_to_select=self.n_features_to_select,
86
+ ),
87
+ ]
88
+ if not all(isinstance(method, BaseFeatureSelector) for method in self.methods):
89
+ raise ValueError("All methods must be instances of BaseFeatureSelector")
90
+ self.voting = voting
91
+
92
+ def fit(self, X: pd.DataFrame, y: pd.Series) -> "MetaSelector":
93
+ """
94
+ Fit the MetaSelector to the data.
95
+
96
+ This method fits all the individual feature selection methods and then
97
+ combines their results based on the specified voting strategy.
98
+
99
+ Parameters
100
+ ----------
101
+ X : pd.DataFrame
102
+ The input features.
103
+ y : pd.Series
104
+ The target variable.
105
+
106
+ Returns
107
+ -------
108
+ MetaSelector
109
+ The fitted MetaSelector instance.
110
+
111
+ Raises
112
+ ------
113
+ ValueError
114
+ If an invalid voting strategy is specified.
115
+ """
116
+ # Create a progress bar
117
+ pbar = tqdm(total=len(self.methods), desc="Fitting methods")
118
+
119
+ for i, method in enumerate(self.methods):
120
+ print(
121
+ f"\nFitting method {i+1}/{len(self.methods)}: {type(method).__name__}"
122
+ )
123
+ method.fit(X, y)
124
+ # Update the progress bar
125
+ pbar.update(1)
126
+ pbar.set_description(f"Fitted {type(method).__name__}")
127
+
128
+ # Close the progress bar
129
+ pbar.close()
130
+
131
+ self._combine_feature_importances()
132
+
133
+ if self.voting == "majority":
134
+ self._majority_voting()
135
+ elif self.voting == "union":
136
+ self._union_voting()
137
+ elif self.voting == "intersection":
138
+ self._intersection_voting()
139
+ else:
140
+ raise ValueError("Voting must be 'majority', 'union', or 'intersection'")
141
+
142
+ return self
143
+
144
+ def _combine_feature_importances(self):
145
+ all_importances = [method.get_feature_importances() for method in self.methods]
146
+ combined_importances = pd.concat(all_importances, axis=1)
147
+
148
+ if self.voting == "majority":
149
+ self.feature_importances_ = combined_importances.mean(axis=1)
150
+ elif self.voting == "union" or self.voting == "intersection":
151
+ self.feature_importances_ = combined_importances.max(axis=1)
152
+
153
+ self.feature_importances_ = self.feature_importances_.sort_values(
154
+ ascending=False
155
+ )
156
+
157
+ def print_feature_importances(self):
158
+ if self.feature_importances_ is None:
159
+ raise FeatureSelectionError(
160
+ "Feature importances are not available. Call 'fit' first."
161
+ )
162
+
163
+ print("Combined Feature Importances (in descending order):")
164
+ for feature, importance in self.feature_importances_.items():
165
+ print(f"('{feature}', {importance:.6f})")
166
+
167
+ def get_feature_importances(self) -> pd.Series:
168
+ """
169
+ Get the combined feature importances.
170
+
171
+ Returns
172
+ -------
173
+ pd.Series
174
+ The combined feature importances.
175
+
176
+ Raises
177
+ ------
178
+ FeatureSelectionError
179
+ If the selector has not been fitted yet.
180
+ """
181
+ if self.feature_importances_ is None:
182
+ raise FeatureSelectionError(
183
+ "Selector has not been fitted yet. Call 'fit' first."
184
+ )
185
+ return self.feature_importances_
186
+
187
+ def _majority_voting(self):
188
+ all_importances = [method.get_feature_importances() for method in self.methods]
189
+ combined_importances = pd.concat(all_importances, axis=1)
190
+ threshold = len(self.methods) / 2
191
+ feature_counts = (combined_importances > 0).sum(axis=1)
192
+ self.selected_features_ = list(feature_counts[feature_counts > threshold].index)
193
+ self.feature_importances_ = combined_importances.loc[
194
+ self.selected_features_
195
+ ].mean(axis=1)
196
+
197
+ def _union_voting(self):
198
+ all_selected = [set(method.selected_features_) for method in self.methods]
199
+ union_features = set.union(*all_selected)
200
+ self.selected_features_ = list(union_features)
201
+ all_importances = [method.get_feature_importances() for method in self.methods]
202
+ combined_importances = pd.concat(all_importances, axis=1)
203
+ self.feature_importances_ = combined_importances.loc[
204
+ self.selected_features_
205
+ ].mean(axis=1)
206
+
207
+ def _intersection_voting(self):
208
+ all_selected = [set(method.selected_features_) for method in self.methods]
209
+ intersection_features = set.intersection(*all_selected)
210
+ self.selected_features_ = list(intersection_features)
211
+ all_importances = [method.get_feature_importances() for method in self.methods]
212
+ combined_importances = pd.concat(all_importances, axis=1)
213
+ self.feature_importances_ = combined_importances.loc[
214
+ self.selected_features_
215
+ ].mean(axis=1)
216
+
217
+ def print_results(self):
218
+ """
219
+ Print the results of the feature selection process.
220
+
221
+ This method prints the results of individual methods, the combined results
222
+ based on voting, and the final feature importances.
223
+ """
224
+ print("Individual method results:")
225
+ for method in self.methods:
226
+ print(f"{type(method).__name__}: {method.selected_features_}")
227
+
228
+ print("\nCombined results based on voting:")
229
+ print(f"Voting method: {self.voting}")
230
+ print(f"Selected features: {self.selected_features_}")
231
+
232
+ print("\nFinal feature importances:")
233
+ for feature, importance in self.feature_importances_.sort_values(
234
+ ascending=False
235
+ ).items():
236
+ print(f"('{feature}', {importance:.6f})")
237
+
238
+ def transform(self, X: pd.DataFrame) -> pd.DataFrame:
239
+ """
240
+ Transform the input data by selecting the chosen features.
241
+
242
+ Parameters
243
+ ----------
244
+ X : pd.DataFrame
245
+ The input features.
246
+
247
+ Returns
248
+ -------
249
+ pd.DataFrame
250
+ The transformed data containing only the selected features.
251
+
252
+ Raises
253
+ ------
254
+ FeatureSelectionError
255
+ If the selector has not been fitted yet.
256
+ """
257
+ if self.selected_features_ is None:
258
+ raise FeatureSelectionError(
259
+ "Selector has not been fitted yet. Call 'fit' first."
260
+ )
261
+ return X[self.selected_features_]
262
+
263
+ def fit_transform(self, X: pd.DataFrame, y: pd.Series) -> pd.DataFrame:
264
+ """
265
+ Fit the MetaSelector to the data and then transform it.
266
+
267
+ Parameters
268
+ ----------
269
+ X : pd.DataFrame
270
+ The input features.
271
+ y : pd.Series
272
+ The target variable.
273
+
274
+ Returns
275
+ -------
276
+ pd.DataFrame
277
+ The transformed data containing only the selected features.
278
+ """
279
+ return self.fit(X, y).transform(X)
280
+
281
+ def get_support(self, indices: bool = False) -> Union[List[bool], List[int]]:
282
+ """
283
+ Get a boolean mask or integer index array indicating the selected features.
284
+
285
+ Parameters
286
+ ----------
287
+ indices : bool, optional
288
+ If True, returns an integer index array, otherwise returns a boolean mask.
289
+
290
+ Returns
291
+ -------
292
+ Union[List[bool], List[int]]
293
+ Boolean mask or integer index array indicating the selected features.
294
+
295
+ Raises
296
+ ------
297
+ FeatureSelectionError
298
+ If the selector has not been fitted yet.
299
+ """
300
+ if self.selected_features_ is None:
301
+ raise FeatureSelectionError(
302
+ "Selector has not been fitted yet. Call 'fit' first."
303
+ )
304
+ assert self.feature_importances_ is not None, ValueError(
305
+ 'Feature importances are not available. Call "fit" first.'
306
+ )
307
+ mask = [
308
+ feature in self.selected_features_
309
+ for feature in self.feature_importances_.index
310
+ ]
311
+ if indices:
312
+ return [i for i, m in enumerate(mask) if m]
313
+ return mask
314
+
315
+ def get_individual_results(self):
316
+ """
317
+ Get the results of individual feature selection methods.
318
+
319
+ Returns
320
+ -------
321
+ dict
322
+ A dictionary containing the selected features for each method.
323
+ """
324
+ return {
325
+ type(method).__name__: method.selected_features_ for method in self.methods
326
+ }
327
+
328
+ def _get_score(self, X: pd.DataFrame, y: pd.Series, features: List[int]) -> float:
329
+ raise NotImplementedError
@@ -0,0 +1,17 @@
1
+ from classifier_toolkit.feature_selection.utils.plottings import (
2
+ plot_feature_importances,
3
+ plot_rfecv_results,
4
+ )
5
+ from classifier_toolkit.feature_selection.utils.scoring import (
6
+ false_positive_rate,
7
+ get_scorer,
8
+ true_positive_rate,
9
+ )
10
+
11
+ __all__ = [
12
+ "plot_feature_importances",
13
+ "plot_rfecv_results",
14
+ "get_scorer",
15
+ "false_positive_rate",
16
+ "true_positive_rate",
17
+ ]
@@ -0,0 +1,65 @@
1
+ import matplotlib.pyplot as plt
2
+ import numpy as np
3
+
4
+ from classifier_toolkit.feature_selection.base import BaseFeatureSelector
5
+
6
+
7
+ def plot_feature_importances(selector: BaseFeatureSelector) -> None:
8
+ importances = selector.get_feature_importances().sort_values(ascending=True)
9
+
10
+ plt.figure(figsize=(10, max(8, len(importances) * 0.3)))
11
+ y_pos = np.arange(len(importances))
12
+ plt.barh(y_pos, np.array(importances.values))
13
+ plt.yticks(y_pos, importances.index.astype(str).tolist())
14
+ plt.xlabel("Importance")
15
+ plt.title("Feature Importances")
16
+ plt.tight_layout()
17
+ plt.show()
18
+
19
+
20
+ def plot_rfecv_results(selector: BaseFeatureSelector) -> None:
21
+ assert hasattr(
22
+ selector, "rfecv"
23
+ ), "This plot is only available for RFECV-based selectors."
24
+ assert (
25
+ selector.rfecv is not None
26
+ ), "RFECV results are not available. Make sure to fit the selector first."
27
+ assert hasattr(
28
+ selector.rfecv, "cv_results_"
29
+ ), "The selector's RFECV doesn't have cv_results_. Make sure to fit the selector first."
30
+
31
+ n_features_total = len(selector.get_feature_importances())
32
+
33
+ grid_scores = selector.rfecv.cv_results_["mean_test_score"]
34
+ std_errors = selector.rfecv.cv_results_["std_test_score"]
35
+
36
+ n_features = np.arange(1, n_features_total + 1)
37
+
38
+ plt.figure(figsize=(12, 6))
39
+ plt.title("Feature Selection using RFECV")
40
+ plt.xlabel("Number of features selected")
41
+ plt.ylabel("Cross-validation score")
42
+ plt.plot(n_features, grid_scores)
43
+ plt.fill_between(
44
+ n_features, grid_scores - std_errors, grid_scores + std_errors, alpha=0.2
45
+ )
46
+ plt.axvline(
47
+ selector.rfecv.n_features_,
48
+ color="red",
49
+ linestyle="--",
50
+ label=f"Optimal number of features ({selector.rfecv.n_features_})",
51
+ )
52
+ plt.legend()
53
+ plt.tight_layout()
54
+ plt.show()
55
+
56
+ # Plot feature importances
57
+ importances = selector.get_feature_importances().sort_values(ascending=False)
58
+ plt.figure(figsize=(12, 8))
59
+ plt.title("Feature Importances")
60
+ importances.plot(kind="bar")
61
+ plt.xlabel("Features")
62
+ plt.ylabel("Importance")
63
+ plt.xticks(rotation=90)
64
+ plt.tight_layout()
65
+ plt.show()
@@ -0,0 +1,86 @@
1
+ from typing import Callable, Dict, Union
2
+
3
+ from sklearn.metrics import (
4
+ accuracy_score,
5
+ confusion_matrix,
6
+ f1_score,
7
+ make_scorer,
8
+ precision_score,
9
+ recall_score,
10
+ roc_auc_score,
11
+ )
12
+
13
+ # Define a dictionary of scoring functions
14
+ SCORING_FUNCTIONS: Dict[str, Callable] = {
15
+ "accuracy": accuracy_score,
16
+ "f1": f1_score,
17
+ "precision": precision_score,
18
+ "recall": recall_score,
19
+ "roc_auc": roc_auc_score,
20
+ # add prauc to the scoring functions, and make it default in the Literal call
21
+ }
22
+
23
+
24
+ def get_scorer(metric: Union[str, Callable]) -> Callable:
25
+ """
26
+ Get a scorer function for the given metric.
27
+
28
+ Args:
29
+ metric (str or callable): The metric to use for scoring.
30
+ If string, must be one of the keys in SCORING_FUNCTIONS.
31
+ If callable, should be a custom scoring function.
32
+
33
+ Returns:
34
+ A scorer function that can be used with scikit-learn's cross-validation and model selection tools.
35
+
36
+ Raises:
37
+ ValueError: If the metric is not recognized.
38
+ """
39
+ if isinstance(metric, str):
40
+ if metric not in SCORING_FUNCTIONS:
41
+ raise ValueError(
42
+ f"Unrecognized metric: {metric}. Available metrics are: {', '.join(SCORING_FUNCTIONS.keys())}"
43
+ )
44
+ return make_scorer(SCORING_FUNCTIONS[metric])
45
+ elif callable(metric):
46
+ return make_scorer(metric)
47
+ else:
48
+ raise ValueError("metric must be either a string or a callable")
49
+
50
+
51
+ def false_positive_rate(y_true: int, y_pred: int) -> float:
52
+ """
53
+ Calculate the false positive rate.
54
+
55
+ Args:
56
+ y_true: True labels
57
+ y_pred: Predicted labels
58
+
59
+ Returns:
60
+ False positive rate
61
+ """
62
+ tn, fp, fn, tp = confusion_matrix(y_true, y_pred).ravel()
63
+ return fp / (fp + tn)
64
+
65
+
66
+ def true_positive_rate(y_true: int, y_pred: int):
67
+ """
68
+ Calculate the true positive rate (also known as recall or sensitivity).
69
+
70
+ Args:
71
+ y_true: True labels
72
+ y_pred: Predicted labels
73
+
74
+ Returns:
75
+ True positive rate
76
+ """
77
+ return recall_score(y_true, y_pred)
78
+
79
+
80
+ # Add FPR and TPR to the scoring functions
81
+ SCORING_FUNCTIONS["fpr"] = false_positive_rate
82
+ SCORING_FUNCTIONS["tpr"] = true_positive_rate
83
+
84
+ # Create scorers for FPR and TPR
85
+ fpr_scorer = make_scorer(false_positive_rate, greater_is_better=False)
86
+ tpr_scorer = make_scorer(true_positive_rate, greater_is_better=True)
@@ -0,0 +1,11 @@
1
+ from classifier_toolkit.feature_selection.wrapper_methods.rfe import (
2
+ EnsembleFeatureSelector,
3
+ )
4
+ from classifier_toolkit.feature_selection.wrapper_methods.sequential_selection import (
5
+ SequentialSelector,
6
+ )
7
+
8
+ __all__ = [
9
+ "SequentialSelector",
10
+ "EnsembleFeatureSelector",
11
+ ]