---
title: "Supported Models and recipes steps"
description: >
  A complete list of supported parsnip models, recipes steps, and tailor
  adjustments that can be converted to orbital objects.
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Supported Models and recipes steps}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

The supported methods currently all come from [tidypredict](https://tidypredict.tidymodels.org/) right now.

## Supported models

This table doesn't exhaustively list fully unsupported models. Please file [an issue](https://github.com/tidymodels/orbital/issues) to add model to table.

```{r}
#| echo: false
#| message: false
if (!rlang::is_installed(c("gt", "tibble"))) {
  knitr::knit_exit()
}
```

```{r}
#| echo: false
#| message: false
#| error: false
#| output: false
library(gt)
library(dplyr)

tibble::tribble(
  ~parsnip,               ~engine,              ~numeric, ~class, ~prob,
  "`bag_tree()`",         "`\"C5.0\"`",         "❌",     "✅",    "❌",
  "`bag_tree()`",         "`\"rpart\"`",        "❌",     "✅",    "❌",
  "`bart()`",             "`\"dbarts\"`",       "✅",     "❌",    "❌",
  "`boost_tree()`",       "`\"C5.0\"`",         "❌",     "✅",    "❌",
  "`boost_tree()`",       "`\"catboost\"`",     "✅",     "✅",    "✅",
  "`boost_tree()`",       "`\"h2o_gbm\"`",      "✅",     "✅",    "✅",
  "`boost_tree()`",       "`\"lightgbm\"`",     "✅",     "✅",    "✅",
  "`boost_tree()`",       "`\"xgboost\"`",      "✅",     "✅",    "✅",
  "`C5_rules()`",         "`\"C5.0\"`",         "❌",     "✅",    "❌",
  "`cubist_rules()`",     "`\"Cubist\"`",       "✅",     "❌",    "❌",
  "`decision_tree()`",    "`\"C5.0\"`",         "❌",     "✅",    "❌",
  "`decision_tree()`",    "`\"partykit\"`",     "✅",     "✅",    "✅",
  "`decision_tree()`",    "`\"rpart\"`",        "✅",     "✅",    "✅",
  "`discrim_flexible()`", "`\"earth\"`",        "❌",     "❌",    "❌",
  "`discrim_linear()`",   "`\"MASS\"`",         "❌",     "✅",    "✅",
  "`discrim_linear()`",   "`\"mda\"`",          "❌",     "✅",    "✅",
  "`discrim_linear()`",   "`\"sda\"`",          "❌",     "✅",    "✅",
  "`discrim_linear()`",   "`\"sparsediscrim\"`","❌",     "✅",    "✅",
  "`discrim_quad()`",     "`\"MASS\"`",         "❌",     "✅",    "✅",
  "`linear_reg()`",       "`\"lm\"`",           "✅",     "❌",    "❌",
  "`linear_reg()`",       "`\"glm\"`",          "✅",     "❌",    "❌",
  "`linear_reg()`",       "`\"glmnet\"`",       "✅",     "❌",    "❌",
  "`logistic_reg()`",     "`\"glm\"`",          "❌",     "✅",    "✅",
  "`logistic_reg()`",     "`\"glmnet\"`",       "❌",     "✅",    "✅",
  "`logistic_reg()`",     "`\"LiblineaR\"`",    "❌",     "✅",    "✅",
  "`mars()`",             "`\"earth\"`",        "✅",     "✅",    "✅",
  "`mlp()`",              "`\"nnet\"`",         "✅",     "✅",    "✅",
  "`multinom_reg()`",     "`\"glmnet\"`",       "❌",     "✅",    "✅",
  "`multinom_reg()`",     "`\"nnet\"`",         "❌",     "✅",    "✅",
  "`naive_Bayes()`",      "`\"klaR\"`",         "❌",     "✅",    "✅",
  "`naive_Bayes()`",      "`\"naivebayes\"`",   "❌",     "✅",    "✅",
  "`nearest_neighbor()`", "`any`",              "❌",     "❌",    "❌",
  "`null_model()`",       "`\"parsnip\"`",      "✅",     "✅",    "✅",
  "`pls()`",              "`\"mixOmics\"`",     "✅",     "❌",    "✅",
  "`rand_forest()`",      "`\"aorsf\"`",        "✅",     "❌",    "❌",
  "`rand_forest()`",      "`\"partykit\"`",     "✅",     "❌",    "❌",
  "`rand_forest()`",      "`\"randomForest\"`", "✅",     "✅",    "✅",
  "`rand_forest()`",      "`\"ranger\"`",       "✅",     "✅",    "✅",
  "`rule_fit()`",         "`\"h2o\"`",          "✅",     "✅",    "✅",
  "`rule_fit()`",         "`\"xrf\"`",          "✅",     "✅",    "✅",
  "`svm_linear()`",       "`\"kernlab\"`",      "✅",     "✅",    "✅",
  "`svm_linear()`",       "`\"LiblineaR\"`",    "✅",     "✅",    "❌"
) |>
  gt() |>
  tab_spanner(
    label = "Model",
    columns = c(parsnip, engine)
  ) |>
  tab_spanner(
    label = "Regression",
    columns = c(numeric)
  ) |>
  tab_spanner(
    label = "Classification",
    columns = c(class, prob)
  ) |>
  tab_header(
    title = "Supported Prediction Types"
  ) |>
  cols_align(
    "center",
    columns = c(numeric, class, prob)
  ) |>
  tab_footnote("✅: Supported") |>
  tab_footnote("❌: Cannot be supported") |>
  tab_footnote("⚪: Not yet supported") |>
  fmt_markdown(
    columns = c(parsnip, engine)
  )
