From d9ac995ca5e87c698a0a433bec7143691c8bbff6 Mon Sep 17 00:00:00 2001 From: Raphael Sonabend Date: Tue, 31 Aug 2021 12:10:19 +0100 Subject: [PATCH 1/6] Fix catboost, closes #100 --- .github/workflows/rcmdcheck.yml | 2 +- .gitignore | 1 + DESCRIPTION | 2 +- NAMESPACE | 84 ------------------- NEWS.md | 6 ++ R/fn_install_catboost.R | 32 +++++++ R/learner_catboost_classif_catboost.R | 53 +++++------- R/learner_catboost_regr_catboost.R | 27 +++--- ...test_paramtest_catboost_classif_catboost.R | 5 +- .../test_paramtest_catboost_regr_catboost.R | 5 +- .../testthat/test_catboost_classif_catboost.R | 7 +- tests/testthat/test_catboost_regr_catboost.R | 8 +- 12 files changed, 86 insertions(+), 146 deletions(-) create mode 100644 R/fn_install_catboost.R diff --git a/.github/workflows/rcmdcheck.yml b/.github/workflows/rcmdcheck.yml index 840036637..cd4e5cc28 100644 --- a/.github/workflows/rcmdcheck.yml +++ b/.github/workflows/rcmdcheck.yml @@ -65,7 +65,7 @@ jobs: - name: Install CatBoost run: | install.packages("remotes") - remotes::install_url('https://github.com/catboost/catboost/releases/download/v0.24.1/catboost-R-Linux-0.24.1.tgz', INSTALL_opts = c("--no-multiarch")) + remotes::install_url('https://github.com/catboost/catboost/releases/download/v0.26.1/catboost-R-Linux-0.26.1.tgz', INSTALL_opts = c("--no-multiarch")) shell: Rscript {0} - name: Install Python diff --git a/.gitignore b/.gitignore index 50c124e29..74ff8cade 100644 --- a/.gitignore +++ b/.gitignore @@ -125,3 +125,4 @@ docs/ .ccache/ .vscode/settings.json +catboost_info/ diff --git a/DESCRIPTION b/DESCRIPTION index e19e8fe9a..4181245d1 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: mlr3extralearners Title: Extra Learners For mlr3 -Version: 0.5.2 +Version: 0.5.3 Authors@R: c(person(given = "Raphael", family = "Sonabend", diff --git a/NAMESPACE b/NAMESPACE index a0d955304..f414569db 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,89 +1,5 @@ # Generated by roxygen2: do not edit by hand -export(LearnerClassifAdaBoostM1) -export(LearnerClassifBart) -export(LearnerClassifC50) -export(LearnerClassifCForest) -export(LearnerClassifCTree) -export(LearnerClassifCatboost) -export(LearnerClassifEarth) -export(LearnerClassifExtraTrees) -export(LearnerClassifFNN) -export(LearnerClassifGAMBoost) -export(LearnerClassifGBM) -export(LearnerClassifGLMBoost) -export(LearnerClassifGam) -export(LearnerClassifIBk) -export(LearnerClassifJ48) -export(LearnerClassifJRip) -export(LearnerClassifKSVM) -export(LearnerClassifLMT) -export(LearnerClassifLiblineaR) -export(LearnerClassifLightGBM) -export(LearnerClassifMob) -export(LearnerClassifOneR) -export(LearnerClassifPART) -export(LearnerClassifRandomForest) -export(LearnerClassifRandomForestSRC) -export(LearnerDensKDEkd) -export(LearnerDensKDEks) -export(LearnerDensLocfit) -export(LearnerDensLogspline) -export(LearnerDensMixed) -export(LearnerDensNonparametric) -export(LearnerDensPenalized) -export(LearnerDensPlugin) -export(LearnerDensSpline) -export(LearnerRegrBart) -export(LearnerRegrCForest) -export(LearnerRegrCTree) -export(LearnerRegrCatboost) -export(LearnerRegrCubist) -export(LearnerRegrEarth) -export(LearnerRegrExtraTrees) -export(LearnerRegrFNN) -export(LearnerRegrGAMBoost) -export(LearnerRegrGBM) -export(LearnerRegrGLMBoost) -export(LearnerRegrGam) -export(LearnerRegrGlm) -export(LearnerRegrIBk) -export(LearnerRegrKSVM) -export(LearnerRegrLiblineaR) -export(LearnerRegrLightGBM) -export(LearnerRegrM5Rules) -export(LearnerRegrMars) -export(LearnerRegrMob) -export(LearnerRegrRandomForest) -export(LearnerRegrRandomForestSRC) -export(LearnerSurvAkritas) -export(LearnerSurvBlackBoost) -export(LearnerSurvCForest) -export(LearnerSurvCTree) -export(LearnerSurvCVCoxboost) -export(LearnerSurvCoxboost) -export(LearnerSurvCoxtime) -export(LearnerSurvDNNSurv) -export(LearnerSurvDeephit) -export(LearnerSurvDeepsurv) -export(LearnerSurvFlexible) -export(LearnerSurvGAMBoost) -export(LearnerSurvGBM) -export(LearnerSurvGLMBoost) -export(LearnerSurvLogisticHazard) -export(LearnerSurvMBoost) -export(LearnerSurvNelson) -export(LearnerSurvObliqueRSF) -export(LearnerSurvPCHazard) -export(LearnerSurvParametric) -export(LearnerSurvPenalized) -export(LearnerSurvRandomForestSRC) -export(LearnerSurvSVM) -export(create_learner) -export(install_learners) -export(list_mlr3learners) -export(lrn) -export(lrns) import(checkmate) import(mlr3misc) import(paradox) diff --git a/NEWS.md b/NEWS.md index c57fb7e87..32ea5f7b6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,9 @@ +# mlr3extralearners 0.5.3 + +* Fixed bugs in catboost for classification +* Removed factor feature types from catboost +* Added `install_catboost` to make installation from CatBoost simpler + # mlr3extralearners 0.5.2 * Fixed learner tests diff --git a/R/fn_install_catboost.R b/R/fn_install_catboost.R new file mode 100644 index 000000000..846d6ce1a --- /dev/null +++ b/R/fn_install_catboost.R @@ -0,0 +1,32 @@ +#' Helper function to install catboost +#' @title Install catboost +#' @param version `(character(1))` +#' Version to install, if `NULL` installs latest +#' @param os `(character(1))` \cr +#' Operating system to install on, if `NULL` automatically detected +#' @param INSTALL_opts `(character())` \cr +#' Passed to [devtools::install_url] +#' @param ... `ANY` \cr +#' Other arguments passed to [devtools::install_url] +#' @export +install_catboost <- function(version = NULL, os = NULL, + INSTALL_opts = c("--no-multiarch", + "--no-test-load"), ...) { + if (is.null(version)) { + version <- jsonlite::fromJSON( + "https://api.github.com/repos/catboost/catboost/releases" + )$tag_name[1] + } + + version <- gsub("v", "", version) + + if (is.null(os)) { + os <- as.character(Sys.info()["sysname"]) + } + + url <- sprintf( + "https://github.com/catboost/catboost/releases/download/v%s/catboost-R-%s-%s.tgz", + version, os, version) + + devtools::install_url(url, INSTALL_opts = INSTALL_opts, ...) +} diff --git a/R/learner_catboost_classif_catboost.R b/R/learner_catboost_classif_catboost.R index 507480364..548f6b1ff 100644 --- a/R/learner_catboost_classif_catboost.R +++ b/R/learner_catboost_classif_catboost.R @@ -6,6 +6,10 @@ #' @templateVar id classif.catboost #' @templateVar caller catboost.train #' +#' @section Installation: +#' The easiest way to install CatBoost is with the helper function +#' [install_catboost]. +#' #' @section Custom mlr3 defaults: #' - `logging_level`: #' - Actual default: "Verbose" @@ -155,7 +159,7 @@ LearnerClassifCatboost = R6Class("LearnerClassifCatboost", super$initialize( id = "classif.catboost", packages = "catboost", - feature_types = c("logical", "integer", "numeric", "factor", "ordered"), + feature_types = c("numeric", "integer", "factor", "ordered"), predict_types = c("response", "prob"), param_set = ps, properties = c( @@ -182,18 +186,14 @@ LearnerClassifCatboost = R6Class("LearnerClassifCatboost", private = list( .train = function(task) { - # integer/logical features must be converted to numerics explicitly - data = task$data(cols = task$feature_names) - to_numerics = task$feature_types$id[task$feature_types$type %in% - c("integer", "logical")] - if (length(to_numerics)) { - data[, (to_numerics) := lapply(.SD, as.numeric), .SDcols = to_numerics] + if (packageVersion('catboost') < '0.21') { + stop('catboost v0.21 or greater is required, update with install_catboost') } # target is encoded as integer values from 0 # if binary, the positive class is 1 - is_binary = (length(task$class_names) == 2L) + is_binary = length(task$class_names) == 2L label = if (is_binary) { ifelse(task$data(cols = task$target_names)[[1L]] == task$positive, yes = 1L, @@ -204,7 +204,7 @@ LearnerClassifCatboost = R6Class("LearnerClassifCatboost", # data must be a dataframe learn_pool = mlr3misc::invoke(catboost::catboost.load_pool, - data = data.table::setDF(data), + data = task$data(cols = task$feature_names), label = label, weight = task$weights$weight, thread_count = self$param_set$values$thread_count) @@ -218,30 +218,17 @@ LearnerClassifCatboost = R6Class("LearnerClassifCatboost", } pars$loss_function_twoclass = NULL pars$loss_function_multiclass = NULL - - mlr3misc::invoke(catboost::catboost.train, - learn_pool = learn_pool, - test_pool = NULL, - params = pars) +browser() + catboost::catboost.train(learn_pool, NULL, pars) }, .predict = function(task) { - # integer/logical features must be converted to numerics explicitly - - data = task$data(cols = task$feature_names) - to_numerics = task$feature_types$id[task$feature_types$type %in% - c("integer", "logical")] - if (length(to_numerics)) { - data[, (to_numerics) := lapply(.SD, as.numeric), .SDcols = to_numerics] - } - # target was encoded as integer values based on the train_task - # to later revert this, again use the train_task - is_binary = (length(self$state$train_task$class_names) == 2L) + is_binary = (length(task$class_names) == 2L) # data must be a dataframe pool = mlr3misc::invoke(catboost::catboost.load_pool, - data = data.table::setDF(data), + data = task$data(cols = task$feature_names), thread_count = self$param_set$values$thread_count) prediction_type = if (self$predict_type == "response") { @@ -257,20 +244,20 @@ LearnerClassifCatboost = R6Class("LearnerClassifCatboost", if (self$predict_type == "response") { response = if (is_binary) { - ifelse(preds == 1L, - yes = self$state$train_task$positive, - no = setdiff( - self$state$train_task$class_names, - self$state$train_task$positive)) + ifelse(preds == 1L, yes = task$positive, no = task$negative) } else { - self$state$train_task$class_names[preds + 1L] + task$class_names[preds + 1L] } list(response = response) } else { + if (is_binary && is.null(dim(preds))) { preds = matrix(c(preds, 1 - preds), ncol = 2L, nrow = length(preds)) + colnames(preds) = c(task$positive, task$negative) + } else { + colnames(preds) = self$state$task$class_names } - colnames(preds) = self$state$train_task$class_names + list(prob = preds) } } diff --git a/R/learner_catboost_regr_catboost.R b/R/learner_catboost_regr_catboost.R index 3b159e667..1b5534536 100644 --- a/R/learner_catboost_regr_catboost.R +++ b/R/learner_catboost_regr_catboost.R @@ -6,6 +6,10 @@ #' @templateVar id regr.catboost #' @templateVar caller catboost.train #' +#' @section Installation: +#' The easiest way to install CatBoost is with the helper function +#' [install_catboost]. +#' #' @section Custom mlr3 defaults: #' - `logging_level`: #' - Actual default: "Verbose" @@ -149,7 +153,7 @@ LearnerRegrCatboost = R6Class("LearnerRegrCatboost", super$initialize( id = "regr.catboost", packages = "catboost", - feature_types = c("logical", "integer", "numeric", "factor", "ordered"), + feature_types = c("numeric", "integer", "factor", "ordered"), predict_types = "response", param_set = ps, properties = c( @@ -176,17 +180,14 @@ LearnerRegrCatboost = R6Class("LearnerRegrCatboost", private = list( .train = function(task) { - # integer/logical features must be converted to numerics explicitly - data = task$data(cols = task$feature_names) - to_numerics = task$feature_types$id[task$feature_types$type %in% - c("integer", "logical")] - if (length(to_numerics)) { - data[, (to_numerics) := lapply(.SD, as.numeric), .SDcols = to_numerics] + + if (packageVersion('catboost') < '0.21') { + stop('catboost v0.21 or greater is required, update with install_catboost') } # data must be a dataframe learn_pool = mlr3misc::invoke(catboost::catboost.load_pool, - data = data.table::setDF(data), + data = task$data(cols = task$feature_names), label = task$data(cols = task$target_names)[[1L]], weight = task$weights$weight, thread_count = self$param_set$values$thread_count) @@ -198,18 +199,10 @@ LearnerRegrCatboost = R6Class("LearnerRegrCatboost", }, .predict = function(task) { - # integer/logical features must be converted to numerics explicitly - - data = task$data(cols = task$feature_names) - to_numerics = task$feature_types$id[task$feature_types$type %in% - c("integer", "logical")] - if (length(to_numerics)) { - data[, (to_numerics) := lapply(.SD, as.numeric), .SDcols = to_numerics] - } # data must be a dataframe pool = mlr3misc::invoke(catboost::catboost.load_pool, - data = data.table::setDF(data), + data = task$data(cols = task$feature_names), thread_count = self$param_set$values$thread_count) preds = mlr3misc::invoke(catboost::catboost.predict, diff --git a/inst/paramtest/test_paramtest_catboost_classif_catboost.R b/inst/paramtest/test_paramtest_catboost_classif_catboost.R index e43b4ffbd..5f5d73a05 100644 --- a/inst/paramtest/test_paramtest_catboost_classif_catboost.R +++ b/inst/paramtest/test_paramtest_catboost_classif_catboost.R @@ -1,6 +1,7 @@ library(mlr3extralearners) -remotes::install_url('https://github.com/catboost/catboost/releases/download/v0.24.1/catboost-R-Linux-0.24.1.tgz', # nolint - INSTALL_opts = c("--no-multiarch")) +if (!requireNamespace("catboost", quietly = TRUE)) { + install_catboost() +} test_that("classif.catboost_catboost.train", { learner = lrn("classif.catboost") diff --git a/inst/paramtest/test_paramtest_catboost_regr_catboost.R b/inst/paramtest/test_paramtest_catboost_regr_catboost.R index 0c8edcd1f..c276ff242 100644 --- a/inst/paramtest/test_paramtest_catboost_regr_catboost.R +++ b/inst/paramtest/test_paramtest_catboost_regr_catboost.R @@ -1,6 +1,7 @@ library(mlr3extralearners) -remotes::install_url('https://github.com/catboost/catboost/releases/download/v0.24.1/catboost-R-Linux-0.24.1.tgz', # nolint - INSTALL_opts = c("--no-multiarch")) +if (!requireNamespace("catboost", quietly = TRUE)) { + install_catboost() +} test_that("regr.catboost_catboost.train", { diff --git a/tests/testthat/test_catboost_classif_catboost.R b/tests/testthat/test_catboost_classif_catboost.R index 8b73dcfa7..f26d43f63 100644 --- a/tests/testthat/test_catboost_classif_catboost.R +++ b/tests/testthat/test_catboost_classif_catboost.R @@ -1,8 +1,9 @@ -remotes::install_url('https://github.com/catboost/catboost/releases/download/v0.24.1/catboost-R-Linux-0.24.1.tgz', # nolint - INSTALL_opts = c("--no-multiarch")) +if (!requireNamespace("catboost", quietly = TRUE)) { + install_catboost() +} test_that("autotest", { - learner = LearnerClassifCatboost$new() + learner = lrn("classif.catboost", iterations = 1) expect_learner(learner) result = run_autotest(learner) expect_true(result, info = result$error) diff --git a/tests/testthat/test_catboost_regr_catboost.R b/tests/testthat/test_catboost_regr_catboost.R index e23328c4e..8dce43c58 100644 --- a/tests/testthat/test_catboost_regr_catboost.R +++ b/tests/testthat/test_catboost_regr_catboost.R @@ -1,8 +1,10 @@ -remotes::install_url('https://github.com/catboost/catboost/releases/download/v0.24.1/catboost-R-Linux-0.24.1.tgz', # nolint - INSTALL_opts = c("--no-multiarch")) +if (!requireNamespace("catboost", quietly = TRUE)) { + install_catboost() +} + test_that("autotest", { - learner = LearnerRegrCatboost$new() + learner = lrn("regr.catboost", iterations = 1) expect_learner(learner) result = run_autotest(learner) expect_true(result, info = result$error) From 537d446158e3f2c6c866d762a77cbd654c4a1e8a Mon Sep 17 00:00:00 2001 From: Raphael Sonabend Date: Tue, 31 Aug 2021 12:26:53 +0100 Subject: [PATCH 2/6] fix regr catboost --- R/learner_catboost_classif_catboost.R | 8 ++++---- R/learner_catboost_regr_catboost.R | 7 ++++--- tests/testthat/test_catboost_classif_catboost.R | 2 +- tests/testthat/test_catboost_regr_catboost.R | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/R/learner_catboost_classif_catboost.R b/R/learner_catboost_classif_catboost.R index 548f6b1ff..30e89ba1b 100644 --- a/R/learner_catboost_classif_catboost.R +++ b/R/learner_catboost_classif_catboost.R @@ -159,7 +159,7 @@ LearnerClassifCatboost = R6Class("LearnerClassifCatboost", super$initialize( id = "classif.catboost", packages = "catboost", - feature_types = c("numeric", "integer", "factor", "ordered"), + feature_types = c("numeric", "factor", "ordered"), predict_types = c("response", "prob"), param_set = ps, properties = c( @@ -218,7 +218,7 @@ LearnerClassifCatboost = R6Class("LearnerClassifCatboost", } pars$loss_function_twoclass = NULL pars$loss_function_multiclass = NULL -browser() + catboost::catboost.train(learn_pool, NULL, pars) }, @@ -248,14 +248,14 @@ browser() } else { task$class_names[preds + 1L] } - list(response = response) + list(response = as.character(unname(response))) } else { if (is_binary && is.null(dim(preds))) { preds = matrix(c(preds, 1 - preds), ncol = 2L, nrow = length(preds)) colnames(preds) = c(task$positive, task$negative) } else { - colnames(preds) = self$state$task$class_names + colnames(preds) = self$state$train_task$class_names } list(prob = preds) diff --git a/R/learner_catboost_regr_catboost.R b/R/learner_catboost_regr_catboost.R index 1b5534536..6c2a25cbc 100644 --- a/R/learner_catboost_regr_catboost.R +++ b/R/learner_catboost_regr_catboost.R @@ -153,7 +153,7 @@ LearnerRegrCatboost = R6Class("LearnerRegrCatboost", super$initialize( id = "regr.catboost", packages = "catboost", - feature_types = c("numeric", "integer", "factor", "ordered"), + feature_types = c("numeric", "factor", "ordered"), predict_types = "response", param_set = ps, properties = c( @@ -185,6 +185,8 @@ LearnerRegrCatboost = R6Class("LearnerRegrCatboost", stop('catboost v0.21 or greater is required, update with install_catboost') } + self$state$feature_names = task$feature_names + # data must be a dataframe learn_pool = mlr3misc::invoke(catboost::catboost.load_pool, data = task$data(cols = task$feature_names), @@ -200,9 +202,8 @@ LearnerRegrCatboost = R6Class("LearnerRegrCatboost", .predict = function(task) { - # data must be a dataframe pool = mlr3misc::invoke(catboost::catboost.load_pool, - data = task$data(cols = task$feature_names), + data = task$data(cols = self$state$feature_names), thread_count = self$param_set$values$thread_count) preds = mlr3misc::invoke(catboost::catboost.predict, diff --git a/tests/testthat/test_catboost_classif_catboost.R b/tests/testthat/test_catboost_classif_catboost.R index f26d43f63..1e38d0932 100644 --- a/tests/testthat/test_catboost_classif_catboost.R +++ b/tests/testthat/test_catboost_classif_catboost.R @@ -3,7 +3,7 @@ if (!requireNamespace("catboost", quietly = TRUE)) { } test_that("autotest", { - learner = lrn("classif.catboost", iterations = 1) + learner = lrn("classif.catboost", iterations = 10) expect_learner(learner) result = run_autotest(learner) expect_true(result, info = result$error) diff --git a/tests/testthat/test_catboost_regr_catboost.R b/tests/testthat/test_catboost_regr_catboost.R index 8dce43c58..df4864711 100644 --- a/tests/testthat/test_catboost_regr_catboost.R +++ b/tests/testthat/test_catboost_regr_catboost.R @@ -4,7 +4,7 @@ if (!requireNamespace("catboost", quietly = TRUE)) { test_that("autotest", { - learner = lrn("regr.catboost", iterations = 1) + learner = lrn("regr.catboost", iterations = 10) expect_learner(learner) result = run_autotest(learner) expect_true(result, info = result$error) From eded69d1af909d94ff6f87bcd771c137002415e3 Mon Sep 17 00:00:00 2001 From: Raphael Sonabend Date: Tue, 31 Aug 2021 12:48:53 +0100 Subject: [PATCH 3/6] fix install in tests --- .github/workflows/rcmdcheck.yml | 2 +- NAMESPACE | 85 +++++++++++++++++++ NEWS.md | 2 +- R/fn_install_catboost.R | 13 +++ R/learner_catboost_classif_catboost.R | 6 +- R/learner_catboost_regr_catboost.R | 6 +- ...test_paramtest_catboost_classif_catboost.R | 2 +- .../test_paramtest_catboost_regr_catboost.R | 2 +- man/install_catboost.Rd | 34 ++++++++ man/mlr_learners_classif.catboost.Rd | 12 ++- man/mlr_learners_regr.catboost.Rd | 12 ++- .../testthat/test_catboost_classif_catboost.R | 2 +- tests/testthat/test_catboost_regr_catboost.R | 2 +- 13 files changed, 162 insertions(+), 18 deletions(-) create mode 100644 man/install_catboost.Rd diff --git a/.github/workflows/rcmdcheck.yml b/.github/workflows/rcmdcheck.yml index cd4e5cc28..7d3fef17a 100644 --- a/.github/workflows/rcmdcheck.yml +++ b/.github/workflows/rcmdcheck.yml @@ -62,7 +62,7 @@ jobs: pak::pkg_install("rcmdcheck") shell: Rscript {0} - - name: Install CatBoost + - name: Install catboost run: | install.packages("remotes") remotes::install_url('https://github.com/catboost/catboost/releases/download/v0.26.1/catboost-R-Linux-0.26.1.tgz', INSTALL_opts = c("--no-multiarch")) diff --git a/NAMESPACE b/NAMESPACE index f414569db..c1197a084 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,5 +1,90 @@ # Generated by roxygen2: do not edit by hand +export(LearnerClassifAdaBoostM1) +export(LearnerClassifBart) +export(LearnerClassifC50) +export(LearnerClassifCForest) +export(LearnerClassifCTree) +export(LearnerClassifCatboost) +export(LearnerClassifEarth) +export(LearnerClassifExtraTrees) +export(LearnerClassifFNN) +export(LearnerClassifGAMBoost) +export(LearnerClassifGBM) +export(LearnerClassifGLMBoost) +export(LearnerClassifGam) +export(LearnerClassifIBk) +export(LearnerClassifJ48) +export(LearnerClassifJRip) +export(LearnerClassifKSVM) +export(LearnerClassifLMT) +export(LearnerClassifLiblineaR) +export(LearnerClassifLightGBM) +export(LearnerClassifMob) +export(LearnerClassifOneR) +export(LearnerClassifPART) +export(LearnerClassifRandomForest) +export(LearnerClassifRandomForestSRC) +export(LearnerDensKDEkd) +export(LearnerDensKDEks) +export(LearnerDensLocfit) +export(LearnerDensLogspline) +export(LearnerDensMixed) +export(LearnerDensNonparametric) +export(LearnerDensPenalized) +export(LearnerDensPlugin) +export(LearnerDensSpline) +export(LearnerRegrBart) +export(LearnerRegrCForest) +export(LearnerRegrCTree) +export(LearnerRegrCatboost) +export(LearnerRegrCubist) +export(LearnerRegrEarth) +export(LearnerRegrExtraTrees) +export(LearnerRegrFNN) +export(LearnerRegrGAMBoost) +export(LearnerRegrGBM) +export(LearnerRegrGLMBoost) +export(LearnerRegrGam) +export(LearnerRegrGlm) +export(LearnerRegrIBk) +export(LearnerRegrKSVM) +export(LearnerRegrLiblineaR) +export(LearnerRegrLightGBM) +export(LearnerRegrM5Rules) +export(LearnerRegrMars) +export(LearnerRegrMob) +export(LearnerRegrRandomForest) +export(LearnerRegrRandomForestSRC) +export(LearnerSurvAkritas) +export(LearnerSurvBlackBoost) +export(LearnerSurvCForest) +export(LearnerSurvCTree) +export(LearnerSurvCVCoxboost) +export(LearnerSurvCoxboost) +export(LearnerSurvCoxtime) +export(LearnerSurvDNNSurv) +export(LearnerSurvDeephit) +export(LearnerSurvDeepsurv) +export(LearnerSurvFlexible) +export(LearnerSurvGAMBoost) +export(LearnerSurvGBM) +export(LearnerSurvGLMBoost) +export(LearnerSurvLogisticHazard) +export(LearnerSurvMBoost) +export(LearnerSurvNelson) +export(LearnerSurvObliqueRSF) +export(LearnerSurvPCHazard) +export(LearnerSurvParametric) +export(LearnerSurvPenalized) +export(LearnerSurvRandomForestSRC) +export(LearnerSurvSVM) +export(create_learner) +export(install_catboost) +export(install_learners) +export(list_mlr3learners) +export(lrn) +export(lrns) import(checkmate) import(mlr3misc) import(paradox) diff --git a/NEWS.md b/NEWS.md index 32ea5f7b6..46c68a5dd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,7 +2,7 @@ * Fixed bugs in catboost for classification * Removed factor feature types from catboost -* Added `install_catboost` to make installation from CatBoost simpler +* Added `install_catboost` to make installation from catboost simpler # mlr3extralearners 0.5.2 diff --git a/R/fn_install_catboost.R b/R/fn_install_catboost.R index 846d6ce1a..052860c7f 100644 --- a/R/fn_install_catboost.R +++ b/R/fn_install_catboost.R @@ -4,15 +4,28 @@ #' Version to install, if `NULL` installs latest #' @param os `(character(1))` \cr #' Operating system to install on, if `NULL` automatically detected +#' @param install_required `(logical(1))` \cr +#' If `TRUE` (default) and version is `NULL` then installs {curl} and +#' {jsonlite} if not already installed. #' @param INSTALL_opts `(character())` \cr #' Passed to [devtools::install_url] #' @param ... `ANY` \cr #' Other arguments passed to [devtools::install_url] #' @export install_catboost <- function(version = NULL, os = NULL, + install_required = TRUE, INSTALL_opts = c("--no-multiarch", "--no-test-load"), ...) { + if (is.null(version)) { + if (requireNamespace("jsonlite", quietly = TRUE) && install_required) { + utils::install.packages("jsonlite", repos = "https://cloud.r-project.org") + } + + if (requireNamespace("curl", quietly = TRUE) && install_required) { + utils::install.packages("curl", repos = "https://cloud.r-project.org") + } + version <- jsonlite::fromJSON( "https://api.github.com/repos/catboost/catboost/releases" )$tag_name[1] diff --git a/R/learner_catboost_classif_catboost.R b/R/learner_catboost_classif_catboost.R index 30e89ba1b..37e559ae6 100644 --- a/R/learner_catboost_classif_catboost.R +++ b/R/learner_catboost_classif_catboost.R @@ -7,7 +7,7 @@ #' @templateVar caller catboost.train #' #' @section Installation: -#' The easiest way to install CatBoost is with the helper function +#' The easiest way to install catboost is with the helper function #' [install_catboost]. #' #' @section Custom mlr3 defaults: @@ -29,11 +29,11 @@ #' - Reason for change: consistent with other mlr3 learners #' #' @references -#' CatBoost: unbiased boosting with categorical features. +#' catboost: unbiased boosting with categorical features. #' Liudmila Prokhorenkova, Gleb Guse, Aleksandr Vorobev, Anna Veronika Dorogush and Andrey Gulin. #' 2017. https://arxiv.org/abs/1706.09516. #' -#' CatBoost: gradient boosting with categorical features support. +#' catboost: gradient boosting with categorical features support. #' Anna Veronika Dorogush, Vasily Ershov and Andrey Gulin. #' 2018. https://arxiv.org/abs/1810.11363. #' diff --git a/R/learner_catboost_regr_catboost.R b/R/learner_catboost_regr_catboost.R index 6c2a25cbc..56b278ad6 100644 --- a/R/learner_catboost_regr_catboost.R +++ b/R/learner_catboost_regr_catboost.R @@ -7,7 +7,7 @@ #' @templateVar caller catboost.train #' #' @section Installation: -#' The easiest way to install CatBoost is with the helper function +#' The easiest way to install catboost is with the helper function #' [install_catboost]. #' #' @section Custom mlr3 defaults: @@ -29,11 +29,11 @@ #' - Reason for change: consistent with other mlr3 learners #' #' @references -#' CatBoost: unbiased boosting with categorical features. +#' catboost: unbiased boosting with categorical features. #' Liudmila Prokhorenkova, Gleb Guse, Aleksandr Vorobev, Anna Veronika Dorogush and Andrey Gulin. #' 2017. https://arxiv.org/abs/1706.09516. #' -#' CatBoost: gradient boosting with categorical features support. +#' catboost: gradient boosting with categorical features support. #' Anna Veronika Dorogush, Vasily Ershov and Andrey Gulin. #' 2018. https://arxiv.org/abs/1810.11363. #' diff --git a/inst/paramtest/test_paramtest_catboost_classif_catboost.R b/inst/paramtest/test_paramtest_catboost_classif_catboost.R index 5f5d73a05..0b34b12eb 100644 --- a/inst/paramtest/test_paramtest_catboost_classif_catboost.R +++ b/inst/paramtest/test_paramtest_catboost_classif_catboost.R @@ -1,6 +1,6 @@ library(mlr3extralearners) if (!requireNamespace("catboost", quietly = TRUE)) { - install_catboost() + install_catboost("0.26.1") } test_that("classif.catboost_catboost.train", { diff --git a/inst/paramtest/test_paramtest_catboost_regr_catboost.R b/inst/paramtest/test_paramtest_catboost_regr_catboost.R index c276ff242..4a94e59ef 100644 --- a/inst/paramtest/test_paramtest_catboost_regr_catboost.R +++ b/inst/paramtest/test_paramtest_catboost_regr_catboost.R @@ -1,6 +1,6 @@ library(mlr3extralearners) if (!requireNamespace("catboost", quietly = TRUE)) { - install_catboost() + install_catboost("0.26.1") } diff --git a/man/install_catboost.Rd b/man/install_catboost.Rd new file mode 100644 index 000000000..8bfabc837 --- /dev/null +++ b/man/install_catboost.Rd @@ -0,0 +1,34 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/fn_install_catboost.R +\name{install_catboost} +\alias{install_catboost} +\title{Install catboost} +\usage{ +install_catboost( + version = NULL, + os = NULL, + install_required = TRUE, + INSTALL_opts = c("--no-multiarch", "--no-test-load"), + ... +) +} +\arguments{ +\item{version}{\code{(character(1))} +Version to install, if \code{NULL} installs latest} + +\item{os}{\code{(character(1))} \cr +Operating system to install on, if \code{NULL} automatically detected} + +\item{install_required}{\code{(logical(1))} \cr +If \code{TRUE} (default) and version is \code{NULL} then installs {curl} and +{jsonlite} if not already installed.} + +\item{INSTALL_opts}{\code{(character())} \cr +Passed to \link[devtools:remote-reexports]{devtools::install_url}} + +\item{...}{\code{ANY} \cr +Other arguments passed to \link[devtools:remote-reexports]{devtools::install_url}} +} +\description{ +Helper function to install catboost +} diff --git a/man/mlr_learners_classif.catboost.Rd b/man/mlr_learners_classif.catboost.Rd index 64ca3afa2..74d962da5 100644 --- a/man/mlr_learners_classif.catboost.Rd +++ b/man/mlr_learners_classif.catboost.Rd @@ -20,11 +20,17 @@ lrn("classif.catboost") \itemize{ \item Packages: catboost \item Predict Types: response, prob -\item Feature Types: logical, integer, numeric, factor, ordered +\item Feature Types: numeric, factor, ordered \item Properties: importance, missings, multiclass, twoclass, weights } } +\section{Installation}{ + +The easiest way to install catboost is with the helper function +\link{install_catboost}. +} + \section{Custom mlr3 defaults}{ \itemize{ @@ -64,11 +70,11 @@ print(learner) learner$param_set$ids() } \references{ -CatBoost: unbiased boosting with categorical features. +catboost: unbiased boosting with categorical features. Liudmila Prokhorenkova, Gleb Guse, Aleksandr Vorobev, Anna Veronika Dorogush and Andrey Gulin. 2017. https://arxiv.org/abs/1706.09516. -CatBoost: gradient boosting with categorical features support. +catboost: gradient boosting with categorical features support. Anna Veronika Dorogush, Vasily Ershov and Andrey Gulin. 2018. https://arxiv.org/abs/1810.11363. } diff --git a/man/mlr_learners_regr.catboost.Rd b/man/mlr_learners_regr.catboost.Rd index 77b0544f9..e1be58958 100644 --- a/man/mlr_learners_regr.catboost.Rd +++ b/man/mlr_learners_regr.catboost.Rd @@ -20,11 +20,17 @@ lrn("regr.catboost") \itemize{ \item Packages: catboost \item Predict Types: response -\item Feature Types: logical, integer, numeric, factor, ordered +\item Feature Types: numeric, factor, ordered \item Properties: importance, missings, weights } } +\section{Installation}{ + +The easiest way to install catboost is with the helper function +\link{install_catboost}. +} + \section{Custom mlr3 defaults}{ \itemize{ @@ -64,11 +70,11 @@ print(learner) learner$param_set$ids() } \references{ -CatBoost: unbiased boosting with categorical features. +catboost: unbiased boosting with categorical features. Liudmila Prokhorenkova, Gleb Guse, Aleksandr Vorobev, Anna Veronika Dorogush and Andrey Gulin. 2017. https://arxiv.org/abs/1706.09516. -CatBoost: gradient boosting with categorical features support. +catboost: gradient boosting with categorical features support. Anna Veronika Dorogush, Vasily Ershov and Andrey Gulin. 2018. https://arxiv.org/abs/1810.11363. } diff --git a/tests/testthat/test_catboost_classif_catboost.R b/tests/testthat/test_catboost_classif_catboost.R index 1e38d0932..8837ec26b 100644 --- a/tests/testthat/test_catboost_classif_catboost.R +++ b/tests/testthat/test_catboost_classif_catboost.R @@ -1,5 +1,5 @@ if (!requireNamespace("catboost", quietly = TRUE)) { - install_catboost() + install_catboost("0.26.1") } test_that("autotest", { diff --git a/tests/testthat/test_catboost_regr_catboost.R b/tests/testthat/test_catboost_regr_catboost.R index df4864711..90a038dd5 100644 --- a/tests/testthat/test_catboost_regr_catboost.R +++ b/tests/testthat/test_catboost_regr_catboost.R @@ -1,5 +1,5 @@ if (!requireNamespace("catboost", quietly = TRUE)) { - install_catboost() + install_catboost("0.26.1") } From 0434147741f2724e076c0050f308e8087dcf0170 Mon Sep 17 00:00:00 2001 From: Raphael Sonabend Date: Tue, 31 Aug 2021 14:11:48 +0100 Subject: [PATCH 4/6] install devtools --- R/fn_install_catboost.R | 9 +++++++-- man/install_catboost.Rd | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/R/fn_install_catboost.R b/R/fn_install_catboost.R index 052860c7f..70bd0c0a4 100644 --- a/R/fn_install_catboost.R +++ b/R/fn_install_catboost.R @@ -5,8 +5,8 @@ #' @param os `(character(1))` \cr #' Operating system to install on, if `NULL` automatically detected #' @param install_required `(logical(1))` \cr -#' If `TRUE` (default) and version is `NULL` then installs {curl} and -#' {jsonlite} if not already installed. +#' If `TRUE` (default) then installs required packages: {curl}, {jsonlite}, +#' {devtools} #' @param INSTALL_opts `(character())` \cr #' Passed to [devtools::install_url] #' @param ... `ANY` \cr @@ -18,6 +18,7 @@ install_catboost <- function(version = NULL, os = NULL, "--no-test-load"), ...) { if (is.null(version)) { + if (requireNamespace("jsonlite", quietly = TRUE) && install_required) { utils::install.packages("jsonlite", repos = "https://cloud.r-project.org") } @@ -41,5 +42,9 @@ install_catboost <- function(version = NULL, os = NULL, "https://github.com/catboost/catboost/releases/download/v%s/catboost-R-%s-%s.tgz", version, os, version) + if (requireNamespace("devtools", quietly = TRUE) && install_required) { + utils::install.packages("devtools", repos = "https://cloud.r-project.org") + } + devtools::install_url(url, INSTALL_opts = INSTALL_opts, ...) } diff --git a/man/install_catboost.Rd b/man/install_catboost.Rd index 8bfabc837..eab544ac7 100644 --- a/man/install_catboost.Rd +++ b/man/install_catboost.Rd @@ -20,8 +20,8 @@ Version to install, if \code{NULL} installs latest} Operating system to install on, if \code{NULL} automatically detected} \item{install_required}{\code{(logical(1))} \cr -If \code{TRUE} (default) and version is \code{NULL} then installs {curl} and -{jsonlite} if not already installed.} +If \code{TRUE} (default) then installs required packages: {curl}, {jsonlite}, +{devtools}} \item{INSTALL_opts}{\code{(character())} \cr Passed to \link[devtools:remote-reexports]{devtools::install_url}} From b42d2bca4349efb4e27935eafb38a812a67a4786 Mon Sep 17 00:00:00 2001 From: Raphael Sonabend Date: Tue, 31 Aug 2021 15:17:01 +0100 Subject: [PATCH 5/6] fix require --- R/fn_install_catboost.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/R/fn_install_catboost.R b/R/fn_install_catboost.R index 70bd0c0a4..85660cf86 100644 --- a/R/fn_install_catboost.R +++ b/R/fn_install_catboost.R @@ -19,11 +19,11 @@ install_catboost <- function(version = NULL, os = NULL, if (is.null(version)) { - if (requireNamespace("jsonlite", quietly = TRUE) && install_required) { + if (!requireNamespace("jsonlite", quietly = TRUE) && install_required) { utils::install.packages("jsonlite", repos = "https://cloud.r-project.org") } - if (requireNamespace("curl", quietly = TRUE) && install_required) { + if (!requireNamespace("curl", quietly = TRUE) && install_required) { utils::install.packages("curl", repos = "https://cloud.r-project.org") } @@ -42,7 +42,7 @@ install_catboost <- function(version = NULL, os = NULL, "https://github.com/catboost/catboost/releases/download/v%s/catboost-R-%s-%s.tgz", version, os, version) - if (requireNamespace("devtools", quietly = TRUE) && install_required) { + if (!requireNamespace("devtools", quietly = TRUE) && install_required) { utils::install.packages("devtools", repos = "https://cloud.r-project.org") } From b68cc16a254a082f917eb9cd0cd33f250f0ea3c0 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 31 Aug 2021 14:49:07 +0000 Subject: [PATCH 6/6] Update learner table --- R/sysdata.rda | Bin 2719 -> 2710 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/R/sysdata.rda b/R/sysdata.rda index bdf1e89f453c5848b1ea55dee39f8c09041985d7..00c31426950ac173aafc8d145297b720aac7c9bb 100644 GIT binary patch literal 2710 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 ZfWXGyb-g>h=*lbpF64@Ep&(hP9!P$m+Gzj)