Skip to content

Commit 3869f07

Browse files
authored
Merge branch 'main' into fix/autofselector-ro-bindings
2 parents e653041 + 22e6a0b commit 3869f07

36 files changed

Lines changed: 333 additions & 173 deletions

DESCRIPTION

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ URL: https://mlr3fselect.mlr-org.com,
2424
https://github.com/mlr-org/mlr3fselect
2525
BugReports: https://github.com/mlr-org/mlr3fselect/issues
2626
Depends:
27-
mlr3 (>= 1.0.1),
27+
mlr3 (>= 1.7.1.9000),
2828
R (>= 3.1.0)
2929
Imports:
3030
bbotk (>= 1.9.0),
@@ -46,10 +46,11 @@ Suggests:
4646
processx,
4747
redux,
4848
rpart,
49-
rush (>= 1.0.0),
49+
rush (>= 1.2.1.9000),
5050
testthat (>= 3.0.0)
5151
Remotes:
52-
mlr-org/mlr3
52+
mlr-org/mlr3,
53+
mlr-org/rush
5354
Config/testthat/edition: 3
5455
Config/testthat/parallel: false
5556
Encoding: UTF-8

NEWS.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
# mlr3fselect (development version)
22

33
* fix: `ArchiveAsyncFSelect` pushed results with the removed `rush::Rush$push_results()` method.
4+
* fix: `fs("rfecv")` left the resampling of the objective set to an insample resampling, so subsequent evaluations on the same instance silently resampled in-sample (#187).
5+
* fix: The `mlr3fselect.backup` callback deleted the backup of the previous batch before it wrote the new one, so a crash in between lost the complete run. The benchmark result is now written to a temporary file and renamed afterwards (#188).
6+
* BREAKING CHANGE: The `mlr3fselect.backup` callback requires the `path` argument now. Previously it wrote a `bmr.rds` file into the working directory of the user (#188).
7+
* fix: `as.data.table()` on an `EnsembleFSResult` accepts the documented `benchmark_result` argument now to omit the task, learner and resampling columns (#190).
8+
* fix: The `$print()` methods of `ArchiveBatchFSelect`, `ArchiveAsyncFSelect`, `ArchiveAsyncFSelectFrozen`, `AutoFSelector` and `FSelector` errored with `unused argument` when arguments such as `digits` were passed (#190).
9+
* fix: `fs("rfecv")` had the same label as `fs("rfe")`, so both were indistinguishable in `as.data.table(mlr_fselectors)`. Its manual page also instructed to construct it with `fs("rfe")` (#191).
10+
* fix: `AutoFSelector$train()` did not check the row ids of an instantiated inner resampling for cross-validation and reported a wrong set number for holdout (#197).
411
* fix: The `mlr3fselect.svm_rfe` callback accepted support vector machines without a `type` or `kernel` setting, although only `type = "C-classification"` and `kernel = "linear"` are supported. The callback now also errors on multi-class tasks for which the importance scores are not defined (#173).
512
* fix: The asynchronous feature selection ignored the `always_included` column role. Columns with this role were excluded from the models instead of being added to every feature subset (#175).
613
* fix: The `mlr3fselect.one_se_rule` callback errored on archives with a single evaluation or with missing scores, and wrote the `n_features` column as a list column instead of an integer column (#174).

R/ArchiveAsyncFSelect.R

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,6 @@ ArchiveAsyncFSelect = R6Class(
4747
public = list(
4848
#' @description
4949
#' Creates a new instance of this [R6][R6::R6Class] class.
50-
#'
51-
#' @param check_values (`logical(1)`)\cr
52-
#' If `TRUE` (default), feature subsets are check for validity.
5350
initialize = function(
5451
search_space,
5552
codomain,
@@ -123,7 +120,7 @@ ArchiveAsyncFSelect = R6Class(
123120
#' Printer.
124121
#'
125122
#' @param ... (ignored).
126-
print = function() {
123+
print = function(...) {
127124
cat_cli(cli_h1("{format(self)} with {.val {self$n_evals}} evaluations"))
128125
print(
129126
as.data.table(

R/ArchiveAsyncFSelectFrozen.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ ArchiveAsyncFSelectFrozen = R6Class(
8383
#' Printer.
8484
#'
8585
#' @param ... (ignored).
86-
print = function() {
86+
print = function(...) {
8787
cat_cli(cli_h1("{.cls {class(self)[1L]}} with {.val {self$n_evals}} evaluations"))
8888
print(
8989
as.data.table(

R/ArchiveBatchFSelect.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ ArchiveBatchFSelect = R6Class(
155155
#' Printer.
156156
#'
157157
#' @param ... (ignored).
158-
print = function() {
158+
print = function(...) {
159159
cat_cli(cli_h1("{.cls {class(self)[1]}}"))
160160
print(self$data[, setdiff(names(self$data), "uhash"), with = FALSE], digits = 2)
161161
},

R/AutoFSelector.R

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ AutoFSelector = R6Class(
188188
#' @return Named `numeric()`.
189189
importance = function() {
190190
if ("importance" %nin% self$instance_args$learner$properties) {
191-
stopf("Learner ''%s' cannot calculate important scores.", self$instance_args$learner$id)
191+
stopf("Learner '%s' cannot calculate importance scores.", self$instance_args$learner$id)
192192
}
193193
if (is.null(self$model$learner$model)) {
194194
self$instance_args$learner$importance()
@@ -204,7 +204,7 @@ AutoFSelector = R6Class(
204204
#' @return `character()`.
205205
selected_features = function() {
206206
if ("selected_features" %nin% self$instance_args$learner$properties) {
207-
stopf("Learner ''%s' cannot select features.", self$instance_args$learner$id)
207+
stopf("Learner '%s' cannot select features.", self$instance_args$learner$id)
208208
}
209209
if (is.null(self$model$learner$model)) {
210210
self$instance_args$learner$selected_features()
@@ -243,9 +243,11 @@ AutoFSelector = R6Class(
243243
}
244244
},
245245

246+
#' @description
246247
#' Printer.
248+
#'
247249
#' @param ... (ignored).
248-
print = function() {
250+
print = function(...) {
249251
msg_h = if (is.null(self$label) || is.na(self$label)) "" else paste0(": ", self$label)
250252
model = if (is.null(self$model)) "-" else class(self$model)[1L]
251253

@@ -359,29 +361,22 @@ AutoFSelector = R6Class(
359361
ia$task = task$clone()
360362

361363
# check if task contains all row ids required for instantiated resampling
364+
# `$train_set()` and `$test_set()` are used because the layout of `$instance` differs between resamplings
362365
if (ia$resampling$is_instantiated) {
363-
imap(ia$resampling$instance$train, function(x, i) {
364-
if (!test_subset(x, task$row_ids)) {
365-
stopf(
366-
"Train set %i of inner resampling '%s' contains row ids not present in task '%s': {%s}",
367-
i,
368-
ia$resampling$id,
369-
task$id,
370-
paste(setdiff(x, task$row_ids), collapse = ", ")
371-
)
372-
}
373-
})
374-
375-
imap(ia$resampling$instance$test, function(x, i) {
376-
if (!test_subset(x, task$row_ids)) {
377-
stopf(
378-
"Test set %i of inner resampling '%s' contains row ids not present in task '%s': {%s}",
379-
i,
380-
ia$resampling$id,
381-
task$id,
382-
paste(setdiff(x, task$row_ids), collapse = ", ")
383-
)
384-
}
366+
walk(seq_len(ia$resampling$iters), function(i) {
367+
sets = list(Train = ia$resampling$train_set(i), Test = ia$resampling$test_set(i))
368+
imap(sets, function(row_ids, set_type) {
369+
if (!test_subset(row_ids, task$row_ids)) {
370+
stopf(
371+
"%s set %i of inner resampling '%s' contains row ids not present in task '%s': {%s}",
372+
set_type,
373+
i,
374+
ia$resampling$id,
375+
task$id,
376+
paste(setdiff(row_ids, task$row_ids), collapse = ", ")
377+
)
378+
}
379+
})
385380
})
386381
}
387382

R/EnsembleFSResult.R

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ EnsembleFSResult = R6Class(
214214

215215
# check if `inner_measure` is an `mlr3::Measure`
216216
if (which == "inner" && is.null(private$.inner_measure)) {
217-
stop("No inner_measure was defined during initialization")
217+
error_input("No `inner_measure` was defined during initialization.")
218218
}
219219

220220
private$.active_measure = which
@@ -277,7 +277,7 @@ EnsembleFSResult = R6Class(
277277
}
278278

279279
# Combine results from both objects
280-
private$.result = data.table::rbindlist(list(private$.result, result2), fill = FALSE)
280+
private$.result = rbindlist(list(private$.result, result2), fill = FALSE)
281281

282282
# Merge benchmark results if available in both objects
283283
has_bmr = !is.null(self$benchmark_result)
@@ -523,7 +523,7 @@ EnsembleFSResult = R6Class(
523523
pf = pf[n_features > 0]
524524

525525
# Fit the linear model
526-
form = mlr3misc::formulate(lhs = measure_id, rhs = "n_features_inv")
526+
form = formulate(lhs = measure_id, rhs = "n_features_inv")
527527
model = stats::lm(formula = form, data = pf)
528528

529529
# Predict values using the model to create a smooth curve
@@ -671,8 +671,9 @@ EnsembleFSResult = R6Class(
671671
)
672672

673673
#' @export
674-
as.data.table.EnsembleFSResult = function(x, ...) {
675-
x$result
674+
as.data.table.EnsembleFSResult = function(x, ..., benchmark_result = TRUE) {
675+
assert_flag(benchmark_result)
676+
if (benchmark_result) x$result else copy(get_private(x)$.result)
676677
}
677678

678679
#' @export

R/FSelector.R

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,10 @@ FSelector = R6Class(
7373
#' @description
7474
#' Print method.
7575
#'
76+
#' @param ... (ignored).
77+
#'
7678
#' @return (`character()`).
77-
print = function() {
79+
print = function(...) {
7880
msg_h = if (is.na(self$label)) "" else paste0(": ", self$label)
7981
cat_cli({
8082
cli_h1("{.cls {class(self)[1L]}}{msg_h}")
@@ -96,7 +98,7 @@ FSelector = R6Class(
9698
#' Set of control parameters.
9799
param_set = function(rhs) {
98100
if (!missing(rhs) && !identical(rhs, private$.param_set)) {
99-
stop("$param_set is read-only.")
101+
error_input("`$param_set` is read-only.")
100102
}
101103
private$.param_set
102104
},
@@ -106,7 +108,7 @@ FSelector = R6Class(
106108
#' Must be a subset of [`mlr_reflections$fselect_properties`][mlr3::mlr_reflections].
107109
properties = function(rhs) {
108110
if (!missing(rhs) && !identical(rhs, private$.properties)) {
109-
stop("$properties is read-only.")
111+
error_input("`$properties` is read-only.")
110112
}
111113
private$.properties
112114
},
@@ -116,7 +118,7 @@ FSelector = R6Class(
116118
#' Note that these packages will be loaded via [requireNamespace()], and are not attached.
117119
packages = function(rhs) {
118120
if (!missing(rhs) && !identical(rhs, private$.packages)) {
119-
stop("$packages is read-only.")
121+
error_input("`$packages` is read-only.")
120122
}
121123
private$.packages
122124
},
@@ -126,7 +128,7 @@ FSelector = R6Class(
126128
#' Can be used in tables, plot and text output instead of the ID.
127129
label = function(rhs) {
128130
if (!missing(rhs) && !identical(rhs, private$.param_set)) {
129-
stop("$label is read-only.")
131+
error_input("`$label` is read-only.")
130132
}
131133
private$.label
132134
},
@@ -136,14 +138,14 @@ FSelector = R6Class(
136138
#' The referenced help package can be opened via method `$help()`.
137139
man = function(rhs) {
138140
if (!missing(rhs) && !identical(rhs, private$.man)) {
139-
stop("$man is read-only.")
141+
error_input("`$man` is read-only.")
140142
}
141143
private$.man
142144
}
143145
),
144146

145147
private = list(
146-
.optimize = function(inst) stop("abstract"),
148+
.optimize = function(inst) error_bbotk("`.optimize()` is abstract and must be implemented by the subclass."),
147149

148150
.assign_result = function(inst) {
149151
assert_fselect_instance(inst)

R/FSelectorAsyncFromOptimizerAsync.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ FSelectorAsyncFromOptimizerAsync = R6Class(
5353
#' Set of control parameters.
5454
param_set = function(rhs) {
5555
if (!missing(rhs) && !identical(rhs, private$.optimizer$param_set)) {
56-
stop("$param_set is read-only.")
56+
error_input("`$param_set` is read-only.")
5757
}
5858
private$.optimizer$param_set
5959
}

R/FSelectorBatchGeneticSearch.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ FSelectorBatchGeneticSearch = R6Class(
5656
}
5757
n = inst$objective$domain$length
5858

59-
mlr3misc::invoke(genalg::rbga.bin, size = n, evalFunc = inst$objective_function, .args = pars)
59+
invoke(genalg::rbga.bin, size = n, evalFunc = inst$objective_function, .args = pars)
6060
}
6161
)
6262
)

0 commit comments

Comments
 (0)