Nothing
#' Save a model to a JSON file.
#'
#' Serializes a \code{pptr} or \code{pprf} model to JSON format compatible
#' with the C++ CLI. The JSON includes the model structure, group labels,
#' training parameters, and optionally variable importance metrics.
#'
#' @param model A \code{pptr} or \code{pprf} model (inheriting from \code{ppmodel}).
#' @param path File path to write the JSON to.
#' @param include_metrics If \code{TRUE} (default), include variable importance
#' and (for forests) OOB error in the output. Set to \code{FALSE} to save
#' only the model structure and metadata.
#' @param ... Additional arguments (currently unused).
#' @return Invisibly returns the file \code{path} that was written (a length-one
#' character string). Called for its side effect of serializing \code{model}
#' to a JSON file at \code{path}.
#' @seealso \code{\link{load_json}}, \code{\link{pptr}}, \code{\link{pprf}}
#' @examples
#' model <- pptr(Species ~ ., data = iris, seed = 0)
#' path <- tempfile(fileext = ".json")
#' save_json(model, path)
#'
#' @export
save_json <- function(model, path, ...) {
UseMethod("save_json")
}
#' @rdname save_json
#' @export
save_json.ppmodel <- function(model, path, include_metrics = TRUE, ...) {
if (include_metrics && is.null(model$x)) {
warning("Training data not available; saving without metrics.", call. = FALSE)
include_metrics <- FALSE
}
x <- if (!is.null(model$x)) model$x else matrix(0, nrow = 0, ncol = 0)
feature_names <- if (!is.null(model$x)) colnames(model$x) else character(0)
groups <- if (!is.null(model$groups)) model$groups else character(0)
# `y` is the unified response vector: integer class labels (classification)
# or the continuous response (regression). Single field, mode-agnostic —
# matches the C++ unified-y convention.
y <- if (!is.null(model$y)) model$y else {
if (identical(model$mode, "regression")) numeric(0) else integer(0)
}
json_str <- ppforest2_save_model_json(
model, groups, include_metrics, x, y, feature_names
)
writeLines(json_str, path)
invisible(path)
}
#' Load a model from a JSON file.
#'
#' Deserializes a \code{pptr} or \code{pprf} model from a JSON file. The model
#' can be used for prediction immediately. If variable importance metrics and
#' OOB fields were saved, they are restored into the model's lazy-computation
#' cache so later accessor calls (\code{oob_error()}, \code{permuted_importance()},
#' etc.) return the stored values without recomputation.
#'
#' Note that \code{formula}, \code{x}, and \code{y} are not stored in the JSON
#' and will be \code{NULL} on the loaded model. Formula-based prediction and
#' any accessor that requires training data will error unless the user
#' re-attaches those fields.
#'
#' @param path File path to read the JSON from.
#' @return A \code{pptr} or \code{pprf} model (with the appropriate
#' \code{_classification} / \code{_regression} subclass).
#' @seealso \code{\link{save_json}}, \code{\link{pptr}}, \code{\link{pprf}}
#' @examples
#' model <- pptr(Species ~ ., data = iris, seed = 0)
#' path <- tempfile(fileext = ".json")
#' save_json(model, path)
#' loaded <- load_json(path)
#' predict(loaded, as.matrix(iris[, 1:4]))
#'
#' @export
load_json <- function(path) {
# The underlying C++ loader throws `std::runtime_error` (surfaced by Rcpp
# as a plain R error without the file path) for any structural problem —
# malformed JSON, mode/groups mismatch, missing required keys, etc. Wrap
# here so the user sees which file triggered the failure.
model <- tryCatch(
ppforest2_load_model_json(path),
error = function(e) {
stop(
"Failed to load model from '", path, "': ", conditionMessage(e),
call. = FALSE
)
}
)
model$groups <- as.character(model$groups)
# Restore mode from the training spec.
model$mode <- model$training_spec$mode %||% "classification"
is_forest <- !is.null(model$trees)
is_regression <- identical(model$mode, "regression")
if (is_forest && !is_regression) {
for (i in seq_along(model$trees)) {
model$trees[[i]]$groups <- model$groups
}
}
# Cache env for lazy accessors. Prime with values that travelled through JSON
# (the C++ save/load path persists OOB metrics and weighted/permuted VI).
# `oob_metrics` mirrors the C++ Export shape: a list with `confusion_matrix`
# + `error_rate` (classification) or `mse`/`mae`/`r_squared` (regression).
# `oob_error()` extracts the right scalar from it without recomputation.
model$.cache <- .new_cache()
oob_scalar <- if (!is.null(model$oob_metrics)) {
if (is_regression) model$oob_metrics$mse else model$oob_metrics$error_rate
} else NULL
.prime_cache(model, "oob_error", oob_scalar)
.prime_cache(model, "weighted_importance", model$vi$weighted)
.prime_cache(model, "permuted_importance", model$vi$permuted)
# Clean up primed VI fields so users access via generics, not raw slots.
# `oob_metrics` itself stays on the model — it's the richer companion to
# `oob_error()` and `model$training_metrics`.
model$vi$weighted <- NULL
model$vi$permuted <- NULL
# Class is set by the Rcpp wrap layer (see `make_model_class` in
# bindings/R/inst/include/ppforest2.h), which derives it from
# `training_spec->mode`. The deserialized model already carries the
# correct 3-level S3 class — no reassignment needed.
model
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.