Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add d2b and handle pre-summed trees #63

Merged
merged 11 commits into from
Feb 25, 2018
7 changes: 6 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Package: sunburstR
Type: Package
Title: 'Htmlwidget' for 'Kerry Rodden' 'd3.js' Sequence Sunburst
Version: 2.0.0
Date: 2018-02-19
Date: 2018-02-25
Authors@R: c(
person(
"Mike", "Bostock"
Expand All @@ -14,6 +14,11 @@ Authors@R: c(
, role = c("aut", "cph")
, comment = "sequences library in htmlwidgets/lib, https://gist.github.com/kerryrodden/7090426"
),
person(
"Kevin", "Warne"
, role = c("aut", "cph")
, comment = "d2b sunburst library in htmlwidgets/lib, https://github.com/d2bjs/d2b"
),
person(
"Kent", "Russell"
, role = c("aut", "cre")
Expand Down
3 changes: 3 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

export(add_shiny)
export(renderSunburst)
export(renderSund2b)
export(sunburst)
export(sunburstOutput)
export(sund2b)
export(sund2bOutput)
import(htmltools)
import(htmlwidgets)
4 changes: 3 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# sunburstR 2.0.0

* fix to correctly handle pre-summed trees, like `treemap::treemap` to not double count the aggregate (see (issue)[https://github.com/timelyportfolio/sunburstR/issues/60#issuecomment-365751989])
* add `sumNodes` argument to correctly handle pre-summed trees, like `treemap::treemap` to not double count the aggregate (see (issue)[https://github.com/timelyportfolio/sunburstR/issues/62])

* add `sund2b()` htmlwidget for a [d2b](http://www.d2bjs.org/) sunburst chart

* allow turning off the legend with argument `legend = FALSE` (see [issue](https://github.com/timelyportfolio/sunburstR/issues/61))

Expand Down
121 changes: 121 additions & 0 deletions R/d2b.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#' Sunburst Using 'd2b'
#'
#' Create interactive sunburst chart with the 'd2b' charting library.
#'
#' @param data data in csv source,target form or in
#' nested d3 JSON hierarchy with `{name:..., children:[];}`. \code{list}, \code{character},
#' or \code{connection} data will be assumed to be \code{JSON}.
#' \code{data.frame} data will be assumed to be \code{csvdata} and converted
#' to \code{JSON} by \code{sunburstR:::csv_to_hier()}.
#' @param colors \code{vector} of strings representing colors as hexadecimal for
#' manual colors. If you want precise control of colors, supply a \code{list}
#' with \code{range} and/or \code{domain}. For advanced customization, supply
#' a JavaScript \code{function}.
#' @param valueField \code{character} for the field to use to calculate size. The default
#' value is \code{"size"}.
#' @param height,width height and width of sunburst htmlwidget containing div
#' specified in any valid \code{CSS} size unit.
#' @param elementId string id as a valid \code{CSS} element id.
#'
#' @example inst/examples/example_sund2b.R
#'
#' @import htmlwidgets
#'
#' @export
sund2b <- function(
data = NULL,
colors = NULL,
valueField = "size",
width = NULL, height = NULL, elementId = NULL
) {


if(is.null(data)) stop("please provide data",call.=FALSE)

# accept JSON string as data
if( inherits(data,c("character","connection")) ){
data = jsonlite::toJSON(
jsonlite::fromJSON( data )
, auto_unbox = TRUE
, dataframe = "rows"
)
}
# accept list as data
if( inherits(data, "list") ) {
data = jsonlite::toJSON(
data
, auto_unbox = TRUE
, dataframe = "rows"
)
}
# accept data.frame as data
# and convert to JSON with csv_to_hier
# this conversion should be more robust than
# the JavaScript converter
if( inherits(data, "data.frame") ) {
data = csv_to_hier(data)
}

# forward options using x
x = list(
data = data,
options = list(
colors = colors,
valueField = valueField
)
)

# create widget
htmlwidgets::createWidget(
name = 'sund2b',
x,
width = width,
height = height,
package = 'sunburstR',
elementId = elementId,
dependencies = list(
d3r::d3_dep_v4(),
d2b_dep()
)
)
}

#' Shiny bindings for d2b
#'
#' Output and render functions for using d2b within Shiny
#' applications and interactive Rmd documents.
#'
#' @param outputId output variable to read from
#' @param width,height Must be a valid CSS unit (like \code{'100\%'},
#' \code{'400px'}, \code{'auto'}) or a number, which will be coerced to a
#' string and have \code{'px'} appended.
#' @param expr An expression that generates a d2b
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
#' is useful if you want to save an expression in a variable.
#'
#' @name d2b-shiny
#'
#' @export
sund2bOutput <- function(outputId, width = '100%', height = '400px'){
htmlwidgets::shinyWidgetOutput(outputId, 'sund2b', width, height, package = 'sunburstR')
}

#' @rdname d2b-shiny
#' @export
renderSund2b <- function(expr, env = parent.frame(), quoted = FALSE) {
if (!quoted) { expr <- substitute(expr) } # force quoted
htmlwidgets::shinyRenderWidget(expr, sund2bOutput, env, quoted = TRUE)
}

#' @keywords internal
d2b_dep <- function() {
htmltools::htmlDependency(
name = "d2b",
version = "0.5.1",
src = c(
file = system.file("htmlwidgets/lib/d2b", package="sunburstR")
),
script = "d2b.js"
)
}
5 changes: 5 additions & 0 deletions R/sunburst.R
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
#' and \code{r} is the radius all in \code{px}.
#' @param sortFunction \code{\link[htmlwidgets]{JS}} function to sort the slices.
#' The default sort is by size.
#' @param sumNodes \code{logical} to sum non-leaf nodes. The default
#' \code{sumNodes = TRUE} assumes that the user has not already
#' calculated a sum.
#' @param withD3 \code{logical} to include d3 dependency from \code{d3r}. As of
#' version 1.0, sunburst uses a standalone JavaScript build and will
#' not include the entire d3 in the global/window namespace. To include
Expand Down Expand Up @@ -65,6 +68,7 @@ sunburst <- function(
, breadcrumb = list()
, legend = list()
, sortFunction = NULL
, sumNodes = TRUE
, withD3 = FALSE
, width = NULL
, height = NULL
Expand Down Expand Up @@ -122,6 +126,7 @@ sunburst <- function(
,breadcrumb = breadcrumb
,legend = legend
,sortFunction = sortFunction
,sumNodes = sumNodes
)
)

Expand Down
60 changes: 60 additions & 0 deletions inst/examples/example_sund2b.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
\dontrun{

# The sund2b() API mirrors sunburst() with fewer arguments.

library(sunburstR)

# use a sample of the sequences csv data
sequences <- read.csv(
system.file("examples/visit-sequences.csv",package="sunburstR")
,header = FALSE
,stringsAsFactors = FALSE
)[1:200,]

# create a d2b sunburst
sund2b(sequences)

# change the colors
# using d3.js categorical color scheme
sund2b(
sequences,
colors = htmlwidgets::JS("d3.scaleOrdinal(d3.schemeCategory20b)")
)
# using RColorBrewer palette
sund2b(
sequences,
colors = list(range = RColorBrewer::brewer.pal(9, "Set3"))
)
# using a color column from the R dataset
# treemap has an amazing treecolors ability
library(treemap)
library(d3r)
rhd <- random.hierarchical.data()
tm <- treemap(
rhd,
index = paste0("index", 1:3),
vSize = "x",
draw = FALSE
)$tm
sund2b(
d3_nest(tm, value_cols = colnames(tm)[-(1:3)]),
colors = htmlwidgets::JS(
# yes this is a little different, so please pay attention
# "function(d) {return d.color}" will not work
"function(name, d){return d.color || '#ccc';}"
),
valueField = "vSize"
)


# use sund2b in Shiny
library(shiny)
ui <- sund2bOutput("sun")
server <- function(input, output, session) {
output$sun <- renderSund2b({
sund2b(sequences)
})
}
shinyApp(ui, server)

}
27 changes: 27 additions & 0 deletions inst/htmlwidgets/lib/d2b/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Copyright 2015-2018, Kevin Warne
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51 changes: 51 additions & 0 deletions inst/htmlwidgets/lib/d2b/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# D2B: Data to DashBoards

<a href = 'http://d2bjs.org'><img width = '250' src = 'https://raw.githubusercontent.com/d2bjs/d2b/master/logo/logo.png'></a>

A reusable d3-based chart library.

## Installing

If you use NPM, `npm install d2b`. Otherwise, you can download the latest build [here](https://github.com/d2bjs/d2b) or install it via [CDN](https://unpkg.com/d2b/build/d2b.min.js).

## API Reference

You can see the d2b API references [here](http://docs.d2bjs.org).

## Optional Dependencies

- [font awesome icons](http://fontawesome.io/get-started/): Many of the charts use this icon set.

- [d3-sankey](https://github.com/d3/d3-sankey): Used by the d2b [sankey chart](./charts/sankey.md) and [sankey svg](./svg/sankey.md). If using NPM this dependency will automatically be included.

- [d3-interpolate-path](https://github.com/pbeshai/d3-interpolate-path): Used by the d2b `v > 0.0.41` [line svg](./svg/line.md) and [area svg](./svg/area.md) for smoother interpolation. This dependency is optional, by default d3's path interpolation will be used. If installing with NPM this dependency will automatically be included.

## Examples

You can try out many d2b live code examples [here](http://d2bjs.org).

If you are using Vue.js there is a [vue-d2b](https://github.com/d2bjs/vue-d2b) plugin that makes using d2b even easier.

<figure><figcaption style = 'text-align:center;'>d2b.chartAxis()</figcaption><img src="./docs/gifs/chart-axis.gif" alt="Axis Chart" width = '100%'/></figure><br>

<figure><figcaption style = 'text-align:center;'>d2b.chartSunburst()</figcaption><img src="./docs/gifs/chart-sunburst.gif" alt="Sunburst Chart" width = '100%'/></figure><br>

<figure><figcaption style = 'text-align:center;'>d2b.chartSankey()</figcaption><img src="./docs/gifs/chart-sankey.gif" alt="Sankey Chart" width = '100%'/></figure><br>

<figure><figcaption style = 'text-align:center;'>d2b.chartPie()</figcaption><img src="./docs/gifs/chart-pie.gif" alt="Pie Chart" width = '100%'/></figure><br>

### Some examples of mix and match d2b.chartAxis() generators.

<br />

<figure><figcaption style = 'text-align:center;'>d2b.svgBoxPlot()</figcaption><img src="./docs/gifs/box-plot-svg-transition.gif" alt="Svg Box Plot" width = '100%'/></figure><br>

<figure><figcaption style = 'text-align:center;'>d2b.svgBubblePack()</figcaption><img src="./docs/gifs/bubble-pack-svg-transition.gif" alt="Svg Bubble Pack" width = '100%'/></figure><br>

<figure><figcaption style = 'text-align:center;'>d2b.svgArea()</figcaption><img src="./docs/gifs/area-svg-transition.gif" alt="Svg Area" width = '100%'/></figure><br>

<figure><figcaption style = 'text-align:center;'>d2b.svgLine()</figcaption><img src="./docs/gifs/line-svg-transition.gif" alt="Svg Line" width = '100%'/></figure><br>

<figure><figcaption style = 'text-align:center;'>d2b.svgScatter()</figcaption><img src="./docs/gifs/scatter-svg-transition.gif" alt="Svg Scatter" width = '100%'/></figure><br>

<figure><figcaption style = 'text-align:center;'>d2b.svgBar()</figcaption><img src="./docs/gifs/bar-svg-transition.gif" alt="Svg Bar" width = '100%'/></figure><br>
Loading