diff --git a/DESCRIPTION b/DESCRIPTION index 54fe676..c2954fc 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,12 +1,13 @@ Package: collapsibleTree Type: Package Title: Interactive Collapsible Tree Diagrams using D3.js -Version: 0.1.2 +Version: 0.1.3 Author: Adeel Khan Maintainer: Adeel Khan Description: Interactive Reingold–Tilford tree diagram created using D3.js, where every node can be expanded and collapsed by clicking on it. License: GPL (>= 3) URL: https://github.com/AdeelK93/collapsibleTree +BugReports: https://github.com/AdeelK93/collapsibleTree/issues Encoding: UTF-8 Depends: R (>= 3.0.0) @@ -20,4 +21,5 @@ RoxygenNote: 6.0.1 Suggests: colorspace, RColorBrewer, + dplyr, testthat diff --git a/R/collapsibleTree.R b/R/collapsibleTree.R index ca86a7d..87db4fa 100644 --- a/R/collapsibleTree.R +++ b/R/collapsibleTree.R @@ -12,6 +12,9 @@ #' along with all of its parents. #' @param width width in pixels (optional, defaults to automatic sizing) #' @param height height in pixels (optional, defaults to automatic sizing) +#' @param attribute numeric column not listed in hierarchy that will be used +#' for tooltips, if applicable. Defaults to 'leafCount', +#' which is the cumulative count of a node's children #' @param fill either a single color or a vector of colors the same length #' as the number of nodes. By default, vector should be ordered by level, #' such that the root color is described first, then all the children's colors, @@ -21,6 +24,7 @@ #' \code{FALSE}: Filling by order; will assign fill values to nodes horizontally. #' @param linkLength length of the horizontal links that connect nodes in pixels #' @param fontSize font size of the label text in pixels +#' @param tooltip tooltip shows the node's label and attribute value. #' #' @examples #' collapsibleTree(warpbreaks, c("wool", "tension", "breaks")) @@ -47,12 +51,16 @@ #' @import htmlwidgets #' @importFrom data.tree ToListExplicit #' @importFrom data.tree as.Node +#' @importFrom data.tree Traverse +#' @importFrom data.tree Do +#' @importFrom data.tree Aggregate #' @importFrom stats complete.cases #' @export collapsibleTree <- function(df, hierarchy, root = deparse(substitute(df)), inputId = NULL, width = NULL, height = NULL, - fill = "lightsteelblue", fillByLevel = TRUE, - linkLength = 180, fontSize = 10) { + attribute = "leafCount", fill = "lightsteelblue", + fillByLevel = TRUE, linkLength = 180, + fontSize = 10, tooltip = FALSE) { # preserve this name before evaluating df root <- root @@ -63,14 +71,17 @@ collapsibleTree <- function(df, hierarchy, root = deparse(substitute(df)), if(!is.character(fill)) stop("fill must be a character vector") if(length(hierarchy) <= 1) stop("hierarchy vector must be greater than length 1") if(!all(hierarchy %in% colnames(df))) stop("hierarchy column names are incorrect") + if(!(attribute %in% c(colnames(df), "leafCount"))) stop("attribute column name is incorrect") if(sum(complete.cases(df[hierarchy])) != nrow(df)) stop("NAs in data frame") # create a list that contains the options options <- list( hierarchy = hierarchy, input = inputId, + attribute = attribute, linkLength = linkLength, - fontSize = fontSize + fontSize = fontSize, + tooltip = tooltip ) # the hierarchy that will be used to create the tree @@ -93,9 +104,19 @@ collapsibleTree <- function(df, hierarchy, root = deparse(substitute(df)), options$fill <- fill } + # only necessary to perform these calculations if there is a tooltip + if(tooltip) { + # traverse down the tree and compute the weights of each node for the tooltip + t <- data.tree::Traverse(node, "pre-order") + data.tree::Do(t, function(x) { + x$WeightOfNode <- data.tree::Aggregate(x, attribute, sum) + }) + jsonFields <- c("fill", "WeightOfNode") + } else jsonFields <- "fill" + # keep only the fill attribute in the final JSON json <- htmlwidgets:::toJSON( - data.tree::ToListExplicit(node, unname = TRUE, keepOnly = "fill") + data.tree::ToListExplicit(node, unname = TRUE, keepOnly = jsonFields) ) # pass the data and options using 'x' diff --git a/R/collapsibleTreeSummary.R b/R/collapsibleTreeSummary.R index 4cca3a1..0217952 100644 --- a/R/collapsibleTreeSummary.R +++ b/R/collapsibleTreeSummary.R @@ -27,6 +27,7 @@ #' children. #' @param linkLength length of the horizontal links that connect nodes in pixels #' @param fontSize font size of the label text in pixels +#' @param tooltip tooltip shows the node's label and attribute value. #' @param ... other arguments passed on to \code{fillFun}, such declaring a #' palette for \link[RColorBrewer]{brewer.pal} #' @@ -55,9 +56,9 @@ #' @export collapsibleTreeSummary <- function(df, hierarchy, root = deparse(substitute(df)), inputId = NULL, width = NULL, height = NULL, - attribute = "leafCount", - fillFun = colorspace::heat_hcl, maxPercent = 25, - linkLength = 180, fontSize = 10, ...) { + attribute = "leafCount", fillFun = colorspace::heat_hcl, + maxPercent = 25, linkLength = 180, + fontSize = 10, tooltip = TRUE, ...) { # preserve this name before evaluating df root <- root @@ -75,8 +76,10 @@ collapsibleTreeSummary <- function(df, hierarchy, root = deparse(substitute(df)) options <- list( hierarchy = hierarchy, input = inputId, + attribute = attribute, linkLength = linkLength, - fontSize = fontSize + fontSize = fontSize, + tooltip = tooltip ) # the hierarchy that will be used to create the tree @@ -92,14 +95,14 @@ collapsibleTreeSummary <- function(df, hierarchy, root = deparse(substitute(df)) # traverse down the tree and compute the weights of each node t <- data.tree::Traverse(node, "pre-order") data.tree::Do(t, function(x) { - x$Weight <- data.tree::Aggregate(x, attribute, sum) + x$WeightOfNode <- data.tree::Aggregate(x, attribute, sum) }) data.tree::Do(t, function(x) { - x$WeightOfParent <- round(100*(x$Weight / x$parent$Weight)) + x$WeightOfParent <- round(100*(x$WeightOfNode / x$parent$WeightOfNode)) }) # Sort the tree by weight - data.tree::Sort(node, "Weight", recursive = TRUE, decreasing = TRUE) + data.tree::Sort(node, "WeightOfNode", recursive = TRUE, decreasing = TRUE) # vector of colors to choose from, up to the maxPercent fill <- rev(fillFun(maxPercent, ...)) @@ -114,9 +117,11 @@ collapsibleTreeSummary <- function(df, hierarchy, root = deparse(substitute(df)) else self$fill <- fill[self$WeightOfParent+1] }) - # keep only the fill attribute in the final JSON + # keep only the JSON fields that are necessary + if(tooltip) jsonFields <- c("fill", "WeightOfNode") + else jsonFields <- "fill" json <- htmlwidgets:::toJSON( - data.tree::ToListExplicit(node, unname = TRUE, keepOnly = "fill") + data.tree::ToListExplicit(node, unname = TRUE, keepOnly = jsonFields) ) # pass the data and options using 'x' diff --git a/README.Rmd b/README.Rmd index 55ad5d1..6dc836c 100644 --- a/README.Rmd +++ b/README.Rmd @@ -14,7 +14,9 @@ knitr::opts_chunk$set( ) ``` -## Overview +## collapsibleTree `r packageVersion("collapsibleTree")` + +### Overview collapsibleTree is an R htmlwidget that allows you to create interactive collapsible Reingold–Tilford tree diagram using D3.js. Turn your data frame into a hierarchical visualization without worrying about nested lists or JSON objects! @@ -66,7 +68,7 @@ collapsibleTree( [![Collapsible Tree Colored](README-example-2.png)](https://adeelk93.github.io/collapsibleTree/) -Gradients can be mapped to a column in the data frame to help visualize relative weightings of nodes. +Gradients can be mapped to a column in the data frame to help visualize relative weightings of nodes. Node weighting can also be mapped to a tooltip. ```{r eval=FALSE} collapsibleTreeSummary( diff --git a/README.md b/README.md index 80fa6d7..1124d52 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ -Overview --------- +collapsibleTree 0.1.3 +--------------------- + +### Overview collapsibleTree is an R htmlwidget that allows you to create interactive collapsible Reingold–Tilford tree diagram using D3.js. Turn your data frame into a hierarchical visualization without worrying about nested lists or JSON objects! @@ -52,7 +54,7 @@ collapsibleTree( [![Collapsible Tree Colored](README-example-2.png)](https://adeelk93.github.io/collapsibleTree/) -Gradients can be mapped to a column in the data frame to help visualize relative weightings of nodes. +Gradients can be mapped to a column in the data frame to help visualize relative weightings of nodes. Node weighting can also be mapped to a tooltip. ``` r collapsibleTreeSummary( @@ -80,7 +82,7 @@ shiny::runApp(paste0(system.file(package="collapsibleTree"),"/examples/03shiny") ``` r library(collapsibleTree) date() -#> [1] "Fri Mar 17 00:09:20 2017" +#> [1] "Fri Mar 17 18:07:46 2017" testthat::test_dir("tests/testthat") #> Basic functionality: diff --git a/docs/index.Rmd b/docs/index.Rmd index d0abfe9..aa53407 100644 --- a/docs/index.Rmd +++ b/docs/index.Rmd @@ -109,16 +109,16 @@ collapsibleTree( Using `dplyr` and `colorspace` again, we can create a new column in the source data frame for the total number of countries on each continent, and map that column to the fill gradient of the nodes. `collapsibleTreeSummary` serves as a convenience function around `collapsibleTree`. -Looking at this chart, you can tell that Africa has roughly the same number of countries as Europe, and that most countries are... countries. +Looking at this chart, you can tell that Africa has roughly the same number of countries as Europe, and that most countries are... countries. Hovering over the node can confirm this fact. ```{r plotsummary, warning=FALSE} df %>% group_by(continent, type) %>% - summarise(numberOfCountries = n()) %>% + summarise(`Number of Countries` = n()) %>% collapsibleTreeSummary( hierarchy = c("continent", "type"), root = "Geography", width = 800, - attribute = "numberOfCountries" + attribute = "Number of Countries" ) ``` diff --git a/docs/index.html b/docs/index.html index 9267200..c61ae79 100644 --- a/docs/index.html +++ b/docs/index.html @@ -25,9 +25,9 @@ - - - + + +