@@ -106,11 +106,12 @@ class SequentialFeatureSelector(SelectorMixin, MetaEstimatorMixin,
106
106
>>> sfs.transform(X).shape
107
107
(150, 3)
108
108
"""
109
- def __init__ (self , estimator , * , n_features_to_select = None ,
109
+ def __init__ (self , estimator , * , n_features_to_select = None , censored_rate = None ,
110
110
direction = 'forward' , scoring = None , cv = 5 , n_jobs = None ):
111
111
112
112
self .estimator = estimator
113
113
self .n_features_to_select = n_features_to_select
114
+ self .censored_rate = censored_rate
114
115
self .direction = direction
115
116
self .scoring = scoring
116
117
self .cv = cv
@@ -175,18 +176,34 @@ def fit(self, X, y):
175
176
self .n_features_to_select_ if self .direction == 'forward'
176
177
else n_features - self .n_features_to_select_
177
178
)
178
- for _ in range (n_iterations ):
179
- new_feature_idx = self ._get_best_new_feature (cloned_estimator , X ,
180
- y , current_mask )
181
- current_mask [new_feature_idx ] = True
182
179
183
- if self .direction == 'backward' :
184
- current_mask = ~ current_mask
180
+ if self .censored_rate is None :
181
+ for _ in range (n_iterations ):
182
+ new_feature_idx , new_score = self ._get_best_new_feature_score (cloned_estimator , X ,
183
+ y , current_mask )
184
+ current_mask [new_feature_idx ] = True
185
+
186
+ if self .direction == 'backward' :
187
+ current_mask = ~ current_mask
188
+ else :
189
+ old_score = 0
190
+ for _ in range (n_iterations ):
191
+ new_feature_idx , new_score = self ._get_best_new_feature_score (cloned_estimator , X ,
192
+ y , current_mask )
193
+ if new_score < old_score * (1 + self .censored_rate ):
194
+ break
195
+
196
+ old_score = new_score
197
+ current_mask [new_feature_idx ] = True
198
+
199
+ if self .direction == 'backward' :
200
+ current_mask = ~ current_mask
201
+
185
202
self .support_ = current_mask
186
203
187
204
return self
188
205
189
- def _get_best_new_feature (self , estimator , X , y , current_mask ):
206
+ def _get_best_new_feature_score (self , estimator , X , y , current_mask ):
190
207
# Return the best new feature to add to the current_mask, i.e. return
191
208
# the best new feature to add (resp. remove) when doing forward
192
209
# selection (resp. backward selection)
@@ -201,7 +218,9 @@ def _get_best_new_feature(self, estimator, X, y, current_mask):
201
218
scores [feature_idx ] = cross_val_score (
202
219
estimator , X_new , y , cv = self .cv , scoring = self .scoring ,
203
220
n_jobs = self .n_jobs ).mean ()
204
- return max (scores , key = lambda feature_idx : scores [feature_idx ])
221
+
222
+ new_feature_idx = max (scores , key = lambda feature_idx : scores [feature_idx ])
223
+ return new_feature_idx , scores [new_feature_idx ]
205
224
206
225
def _get_support_mask (self ):
207
226
check_is_fitted (self )
0 commit comments