The classbound package is built around a robust, extensible pipeline leveraging R's S3 object system. The primary goal is to decouple the core boundary computation and visualization logic from the idiosyncrasies of specific modeling packages.

The Core Pipeline

The user-facing workflow consists of four steps. Data validation and metadata extraction happen centrally in fit_model, and the results flow through the pipeline.

graph TD Data[(Training Data)] --> Preprocess[preprocess_data] Preprocess --> Fit[fit_model] Fit --> Model([classbound_model]) Model -.-> Predict[predict_model] Model --> Boundary[boundary_compute] Boundary --> Predict Predict -.-> OutputGrid[(Prediction Grid)] OutputGrid -.-> Boundary Boundary --> Plot[plot_boundary] Plot --> Viz([ggplot2 Object]) classDef function fill:#2563eb,stroke:#1d4ed8,stroke-width:2px,color:#fff; classDef data fill:#fef9c3,stroke:#f59e0b,stroke-width:2px,color:#0f172a; class Preprocess,Fit,Predict,Boundary,Plot function; class Data,Model,OutputGrid,Viz data;

The Adapter Pattern (S3 Dispatch)

To support multiple classifiers natively, the core pipeline never calls specific model algorithms directly. Instead, fit_model and predict_model act as wrappers around internal S3 generics: fit_adapter and predict_adapter.

This allows user-defined classifiers to plug into the pipeline automatically simply by defining these two methods in the global environment, without requiring any changes to the classbound source code.

%%{init: {'themeVariables': {'fontSize': '16px', 'fontFamily': 'inherit'}}}%% classDiagram direction TB class CorePipeline { +fit_model(data, labels, method) +predict_model(model, newdata) } class S3Generics { internal +fit_adapter(object, data, labels) +predict_adapter(model, newdata) } class rpart_adapter { +fit_adapter.classbound_rpart() +predict_adapter.classbound_rpart() } class randomForest_adapter { +fit_adapter.classbound_randomForest() +predict_adapter.classbound_randomForest() } class custom_adapter { User_Defined +fit_adapter.classbound_custom() +predict_adapter.classbound_custom() } CorePipeline --> S3Generics : delegates S3Generics <|-- rpart_adapter : dispatches S3Generics <|-- randomForest_adapter : dispatches S3Generics <|-- custom_adapter : natively supports

Data Validation Contract

Initially, individual adapters handled their own factor conversions and missing value checks. This led to code duplication. Now, preprocess_data() centralizes all factor handling, NA rejection, tibble coercion, and validation.

Adapters assume the input is clean. In return, adapters must fulfill a strict output contract: predict_adapter must return exactly a list structure: list(class = ..., probs = ...). If the model does not support probabilities (e.g., PPtreeExt), it must return probs = NULL.

predict_model() then intercepts this output and strictly enforces that the class factor levels match the exact levels observed during fit_model(). This guarantees that multi-model comparison workflows retain consistent color mappings, even if one model was trained on a subset of the data that lacked a certain class.