plot() draws the trees of one tree family to illustrate how a Random Planted Forest predicts.
type = "tree" draws a split diagram in the style of Figure 1b of the paper, reconstructed from the fitted leaves, type = "boxes" draws the leaves in variable space, before or after purification.
rpf_boxes() returns the leaves as a data frame.
Requires ggplot2, and patchwork for type = "boxes".
The "Getting started" vignette uses it to show how a tree family is built and what purification does."leaves" for regression excluded that split, so binary predictors were never split on, and classification could not learn from them at all.
Fitted models change for classification and for regression with split_structure other than "leaves".nthreads > 1 are reported as R errors.
Previously an error in a worker thread terminated the R session.-Wall -pedantic.predict_components() returns one intercept per target level for multiclass classification, named by target_levels (#70).
Previously only the first level's intercept was returned, so components plus intercept did not sum to the prediction for other levels.rpf() now defaults to max_interaction = 2, main effects and pairwise interactions, instead of 1, an additive model (#66).
Values above the number of predictors are capped silently instead of with a message.rpf() now defaults to loss = "exponential" for classification instead of "L2", so classification fits give proper probability estimates by default; regression still uses "L2" (#66).rpf() arguments are reordered by purpose (forest size, split search, loss, other). Pass arguments after the data arguments by name (#66).rpf(), predict() and purify() error on unknown arguments, such as misspelled ones, instead of silently ignoring them (#66).purify() is a regular function instead of an S3 generic (#66).preprocess_predictors_predict() is no longer exported; it is an internal step of predict() (#66).$fit): set_data() no longer fits, call fit() afterwards, and an unknown classification loss is an error instead of a silent fallback to L2 (@jyliuu, #57).rpf() no longer has a cv argument, which never had an effect: the C++ cross-validation was a no-op (#66).predict() and predict_components() error on missing values in new_data, naming the affected columns.
Previously such rows silently received arbitrary predictions, as rpf() has no handling for missing values.
rpf() likewise reports missing values in the training data by column (#66).predict(type = "class") breaks probability ties by level order instead of at random, so class predictions are deterministic and no longer advance the random number generator (#66).purify() returns its input invisibly, as documented (#66).max_interaction (#66).predict() gains nthreads, defaulting to the nthreads used for fitting.predict_components() is much faster: purified forests look up only the trees of the requested component, and each distinct input row is predicted once, which pays off for low-cardinality features (#59, #65).print() method is overhauled and gains a format() counterpart: it reports the forest size, interaction degree, purification state, split search settings and number of threads (#66).deterministic = TRUE with ntrees > 1 warns, as all tree families are then identical (#66).rpf(), purify() and predict_components(), including guidance on choosing parameters and on how purification interacts with predict_components() (#66).Rcpp: it uses standard C++ types and exceptions, and an Rcpp layer (src/rcpp_interface.*) converts at the boundary.
This is groundwork for bindings in other languages such as Python (@jyliuu, #57).cli and rlang are now imported, mvtnorm is only needed to build the pkgdown site (#66).It is now possible to serialize and de-serialize an rpf object via marshalling,
meaning a fitted model can be saved to disk or dispatched to a worker process for
parallelization or encapsulation, as is done in mlr3. This also re-opens the door
for the mlr3extralearners wrapper, which was removed due to the lack of
serialization support.
rpf_marshal() / rpf_unmarshal() serialize a fitted forest to a plain
R list and back, making saveRDS()-based storage of rpf models possible (#52).
Purified forests restore their purified state directly; training data is only
embedded with include_data = TRUE (required to purify() after restoring).
Blobs record the blob format version and the package version they were
created with; restoring under an older package than the one that saved the
model warns. Malformed or corrupt blobs error instead of reading out of bounds.rpf_is_valid() checks whether an rpf object's internal model is usable,
predict(), purify() and predict_components() now give an actionable
error for rpf objects restored via readRDS() without marshaling.bundle::bundle() method for rpf models wrapping the marshaling API.delta changed from 0 to 0.001. With delta = 0, splits
producing single-class nodes have infinite logit loss and are always rejected,
which also degraded binary logit fits.loss = "logit", which effectively never
split and predicted near-uniform class probabilities (#40). The C++ logit loss
is a reference-class multinomial formulation expecting K-1 indicator columns,
but the R wrapper passed a full K-column one-hot matrix, pinning the implicit
reference-class probability to zero. Multiclass logit outcomes are now encoded
with the first factor level as reference class:
predict(type = "prob") reconstructs all K class probabilities from the
K-1 logits; rows sum to 1 exactly.predict(type = "numeric"/"link") now returns K-1 columns named after the
non-reference levels (previously K columns).predict_components() on multiclass logit fits returns per-class components
for the K-1 non-reference levels; target_levels reflects this.rpf() arguments controlling split-candidate sampling:
split_structure = "leaves": Defines what a split candidate is and how candidates are drawn.
One of "leaves" (default), "hist", "cur_trees_1", "cur_trees_2", or "res_trees"; see ?rpf for details.max_candidates = 50: Maximum number of split candidates sampled per iteration.split_decay_rate = 0.1: Exponential aging of repeatedly drawn but unchosen split candidates.
split_decay_rate = 0 corresponds to no aging and uniform sampling.delete_leaves = TRUE: Whether a parent leaf is deleted when splitting along an existing dimension.nthreads: per-tree seeds are drawn from R's RNG,
so set.seed() gives identical forests for serial and multithreaded fits.purify() gains arguments:
mode = 2: Purification algorithm; 2 is a new fast exact method, 1 is the legacy grid-based path.nthreads = NULL: Purification is now multithreaded, defaulting to the fit's nthreads setting.maxp_interaction = NULL: Optionally only compute purified components up to this interaction order.rpf() argument export_forest = FALSE: The flattened forest is no longer stored in the
fitted object by default, so rpf_object$forest is NULL unless export_forest = TRUE.
This reduces object size; predict(), purify(), and predict_components() are unaffected.preprocess_predictors_predict() is now exported.nthreads > 1, caused by a thread_local buffer
with a non-trivial destructor being destroyed at thread exit.src/ have been refactored into modular sub-files (#53)rpf() now errors if a regression target is combined with a loss other than "L2".logical, which are now converted via as.integer.parallel = TRUE|FALSE argument in rpf() has been substituted by an nthreads = 1L argument, allowing for more flexible parallelization.
The previous behavior only allowed for either no parallelization or using n-1 of n available cores.
The new implementation should be reasonably robust and the default behavior remains serial execution.SystemRequirements field from DESCRIPTION: Now the default C++ version is C++17 and
with a minor change to internal use of random numbers, randomPlantedForest is now compatible with C++11 through C++23.remainder term to predict_components output for case where max_interaction supplied is smaller than max_interaction in rpf fit.
In that case, the m values don't sum up to the global predictions, so we add a remainder to allow reconstruction of that property.glex class to output of predict_components(), for extended functionality available with glex.target_levels vector to output of predict_components() to aid multiclass handling.
Keeping track of levels is somewhat awkward since column names of $m need to be identifiable
regarding the target level.NEWS.md file to track changes to the package.