--- title: "Custom Predictions and Adapters" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Custom Predictions and Adapters} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") ``` `classbound` is designed to work with the widest possible range of R classifiers. This vignette explains how prediction routing works and how to handle classifiers whose APIs do not fit the default path. ## The prediction philosophy ``` Standard classifier (predict returns factor/vector) → handled automatically via predict_adapter.default() Non-standard classifier (predict returns a list or complex object) → provide predfun to extract class labels Officially supported classifiers (rpart, randomForest, PPtree, ppforest2) → handled by built-in S3 adapters with full probability support ``` ## 1. Standard classifiers (no extra work needed) If a classifier's `predict()` method returns a vector or factor of class labels directly, `classbound` handles it automatically. No configuration is needed. ```{r standard, eval=FALSE} library(classbound) library(palmerpenguins) penguins <- na.omit(palmerpenguins::penguins[ , c("species", "bill_length_mm", "bill_depth_mm") ]) # e1071::svm returns a factor of class labels; works out of the box classbound(penguins, species ~ bill_length_mm + bill_depth_mm, e1071::svm) ``` ## 2. Non-standard models (using `predfun`) Some classifiers return a list, data frame, or other complex object from `predict()`. The default path will stop with an informative error message suggesting you provide a `predfun`. The `predfun` receives the fitted model and new data, and must return either a factor/vector of class labels, or a list with `$class` and `$probs`. ```{r predfun, eval=FALSE} # MASS::qda returns list($class, $posterior, $x), so extract $class classbound( penguins, species ~ bill_length_mm + bill_depth_mm, MASS::qda, predfun = function(model, newdata, ...) predict(model, newdata, ...)$class ) # MASS::lda (same approach) classbound( penguins, species ~ bill_length_mm + bill_depth_mm, MASS::lda, predfun = function(model, newdata, ...) predict(model, newdata, ...)$class ) # Return probabilities as well (enables gradient visualization) classbound( penguins, species ~ bill_length_mm + bill_depth_mm, MASS::lda, predfun = function(model, newdata, ...) { out <- predict(model, newdata, ...) list(class = out$class, probs = out$posterior) } ) ``` The `predfun` argument is available in `classbound()`, `fit_model()` (via `boundary_compute()`), and `predict_model()`. ## 3. Officially supported classifiers (built-in adapters) `classbound` maintains a small set of built-in S3 adapters for classifiers whose APIs require model-specific handling to extract both class labels and probabilities: | Classifier | Adapter | Probabilities | |---|---|---| | `rpart::rpart` | `predict_adapter.rpart` | Yes | | `randomForest::randomForest` | `predict_adapter.randomForest` | Yes | | `PPtreeViz::PPTreeclass` | `predict_adapter.PPtreeclass` | No | | `PPtreeExt::PPtreeExtclass` | `predict_adapter.PPtreeExtclass` | No | | `ppforest2::pprf` | `predict_adapter.pprf_classification` | Yes | These adapters are invoked automatically when the classifier object belongs to the corresponding S3 class. No `predfun` is needed. ## The adapter contract Every prediction path must produce a list with exactly two elements: ```r list( class = factor(...), # vector of predicted class labels probs = matrix(...) # n x K probability matrix, or NULL ) ``` `probs` must be `NULL` for classifiers that do not provide probability estimates. `classbound` handles `NULL` probabilities gracefully: the boundary plot renders with flat (non-gradient) colored regions instead of a probability surface. ## 4. Writing a custom S3 adapter Custom S3 adapters are only needed if you are building an extension package for `classbound` and want to officially support a complex classifier without requiring users to write `predfun` every time. For most users, a `predfun` is sufficient and far simpler. ```{r custom_adapter, eval=FALSE} # Example: custom adapter for a hypothetical classifier "myModel" predict_adapter.myModel <- function(model, newdata, ...) { raw <- predict(model, newdata, type = "response") list( class = factor(raw$labels), probs = as.matrix(raw$probabilities) ) } ``` Define the method in your package's namespace and it will be dispatched automatically whenever `classbound` encounters a model object of class `"myModel"`.