Skip to content

Commit

Permalink
Merge pull request #22 from grantmcdermott/cran
Browse files Browse the repository at this point in the history
CRAN v0.1.0 submission
  • Loading branch information
grantmcdermott authored Jan 16, 2025
2 parents 72b179b + 83e1f46 commit c4f2428
Show file tree
Hide file tree
Showing 22 changed files with 460 additions and 112 deletions.
2 changes: 2 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@
^docs$
^pkgdown$
^SCRATCH$
^cran-comments\.md$
^CRAN-SUBMISSION$
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
docs
inst/doc
SCRATCH/
**/*/.DS_Store
.DS_Store
3 changes: 3 additions & 0 deletions CRAN-SUBMISSION
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Version: 0.1.0
Date: 2025-01-15 02:46:58 UTC
SHA: f5833ac6b32f3e95843b0dc9c44aff7e1b4c1dc3
19 changes: 13 additions & 6 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -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,
Expand Down
10 changes: 5 additions & 5 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -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)

Expand Down
38 changes: 21 additions & 17 deletions R/geom_parttree.R
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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)) +
Expand All @@ -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.
Expand Down
42 changes: 27 additions & 15 deletions R/parttree.R
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -26,7 +34,8 @@
#' @export
#' @examples
#' library("parttree")
#'
#' \dontshow{data.table::setDTthreads(2)}
#' #
#' ## rpart trees
#'
#' library("rpart")
Expand All @@ -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")
Expand All @@ -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() |>
Expand All @@ -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")
Expand Down Expand Up @@ -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[
Expand Down Expand Up @@ -242,6 +252,7 @@ parttree.rpart =
response = y_var,
call = orig_call,
na.action = orig_na.action,
flip = flip,
raw_data = raw_data
)

Expand Down Expand Up @@ -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?
)

Expand Down
32 changes: 24 additions & 8 deletions R/plot.R
Original file line number Diff line number Diff line change
Expand Up @@ -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"]]
Expand Down Expand Up @@ -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]
}
}


Expand Down
30 changes: 19 additions & 11 deletions README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ knitr::opts_chunk$set(
# parttree <a href='https://grantmcdermott.com/parttree/'><img src='man/figures/hex.png' align="right" width="120" /></a>

<!-- badges: start -->
[![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)
<!-- badges: end -->
Expand All @@ -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")
Expand Down Expand Up @@ -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
)
```

Expand Down
Loading

0 comments on commit c4f2428

Please sign in to comment.