diff --git a/.Rbuildignore b/.Rbuildignore index d2fede9..6fae4fc 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -7,3 +7,5 @@ ^docs$ ^pkgdown$ ^SCRATCH$ +^cran-comments\.md$ +^CRAN-SUBMISSION$ diff --git a/.gitignore b/.gitignore index 8c80211..5b4673d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ docs inst/doc SCRATCH/ +**/*/.DS_Store +.DS_Store diff --git a/CRAN-SUBMISSION b/CRAN-SUBMISSION new file mode 100644 index 0000000..d963f3f --- /dev/null +++ b/CRAN-SUBMISSION @@ -0,0 +1,3 @@ +Version: 0.1.0 +Date: 2025-01-15 02:46:58 UTC +SHA: f5833ac6b32f3e95843b0dc9c44aff7e1b4c1dc3 diff --git a/DESCRIPTION b/DESCRIPTION index 8da6e0d..9148d25 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,11 +1,12 @@ Package: parttree -Title: Visualise simple decision tree partitions -Version: 0.0.1.9006 +Title: Visualize Simple 2-D Decision Tree Partitions +Version: 0.1.0 +Date: 2025-10-14 Authors@R: c( person(given = "Grant", family = "McDermott", role = c("aut", "cre"), - email = "grantmcd@uoregon.edu", + email = "gmcd@amazon.com", comment = c(ORCID = "0000-0001-7883-8573")), person(given = "Achim", family = "Zeileis", @@ -22,14 +23,20 @@ Authors@R: c( role = "ctb", email = "julia.silge@gmail.com", comment = c(ORCID = "0000-0002-3671-836X"))) -Description: Simple functions for plotting 2D decision tree partition plots. +Description: Visualize the partitions of simple decision trees, involving one or + two predictors, on the scale of the original data. Provides an intuitive + alternative to traditional tree diagrams, by visualizing how a decision tree + divides the predictor space in a simple 2D plot alongside the original data. + The 'parttree' package supports both classification and regression trees + from 'rpart' and 'partykit', as well as trees produced by popular frontend + systems like 'tidymodels' and 'mlr3'. Visualization methods are provided for + both base R graphics and 'ggplot2'. License: MIT + file LICENSE Encoding: UTF-8 Roxygen: list(markdown = TRUE) RoxygenNote: 7.3.2 LazyData: true -URL: https://github.com/grantmcdermott/parttree, - http://grantmcdermott.com/parttree +URL: https://grantmcdermott.com/parttree/ BugReports: https://github.com/grantmcdermott/parttree/issues Imports: graphics, diff --git a/NEWS.md b/NEWS.md index 11f147d..c3600cd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,12 +1,12 @@ -# parttree 0.0.1.9006 +# parttree 0.1.0 -To be released as 0.1.0 +Our first CRAN submission. 🎉🎉 #### Breaking changes -* Move ggplot2 to Enhances, following the addition of native (base R) -`plot.parttree` method. The `geom_parttree()` function now checks whether -ggplot2 is available on the user's system before executing any code. (#18) +* Move ggplot2 to Enhances, following the addition of a dedicated (base R) +`plot.parttree` method. The `geom_parttree()` function is still available, but +requires that ggplot2 is already been installed on the user's system. (#18) * The `flipaxes` argument has been renamed to `flip`, e.g. `parttree(..., flip = TRUE)`. (#18) diff --git a/R/geom_parttree.R b/R/geom_parttree.R index 1d48606..613dc0b 100644 --- a/R/geom_parttree.R +++ b/R/geom_parttree.R @@ -20,6 +20,7 @@ #' plot layers, note that the data input for `geom_parttree()` (i.e. decision #' tree object) must assigned in the layer itself; not in the initialising #' [ggplot2::ggplot()] call. See Examples. +#' @returns A \code{\link[ggplot2]{ggplot}} layer. #' @section Aesthetics: #' \code{geom_parttree()} aims to "work-out-of-the-box" with minimal input from #' the user's side, apart from specifying the data object. This includes taking @@ -42,37 +43,39 @@ #' #' library(parttree) # this package #' library(rpart) # decision trees -#' -#' ### Simple decision tree (max of two predictor variables) +#' \dontshow{data.table::setDTthreads(2)} +#' # +#' ## Simple decision tree (max of two predictor variables) #' #' iris_tree = rpart(Species ~ Petal.Length + Petal.Width, data=iris) #' -#' ## Plot with original iris data only +#' # Plot with original iris data only #' p = ggplot(data = iris, aes(x = Petal.Length, y = Petal.Width)) + #' geom_point(aes(col = Species)) #' -#' ## Add tree partitions to the plot (borders only) +#' # Add tree partitions to the plot (borders only) #' p + geom_parttree(data = iris_tree) #' -#' ## Better to use fill and highlight predictions +#' # Better to use fill and highlight predictions #' p + geom_parttree(data = iris_tree, aes(fill = Species), alpha=0.1) #' -#' ## To drop the black border lines (i.e. fill only) +#' # To drop the black border lines (i.e. fill only) #' p + geom_parttree(data = iris_tree, aes(fill = Species), col = NA, alpha = 0.1) #' -#' -#' ### Example with plot orientation mismatch +#' # +#' ## Example with plot orientation mismatch #' #' p2 = ggplot(iris, aes(x=Petal.Width, y=Petal.Length)) + #' geom_point(aes(col=Species)) #' -#' ## Oops +#' # Oops #' p2 + geom_parttree(data = iris_tree, aes(fill=Species), alpha = 0.1) #' -#' ## Fix with 'flip = TRUE' -#' +#' # Fix with 'flip = TRUE' +#' p2 + geom_parttree(data = iris_tree, aes(fill=Species), alpha = 0.1, flip = TRUE) #' -#' ### Various front-end frameworks are also supported, e.g.: +#' # +#' ## Various front-end frameworks are also supported, e.g.: #' #' # install.packages("parsnip") #' library(parsnip) @@ -84,10 +87,11 @@ #' #' p + geom_parttree(data = iris_tree_parsnip, aes(fill=Species), alpha = 0.1) #' +#' # +#' ## Trees with continuous independent variables are also supported. #' -#' ### Trees with continuous independent variables are also supported. But you -#' ### may need to adjust (or switch off) the fill legend to match the original -#' ### data, e.g.: +#' # Note: you may need to adjust (or switch off) the fill legend to match the +#' # original data, e.g.: #' #' iris_tree_cont = rpart(Petal.Length ~ Sepal.Length + Petal.Width, data=iris) #' p3 = ggplot(data = iris, aes(x = Petal.Width, y = Sepal.Length)) + @@ -98,10 +102,10 @@ #' geom_point(aes(col = Petal.Length)) + #' theme_minimal() #' -#' ## Legend scales don't quite match here: +#' # Legend scales don't quite match here: #' p3 #' -#' ## Better to scale fill to the original data +#' # Better to scale fill to the original data #' p3 + scale_fill_continuous(limits = range(iris$Petal.Length)) #' #' @seealso [plot.parttree()], which provides an alternative plotting method using base R graphics. diff --git a/R/parttree.R b/R/parttree.R index 3a66551..2263110 100644 --- a/R/parttree.R +++ b/R/parttree.R @@ -15,6 +15,14 @@ #' data frame? The default behaviour is for the first split variable in the #' tree to take the "y" slot, and any second split variable to take the "x" #' slot. Setting to `TRUE` switches these around. +#' +#' _Note:_ This argument is primarily useful when it passed via +#' [geom_parttree] to ensure correct axes orientation as part of a `ggplot2` +#' visualization (see [geom_parttree] Examples). We do not expect users to +#' call `parttree(..., flip = TRUE)` directly. Similarly, to switch axes +#' orientation for the native (base graphics) [plot.parttree] method, we +#' recommend calling `plot(..., flip = TRUE)` rather than flipping the +#' underlying `parttree` object. #' @seealso [plot.parttree], [geom_parttree], \code{\link[rpart]{rpart}}, #' \code{\link[partykit]{ctree}} [partykit::ctree]. #' @returns A data frame comprising seven columns: the leaf node, its path, a @@ -26,7 +34,8 @@ #' @export #' @examples #' library("parttree") -#' +#' \dontshow{data.table::setDTthreads(2)} +#' # #' ## rpart trees #' #' library("rpart") @@ -45,16 +54,17 @@ #' # customize further by passing extra options to (tiny)plot #' plot( #' rp_pt, -#' border = NA, # no partition borders -#' pch = 19, # filled points -#' alpha = 0.6, # point transparency -#' grid = TRUE, # background grid -#' palette = "classic", # new colour palette -#' xlab = "Topmost vertebra operated on", # custom x title -#' ylab = "Patient age (months)", # custom y title +#' border = NA, # no partition borders +#' pch = 16, # filled points +#' alpha = 0.6, # point transparency +#' grid = TRUE, # background grid +#' palette = "classic", # new colour palette +#' xlab = "Topmost vertebra operated on", # custom x title +#' ylab = "Patient age (months)", # custom y title #' main = "Tree predictions: Kyphosis recurrence" # custom title #' ) #' +#' # #' ## conditional inference trees from partyit #' #' library("partykit") @@ -66,10 +76,12 @@ #' rp2 = as.party(rp) #' parttree(rp2) #' +#' # #' ## various front-end frameworks are also supported, e.g. #' #' # tidymodels #' +#' # install.packages("parsnip") #' library(parsnip) #' #' decision_tree() |> @@ -81,6 +93,7 @@ #' #' # mlr3 (NB: use `keep_model = TRUE` for mlr3 learners) #' +#' # install.packages("mlr3") #' library(mlr3) #' #' task_iris = TaskClassif$new("iris", iris, target = "Species") @@ -157,15 +170,12 @@ parttree.rpart = if (length(vars)==2) { ## special case we can assume is likely wrong, notwithstanding ability to still flip axes if (vars[1]=="y" & vars[2]=="x") vars = rev(vars) - } - if (flip) { - vars = rev(vars) + } else if (length(vars)==1) { ## Handle edge cases with only 1 level - if (length(vars)==1) { - missing_var = setdiff(attr(tree$terms, 'term.labels'), vars) - vars = c(missing_var, vars) - } + missing_var = setdiff(attr(tree$terms, 'term.labels'), vars) + vars = c(missing_var, vars) } + if (flip) vars = rev(vars) part_coords = part_dt[ @@ -242,6 +252,7 @@ parttree.rpart = response = y_var, call = orig_call, na.action = orig_na.action, + flip = flip, raw_data = raw_data ) @@ -472,6 +483,7 @@ parttree.constparty = response = my, call = NULL, na.action = NULL, + flip = flip, raw_data = substitute(tree$data) # Or, partykit::model_frame_rpart? ) diff --git a/R/plot.R b/R/plot.R index babb849..7c0a410 100644 --- a/R/plot.R +++ b/R/plot.R @@ -35,11 +35,20 @@ plot.parttree = function( add = FALSE, ... ) { + dots = list(...) object = x - xvar = attr(object, "parttree")[["xvar"]] - yvar = attr(object, "parttree")[["yvar"]] - xrange = attr(object, "parttree")[["xrange"]] - yrange = attr(object, "parttree")[["yrange"]] + flip_data = isTRUE(attr(object, "parttree")[["flip"]]) + if (flip_data) { + xvar = attr(object, "parttree")[["yvar"]] + yvar = attr(object, "parttree")[["xvar"]] + xrange = attr(object, "parttree")[["yrange"]] + yrange = attr(object, "parttree")[["xrange"]] + } else { + xvar = attr(object, "parttree")[["xvar"]] + yvar = attr(object, "parttree")[["yvar"]] + xrange = attr(object, "parttree")[["xrange"]] + yrange = attr(object, "parttree")[["yrange"]] + } response = attr(object, "parttree")[["response"]] raw_data = attr(object, "parttree")[["raw_data"]] orig_call = attr(object, "parttree")[["call"]] @@ -109,10 +118,17 @@ plot.parttree = function( # Grab the plot corners and adjust the partition limits if (isTRUE(expand)) { corners = par("usr") - object$xmin[xmin_idxr] = corners[1] - object$xmax[xmax_idxr] = corners[2] - object$ymin[ymin_idxr] = corners[3] - object$ymax[ymax_idxr] = corners[4] + if (isTRUE(dots[["flip"]])) { + object$xmin[xmin_idxr] = corners[3] + object$xmax[xmax_idxr] = corners[4] + object$ymin[ymin_idxr] = corners[1] + object$ymax[ymax_idxr] = corners[2] + } else { + object$xmin[xmin_idxr] = corners[1] + object$xmax[xmax_idxr] = corners[2] + object$ymin[ymin_idxr] = corners[3] + object$ymax[ymax_idxr] = corners[4] + } } diff --git a/README.Rmd b/README.Rmd index 6160f87..9980bdb 100644 --- a/README.Rmd +++ b/README.Rmd @@ -16,6 +16,8 @@ knitr::opts_chunk$set( # parttree +[![CRAN status](https://www.r-pkg.org/badges/version/parttree)](https://CRAN.R-project.org/package=parttree) +[![R-universe status badge](https://grantmcdermott.r-universe.dev/badges/parttree)](https://grantmcdermott.r-universe.dev) [![R-CMD-check](https://github.com/grantmcdermott/parttree/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/grantmcdermott/parttree/actions/workflows/R-CMD-check.yaml) [![Docs](https://img.shields.io/badge/docs-homepage-blue.svg)](https://grantmcdermott.com/parttree/index.html) @@ -27,8 +29,14 @@ package provides visualization methods for both base R graphics (via ## Installation -This package is not on CRAN yet, but can be installed from -[r-universe](https://grantmcdermott.r-universe.dev/parttree): +The stable version of **parttree** is available on CRAN. + +``` r +install.packages("parttree") +``` + +Or, you can grab the latest development version from +[R-universe](https://grantmcdermott.r-universe.dev/parttree). ``` r install.packages("parttree", repos = "https://grantmcdermott.r-universe.dev") @@ -60,15 +68,15 @@ Customize your plots by passing additional arguments: ```{r quickstart2} plot( - fit_pt, - border = NA, # no partition borders - pch = 19, # filled points - alpha = 0.6, # point transparency - grid = TRUE, # background grid - palette = "classic", # new colour palette - xlab = "Topmost vertebra operated on", # custom x title - ylab = "Patient age (months)", # custom y title - main = "Tree predictions: Kyphosis recurrence" # custom title + fit_pt, + border = NA, # no partition borders + pch = 19, # filled points + alpha = 0.6, # point transparency + grid = TRUE, # background grid + palette = "classic", # new colour palette + xlab = "Topmost vertebra operated on", # custom x title + ylab = "Patient age (months)", # custom y title + main = "Tree predictions: Kyphosis recurrence" # custom title ) ``` diff --git a/README.md b/README.md index 3a261b3..eea7277 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,10 @@ +[![CRAN +status](https://www.r-pkg.org/badges/version/parttree)](https://CRAN.R-project.org/package=parttree) +[![R-universe status +badge](https://grantmcdermott.r-universe.dev/badges/parttree)](https://grantmcdermott.r-universe.dev) [![R-CMD-check](https://github.com/grantmcdermott/parttree/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/grantmcdermott/parttree/actions/workflows/R-CMD-check.yaml) [![Docs](https://img.shields.io/badge/docs-homepage-blue.svg)](https://grantmcdermott.com/parttree/index.html) @@ -16,8 +20,14 @@ package provides visualization methods for both base R graphics (via ## Installation -This package is not on CRAN yet, but can be installed from -[r-universe](https://grantmcdermott.r-universe.dev/parttree): +The stable version of **parttree** is available on CRAN. + +``` r +install.packages("parttree") +``` + +Or, you can grab the latest development version from +[R-universe](https://grantmcdermott.r-universe.dev/parttree). ``` r install.packages("parttree", repos = "https://grantmcdermott.r-universe.dev") @@ -55,15 +65,15 @@ Customize your plots by passing additional arguments: ``` r plot( - fit_pt, - border = NA, # no partition borders - pch = 19, # filled points - alpha = 0.6, # point transparency - grid = TRUE, # background grid - palette = "classic", # new colour palette - xlab = "Topmost vertebra operated on", # custom x title - ylab = "Patient age (months)", # custom y title - main = "Tree predictions: Kyphosis recurrence" # custom title + fit_pt, + border = NA, # no partition borders + pch = 19, # filled points + alpha = 0.6, # point transparency + grid = TRUE, # background grid + palette = "classic", # new colour palette + xlab = "Topmost vertebra operated on", # custom x title + ylab = "Patient age (months)", # custom y title + main = "Tree predictions: Kyphosis recurrence" # custom title ) ``` diff --git a/cran-comments.md b/cran-comments.md new file mode 100644 index 0000000..9dbf21e --- /dev/null +++ b/cran-comments.md @@ -0,0 +1,9 @@ +## R CMD check results + +0 errors | 0 warnings | 1 note + +* This is a resubmission addressing the issues from my first submission on Jan + 11. Specifically, I've added a more detailed Description and fixed a missing + \value{} tag in one of my exported functions. Apologies for missing these the + first time around. +* This is a new CRAN release. diff --git a/inst/tinytest/_tinysnapshot/iris_classification_flip.svg b/inst/tinytest/_tinysnapshot/iris_classification_flip.svg new file mode 100644 index 0000000..8698dc0 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/iris_classification_flip.svg @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + +Species +setosa +versicolor +virginica + + + + + + + +Petal.Width +Petal.Length + + + + + + + + +0.5 +1.0 +1.5 +2.0 +2.5 + + + + + + + + +1 +2 +3 +4 +5 +6 +7 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/test_rpart.R b/inst/tinytest/test_rpart.R index 8caee82..420cd6d 100644 --- a/inst/tinytest/test_rpart.R +++ b/inst/tinytest/test_rpart.R @@ -12,9 +12,15 @@ source('known_output/parttree_rpart_classification.R') # rpart rp = rpart::rpart(Species ~ Petal.Length + Petal.Width, data = iris) rp_pt = parttree(rp) + # plot method f = function() {plot(rp_pt)} expect_snapshot_plot(f, label = "iris_classification") + +# ## uncomment once tinyplot 0.3.0 comes out +# f = function() {plot(rp_pt, flip = TRUE)} +# expect_snapshot_plot(f, label = "iris_classification_flip") + # now strip attributes and compare data frames attr(rp_pt, "parttree") = NULL class(rp_pt) = "data.frame" @@ -40,6 +46,12 @@ if (require(partykit)) { # Comparison data source('known_output/parttree_rpart_classification_flip.R') rp_pt_flip = parttree(rp, flip = TRUE) + +# plot method +f = function() {plot(rp_pt_flip)} +expect_snapshot_plot(f, label = "iris_classification_flip") + +# now strip attributes and compare data frames attr(rp_pt_flip, "parttree") = NULL class(rp_pt_flip) = "data.frame" expect_equal(pt_cl_known_flip, rp_pt_flip) diff --git a/man/figures/README-quickstart-1.png b/man/figures/README-quickstart-1.png index bba8a5a..ef6a8a8 100644 Binary files a/man/figures/README-quickstart-1.png and b/man/figures/README-quickstart-1.png differ diff --git a/man/figures/README-quickstart2-1.png b/man/figures/README-quickstart2-1.png index e5c234e..711c798 100644 Binary files a/man/figures/README-quickstart2-1.png and b/man/figures/README-quickstart2-1.png differ diff --git a/man/figures/README-quickstart_gg-1.png b/man/figures/README-quickstart_gg-1.png index 0445a4f..6f623b1 100644 Binary files a/man/figures/README-quickstart_gg-1.png and b/man/figures/README-quickstart_gg-1.png differ diff --git a/man/geom_parttree.Rd b/man/geom_parttree.Rd index 8a99b1c..a1265b7 100644 --- a/man/geom_parttree.Rd +++ b/man/geom_parttree.Rd @@ -103,6 +103,9 @@ lists which parameters it can accept. \link[ggplot2:draw_key]{key glyphs}, to change the display of the layer in the legend. }} } +\value{ +A \code{\link[ggplot2]{ggplot}} layer. +} \description{ \code{geom_parttree()} is a simple wrapper around \code{\link[=parttree]{parttree()}} that takes a tree model object and then converts into an amenable data frame @@ -140,37 +143,39 @@ library(ggplot2) # ggplot2 must be installed/loaded separately library(parttree) # this package library(rpart) # decision trees - -### Simple decision tree (max of two predictor variables) +\dontshow{data.table::setDTthreads(2)} +# +## Simple decision tree (max of two predictor variables) iris_tree = rpart(Species ~ Petal.Length + Petal.Width, data=iris) -## Plot with original iris data only +# Plot with original iris data only p = ggplot(data = iris, aes(x = Petal.Length, y = Petal.Width)) + geom_point(aes(col = Species)) -## Add tree partitions to the plot (borders only) +# Add tree partitions to the plot (borders only) p + geom_parttree(data = iris_tree) -## Better to use fill and highlight predictions +# Better to use fill and highlight predictions p + geom_parttree(data = iris_tree, aes(fill = Species), alpha=0.1) -## To drop the black border lines (i.e. fill only) +# To drop the black border lines (i.e. fill only) p + geom_parttree(data = iris_tree, aes(fill = Species), col = NA, alpha = 0.1) - -### Example with plot orientation mismatch +# +## Example with plot orientation mismatch p2 = ggplot(iris, aes(x=Petal.Width, y=Petal.Length)) + geom_point(aes(col=Species)) -## Oops +# Oops p2 + geom_parttree(data = iris_tree, aes(fill=Species), alpha = 0.1) -## Fix with 'flip = TRUE' - +# Fix with 'flip = TRUE' +p2 + geom_parttree(data = iris_tree, aes(fill=Species), alpha = 0.1, flip = TRUE) -### Various front-end frameworks are also supported, e.g.: +# +## Various front-end frameworks are also supported, e.g.: # install.packages("parsnip") library(parsnip) @@ -182,10 +187,11 @@ iris_tree_parsnip = decision_tree() |> p + geom_parttree(data = iris_tree_parsnip, aes(fill=Species), alpha = 0.1) +# +## Trees with continuous independent variables are also supported. -### Trees with continuous independent variables are also supported. But you -### may need to adjust (or switch off) the fill legend to match the original -### data, e.g.: +# Note: you may need to adjust (or switch off) the fill legend to match the +# original data, e.g.: iris_tree_cont = rpart(Petal.Length ~ Sepal.Length + Petal.Width, data=iris) p3 = ggplot(data = iris, aes(x = Petal.Width, y = Sepal.Length)) + @@ -196,10 +202,10 @@ p3 = ggplot(data = iris, aes(x = Petal.Width, y = Sepal.Length)) + geom_point(aes(col = Petal.Length)) + theme_minimal() -## Legend scales don't quite match here: +# Legend scales don't quite match here: p3 -## Better to scale fill to the original data +# Better to scale fill to the original data p3 + scale_fill_continuous(limits = range(iris$Petal.Length)) } diff --git a/man/parttree.Rd b/man/parttree.Rd index 4839975..26b8c5a 100644 --- a/man/parttree.Rd +++ b/man/parttree.Rd @@ -24,7 +24,15 @@ regular data frame (default behavior) unless the user specifies \code{TRUE}.} \item{flip}{Logical. Should we flip the "x" and "y" variables in the return data frame? The default behaviour is for the first split variable in the tree to take the "y" slot, and any second split variable to take the "x" -slot. Setting to \code{TRUE} switches these around.} +slot. Setting to \code{TRUE} switches these around. + +\emph{Note:} This argument is primarily useful when it passed via +\link{geom_parttree} to ensure correct axes orientation as part of a \code{ggplot2} +visualization (see \link{geom_parttree} Examples). We do not expect users to +call \code{parttree(..., flip = TRUE)} directly. Similarly, to switch axes +orientation for the native (base graphics) \link{plot.parttree} method, we +recommend calling \code{plot(..., flip = TRUE)} rather than flipping the +underlying \code{parttree} object.} } \value{ A data frame comprising seven columns: the leaf node, its path, a @@ -39,7 +47,8 @@ leaf or terminal node) that can easily be plotted in 2-D coordinate space. } \examples{ library("parttree") - +\dontshow{data.table::setDTthreads(2)} +# ## rpart trees library("rpart") @@ -58,16 +67,17 @@ plot(rp_pt, border = NA) # customize further by passing extra options to (tiny)plot plot( rp_pt, - border = NA, # no partition borders - pch = 19, # filled points - alpha = 0.6, # point transparency - grid = TRUE, # background grid - palette = "classic", # new colour palette - xlab = "Topmost vertebra operated on", # custom x title - ylab = "Patient age (months)", # custom y title + border = NA, # no partition borders + pch = 16, # filled points + alpha = 0.6, # point transparency + grid = TRUE, # background grid + palette = "classic", # new colour palette + xlab = "Topmost vertebra operated on", # custom x title + ylab = "Patient age (months)", # custom y title main = "Tree predictions: Kyphosis recurrence" # custom title ) +# ## conditional inference trees from partyit library("partykit") @@ -79,10 +89,12 @@ plot(ct_pt, pch = 19, palette = "okabe", main = "ctree predictions: iris species rp2 = as.party(rp) parttree(rp2) +# ## various front-end frameworks are also supported, e.g. # tidymodels +# install.packages("parsnip") library(parsnip) decision_tree() |> @@ -94,6 +106,7 @@ decision_tree() |> # mlr3 (NB: use `keep_model = TRUE` for mlr3 learners) +# install.packages("mlr3") library(mlr3) task_iris = TaskClassif$new("iris", iris, target = "Species") diff --git a/man/plot.parttree.Rd b/man/plot.parttree.Rd index 29ab8d6..b1fa421 100644 --- a/man/plot.parttree.Rd +++ b/man/plot.parttree.Rd @@ -49,7 +49,8 @@ Provides a plot method for parttree objects. } \examples{ library("parttree") - +\dontshow{data.table::setDTthreads(2)} +# ## rpart trees library("rpart") @@ -68,16 +69,17 @@ plot(rp_pt, border = NA) # customize further by passing extra options to (tiny)plot plot( rp_pt, - border = NA, # no partition borders - pch = 19, # filled points - alpha = 0.6, # point transparency - grid = TRUE, # background grid - palette = "classic", # new colour palette - xlab = "Topmost vertebra operated on", # custom x title - ylab = "Patient age (months)", # custom y title + border = NA, # no partition borders + pch = 16, # filled points + alpha = 0.6, # point transparency + grid = TRUE, # background grid + palette = "classic", # new colour palette + xlab = "Topmost vertebra operated on", # custom x title + ylab = "Patient age (months)", # custom y title main = "Tree predictions: Kyphosis recurrence" # custom title ) +# ## conditional inference trees from partyit library("partykit") @@ -89,10 +91,12 @@ plot(ct_pt, pch = 19, palette = "okabe", main = "ctree predictions: iris species rp2 = as.party(rp) parttree(rp2) +# ## various front-end frameworks are also supported, e.g. # tidymodels +# install.packages("parsnip") library(parsnip) decision_tree() |> @@ -104,6 +108,7 @@ decision_tree() |> # mlr3 (NB: use `keep_model = TRUE` for mlr3 learners) +# install.packages("mlr3") library(mlr3) task_iris = TaskClassif$new("iris", iris, target = "Species") diff --git a/tests/tinytest.R b/tests/tinytest.R index 5aa0983..e4e7455 100644 --- a/tests/tinytest.R +++ b/tests/tinytest.R @@ -1,5 +1,6 @@ if ( requireNamespace("tinytest", quietly=TRUE) ){ + Sys.setenv("OMP_THREAD_LIMIT" = 2) # https://github.com/Rdatatable/data.table/issues/5658 tinytest::test_package("parttree") } diff --git a/vignettes/parttree-art.Rmd b/vignettes/parttree-art.Rmd index 5a91fc8..512a829 100644 --- a/vignettes/parttree-art.Rmd +++ b/vignettes/parttree-art.Rmd @@ -28,7 +28,6 @@ This vignette will show you how to implement the same basic ideas using **parttree** and a few friends. Here are the packages that we'll be using. - ```{r setup, message=FALSE} library(parttree) # This package library(rpart) # For decision trees @@ -38,6 +37,11 @@ library(imager) # Another image library, with some additional features op = par(mar = c(0,0,0,0)) # Remove plot margins ``` +```{r thread_control, echo=-1} +data.table::setDTthreads(2) +magick:::magick_threads(2) +``` + While the exact details will vary depending on the image at hand, the essential recipe for this type of art abstraction is as follows: diff --git a/vignettes/parttree-intro.Rmd b/vignettes/parttree-intro.Rmd index fe7c88b..b887205 100644 --- a/vignettes/parttree-intro.Rmd +++ b/vignettes/parttree-intro.Rmd @@ -38,6 +38,10 @@ data("penguins", package = "palmerpenguins") head(penguins) ``` +```{r thread_control, echo=-1} +data.table::setDTthreads(2) +``` + Dataset in hand, let's say that we are interested in predicting penguin _species_ as a function of 1) flipper length and 2) bill length. We could model this as a simple decision tree: @@ -84,7 +88,7 @@ ptree Speaking of visualization, underneath the hood `plot.parttree` calls the powerful -[**tinyplot**](https://grantmcdermott.com/tinyplot) +[**tinyplot**](https://grantmcdermott.com/tinyplot/) package. All of the latter's various customization arguments can be passed on to our `parttree` plot to make it look a bit nicer. For example: @@ -109,10 +113,10 @@ tree_cont |> ## Supported model classes -Alongside the [**rpart**](https://CRAN.R-project.org/web/package=rpart) model +Alongside the [**rpart**](https://CRAN.R-project.org/package=rpart) model objects that we have been working with thus far, **parttree** also supports decision trees created by the -[**partykit**](https://CRAN.R-project.org/web/package=partykit) package. Here we +[**partykit**](https://CRAN.R-project.org/package=partykit) package. Here we see how the latter's `ctree` (conditional inference tree) algorithm yields a slightly more sophisticated partitioning that the former's default. @@ -121,7 +125,7 @@ library(partykit) ctree(species ~ flipper_length_mm + bill_length_mm, data = penguins) |> parttree() |> - plot(pch = 19, palette = "classic", alpha = 0.5) + plot(pch = 16, palette = "classic", alpha = 0.5) ``` **parttree** also supports a variety of "frontend" modes that call