```

### Why some models support one classification type but not the other

The two classification columns are separate because not every model produces both, and orbital refuses a type rather than inventing it.

**Class without probability.** Some models predict a label directly and never compute a probability at all. `bag_tree()` and `boost_tree()` with the `"C5.0"` engine, `C5_rules()`, and `bag_tree()` with `"rpart"` all reach their answer by voting across an ensemble; the vote yields a winner, not a distribution. `decision_tree()` with `"C5.0"` is the single-tree version of the same thing: it labels each leaf with a class rather than with class counts. `svm_linear()` with `"LiblineaR"` produces a *decision value*, the signed distance from the separating hyperplane. Its sign gives the class, but its magnitude is uncalibrated: it is not a probability and does not become one by being passed through a logistic. Doing that would attach a confidence the model never estimated, so `type = "prob"` is refused for all of these.

**Probability without class.** `pls()` with `"mixOmics"` is the reverse case. It gives per-level values, but mixOmics assigns a class by distance to the class centroid rather than by taking the largest of those values, so the obvious `which.max()` would disagree with the model on some rows. orbital gives the probabilities and refuses `type = "class"`.

**Both, but the cut is not 0.5.** `svm_linear()` with `"kernlab"` supports both, with a wrinkle worth knowing about. kernlab classifies by the sign of its decision function and fits its probabilities separately, using Platt scaling. The two rules do not cross at 0.5, so orbital emits kernlab's own threshold as a literal in the expression. If you compare orbital's `.pred_class` against thresholding its `.pred_*` columns at 0.5 yourself, expect disagreement on rows near the boundary; orbital matches the model, and 0.5 does not.

The general rule: where a model's own prediction rule and the naive rule disagree, orbital follows the model.

## Recipes steps

```{r}
#| include: false
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

```{r}
#| label: setup
#| echo: false
library(orbital)
```

```{r}
#| echo: false
all_funs <- ls(getNamespace("orbital"))

steps <- grep("orbital.step_", all_funs, value = TRUE)
steps <- gsub("orbital.", "", steps)
```

The following `r length(steps)` recipes steps are supported

```{r}
#| results: asis
#| echo: false
cat(paste0("- `", steps, "()`\n"))
```

## tailor adjustments

The following 4 tailor methods are supported


- `tailor::adjust_equivocal_zone()`
- `tailor::adjust_numeric_range()`
- `tailor::adjust_predictions_custom()`
- `tailor::adjust_probability_threshold()`
