@@ -152,6 +152,55 @@ EnsembleFSResult = R6Class(
152152 open_help(self $ man )
153153 },
154154
155+ # ' @description
156+ # ' Removes rows from the ensemble feature selection result where no features were selected.
157+ # '
158+ # ' If a benchmark result is stored, the corresponding resampling iterations are removed as well.
159+ # ' The stability measures are reset and need to be recalculated after this operation.
160+ # '
161+ # ' This method modifies the object by reference.
162+ # ' To preserve the original state, explicitly `$clone()` the object beforehand.
163+ # '
164+ # ' @return
165+ # ' Returns the object itself, but modified **by reference**.
166+ rm_zero_features = function () {
167+ keep = private $ .result $ n_features > 0L
168+ n_removed = sum(! keep )
169+
170+ if (n_removed > 0L ) {
171+ if (! is.null(self $ benchmark_result )) {
172+ # filter the resample results of the benchmark result
173+ resample_results = self $ benchmark_result $ resample_results $ resample_result
174+ filtered_resample_results = list ()
175+ keep_rows = which(keep )
176+ row_start = 0L
177+
178+ for (rr in resample_results ) {
179+ row_end = row_start + rr $ iters
180+ iters = keep_rows [keep_rows > row_start & keep_rows < = row_end ] - row_start
181+ row_start = row_end
182+
183+ if (length(iters )) {
184+ rr $ filter(iters = iters )
185+ filtered_resample_results = c(filtered_resample_results , list (rr ))
186+ }
187+ }
188+
189+ self $ benchmark_result = if (length(filtered_resample_results )) {
190+ do.call(c , filtered_resample_results )
191+ } else {
192+ NULL
193+ }
194+ }
195+ private $ .result = private $ .result [keep ]
196+ private $ .stability_global = NULL
197+ private $ .stability_learner = NULL
198+ lg $ info(sprintf(" %s results with zero selected features have been removed." , n_removed ))
199+ }
200+
201+ invisible (self )
202+ },
203+
155204 # ' @description
156205 # ' Use this function to change the active measure.
157206 # '
@@ -259,8 +308,8 @@ EnsembleFSResult = R6Class(
259308 # ' i.e. it can be used to compare the feature rankings across different methods.
260309 # '
261310 # ' We shuffle the input candidates/features so that we enforce random tie-breaking.
262- # ' Users should set the same `seed` for consistent comparison between the different feature ranking methods
263- # ' and for reproducibility.
311+ # ' Users should set the same `seed` for consistent comparison between the different
312+ # ' feature ranking methods and for reproducibility.
264313 # '
265314 # ' @param method (`character(1)`)\cr
266315 # ' The method to calculate the feature ranking. See [fastVoteR::rank_candidates()]
@@ -269,12 +318,14 @@ EnsembleFSResult = R6Class(
269318 # ' @param use_weights (`logical(1)`)\cr
270319 # ' The default value (`TRUE`) uses weights equal to the performance scores
271320 # ' of each voter/model (or the inverse scores if the measure is minimized).
272- # ' If `FALSE`, we treat all voters as equal and assign them all a weight equal to 1.
321+ # ' Note that the performance scores need to be non-negative for the weights
322+ # ' to be meaningful. If the scores can be negative, it is recommended to set
323+ # ' `use_weights = FALSE`, which treats all voters as equal and assigns them
324+ # ' the same weight equal to 1.
273325 # ' @param committee_size (`integer(1)`)\cr
274- # ' Number of top selected features in the output ranking.
275- # ' This parameter can be used to speed-up methods that build a committee sequentially
276- # ' (`"seq_pav"`), by requesting only the top N selected candidates/features
277- # ' and not the complete feature ranking.
326+ # ' The number of top-ranked features to return.
327+ # ' This can speed up methods that build a committee sequentially (e.g., `"seq_pav"`)
328+ # ' by computing only the top N candidates rather than the full ranking.
278329 # ' @param shuffle_features (`logical(1)`)\cr
279330 # ' Whether to shuffle the task features randomly before computing the ranking.
280331 # ' Shuffling ensures consistent random tie-breaking across methods and prevents
@@ -293,7 +344,6 @@ EnsembleFSResult = R6Class(
293344 # ' where the top feature receives a score of 1 and the lowest-ranked feature receives a score of 0.
294345 # ' This column is always included so that feature ranking methods that output only rankings
295346 # ' have also a feature-wise score.
296- # '
297347 feature_ranking = function (method = " av" , use_weights = TRUE , committee_size = NULL , shuffle_features = TRUE ) {
298348 requireNamespace(" fastVoteR" )
299349
@@ -315,14 +365,16 @@ EnsembleFSResult = R6Class(
315365 }
316366
317367 # get consensus feature ranking
318- res = fastVoteR :: rank_candidates(
319- voters = voters ,
320- candidates = candidates ,
321- weights = weights ,
322- committee_size = committee_size ,
323- method = method ,
324- borda_score = TRUE ,
325- shuffle_candidates = shuffle_features
368+ res = as.data.table(
369+ fastVoteR :: rank_candidates(
370+ voters = voters ,
371+ candidates = candidates ,
372+ weights = weights ,
373+ committee_size = committee_size ,
374+ method = method ,
375+ borda_score = TRUE ,
376+ shuffle_candidates = shuffle_features
377+ )
326378 )
327379
328380 setnames(res , " candidate" , " feature" )
0 commit comments