From 0ad7e66bda34c3c926b631a3c059d833a1b02fdf Mon Sep 17 00:00:00 2001 From: Adeel Khan Date: Fri, 17 Mar 2017 16:56:52 -0500 Subject: [PATCH 1/2] Tooltip for values --- DESCRIPTION | 4 +- R/collapsibleTree.R | 30 +++++++++++-- R/collapsibleTreeSummary.R | 35 +++++++++------ README.Rmd | 10 ++++- README.md | 14 ++++-- docs/index.Rmd | 2 +- docs/index.html | 26 +++++------ inst/examples/01rmd/Example01.Rmd | 2 +- inst/examples/01rmd/Example01.html | 22 +++++----- inst/examples/03shiny/app.R | 17 ++++---- inst/htmlwidgets/collapsibleTree.js | 55 +++++++++++++++++++++--- inst/htmlwidgets/collapsibleTree.yaml | 2 +- inst/htmlwidgets/lib/collapsibleTree.css | 13 ++++++ man/collapsibleTree.Rd | 11 ++++- man/collapsibleTreeSummary.Rd | 6 ++- 15 files changed, 179 insertions(+), 70 deletions(-) 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 38264fe..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,8 +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) + 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 21fc286..0217952 100644 --- a/R/collapsibleTreeSummary.R +++ b/R/collapsibleTreeSummary.R @@ -27,12 +27,13 @@ #' 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} #' #' @examples #' # Color in by number of children -#' collapsibleTree(warpbreaks, c("wool", "tension", "breaks"), maxPercent = 50) +#' collapsibleTreeSummary(warpbreaks, c("wool", "tension", "breaks"), maxPercent = 50) #' #' # Color in by the value of breaks and use the terrain_hcl gradient #' collapsibleTreeSummary( @@ -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,27 +95,33 @@ 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, ...)) node$Do(function(self) { - self$fill <- fill[self$WeightOfParent+1] - # color in high values - if(!length(self$fill)) self$fill <- fill[maxPercent] # color in the root - if(is.na(self$fill)) self$fill <- fill[maxPercent] + if(!length(self$WeightOfParent)) self$fill <- fill[maxPercent] + # color in high values + else if(self$WeightOfParent >= maxPercent) self$fill <- fill[maxPercent] + # negative percents are just going to be treated like 0 for now + else if(self$WeightOfParent < 0) self$fill <- fill[1] + # all other cases + else self$fill <- fill[self$WeightOfParent+1] }) + # 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) + data.tree::ToListExplicit(node, unname = TRUE, keepOnly = jsonFields) ) # pass the data and options using 'x' diff --git a/README.Rmd b/README.Rmd index 0b483a7..bfc3935 100644 --- a/README.Rmd +++ b/README.Rmd @@ -14,11 +14,13 @@ 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! -If you're using Shiny, you can bind the most recently clicked node to a Shiny input, allowing for easier interaction with complex nested objects. The input will return a named list containing the most recently selected node, as well as all of its parents. See the Shiny example for more info. +If you're using Shiny, you can bind the most recently clicked node to a Shiny input, allowing for easier interaction with complex nested objects. The input will return a named list containing the most recently selected node, as well as all of its parents. See the Shiny interaction example for more info. ### Installation @@ -82,7 +84,11 @@ collapsibleTreeSummary( An interactive Shiny demo is also included. For example, you could use the collapsibleTree htmlwidget to select a portion of a larger categorical dataset, with your filter being as deep or shallow as you'd prefer. ```{r eval=FALSE} +# Basic Shiny Interaction shiny::runApp(paste0(system.file(package="collapsibleTree"),"/examples/02shiny")) + +# Interactive Gradient Mapping +shiny::runApp(paste0(system.file(package="collapsibleTree"),"/examples/03shiny")) ``` ### Test Results diff --git a/README.md b/README.md index 55ef1de..2ed1aae 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,12 @@ -Overview --------- +collapsibleTree 0.1.2 +--------------------- + +### 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! -If you're using Shiny, you can bind the most recently clicked node to a Shiny input, allowing for easier interaction with complex nested objects. The input will return a named list containing the most recently selected node, as well as all of its parents. See the Shiny example for more info. +If you're using Shiny, you can bind the most recently clicked node to a Shiny input, allowing for easier interaction with complex nested objects. The input will return a named list containing the most recently selected node, as well as all of its parents. See the Shiny interaction example for more info. ### Installation @@ -68,7 +70,11 @@ collapsibleTreeSummary( An interactive Shiny demo is also included. For example, you could use the collapsibleTree htmlwidget to select a portion of a larger categorical dataset, with your filter being as deep or shallow as you'd prefer. ``` r +# Basic Shiny Interaction shiny::runApp(paste0(system.file(package="collapsibleTree"),"/examples/02shiny")) + +# Interactive Gradient Mapping +shiny::runApp(paste0(system.file(package="collapsibleTree"),"/examples/03shiny")) ``` ### Test Results @@ -76,7 +82,7 @@ shiny::runApp(paste0(system.file(package="collapsibleTree"),"/examples/02shiny") ``` r library(collapsibleTree) date() -#> [1] "Thu Mar 16 16:28:39 2017" +#> [1] "Fri Mar 17 10:55:43 2017" testthat::test_dir("tests/testthat") #> Basic functionality: diff --git a/docs/index.Rmd b/docs/index.Rmd index d0abfe9..3e79909 100644 --- a/docs/index.Rmd +++ b/docs/index.Rmd @@ -109,7 +109,7 @@ 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 %>% diff --git a/docs/index.html b/docs/index.html index 85dd095..57acc18 100644 --- a/docs/index.html +++ b/docs/index.html @@ -11,7 +11,7 @@ - + Collapsible Tree Example: R Markdown @@ -26,8 +26,8 @@ - - + +