├── .github ├── .gitignore └── workflows │ └── R-CMD-check.yaml ├── vignettes ├── .gitignore └── sunburst-2-0-0.Rmd ├── LICENSE ├── inst ├── examples │ ├── .gitignore │ ├── example_sort.R │ ├── example_sund2bTooltip.R │ ├── example_sund2bBreadcrumb.R │ ├── example_treemap.R │ ├── example_TraMineR.R │ ├── example_calendar.R │ ├── example_shiny.R │ ├── example_ngram.R │ ├── example_flexdashboard.Rmd │ ├── example_sund2b.R │ ├── example_replicate.R │ ├── example_baseball_shiny.R │ ├── example_baseball_shiny.Rmd │ └── example_baseball.Rmd └── htmlwidgets │ ├── lib │ ├── d2b │ │ ├── d2b_custom.css │ │ ├── LICENSE │ │ ├── README.md │ │ └── package.json │ └── sequences │ │ ├── sequences.css │ │ └── README.md │ ├── sund2b.yaml │ ├── sunburst.yaml │ └── sund2b.js ├── .gitignore ├── javascript ├── .gitignore ├── global │ └── htmlwidgets.js ├── src │ └── rebind.js ├── package.json ├── index.js └── package-lock.json ├── tests ├── testthat.R └── testthat │ ├── test-basic.R │ └── test-conversion.R ├── docs ├── reference │ ├── Rplot001.png │ ├── libs │ │ └── sequences-0.1 │ │ │ ├── sequences.css │ │ │ └── README.md │ ├── index.html │ ├── d2b-shiny.html │ └── sunburst-shiny.html ├── articles │ ├── sunburst-2-0-0_files │ │ ├── d2b-0.5.1 │ │ │ ├── d2b_custom.css │ │ │ ├── LICENSE │ │ │ ├── package.json │ │ │ └── README.md │ │ ├── d2b-1.0.9 │ │ │ ├── d2b_custom.css │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ └── package.json │ │ ├── header-attrs-2.10 │ │ │ └── header-attrs.js │ │ ├── accessible-code-block-0.0.1 │ │ │ └── empty-anchor.js │ │ ├── sequences-0.1 │ │ │ ├── sequences.css │ │ │ └── README.md │ │ ├── sund2b-binding-2.1.1 │ │ │ └── sund2b.js │ │ ├── sund2b-binding-2.1.2 │ │ │ └── sund2b.js │ │ ├── sund2b-binding-2.1.3 │ │ │ └── sund2b.js │ │ ├── sund2b-binding-2.1.4 │ │ │ └── sund2b.js │ │ ├── sund2b-binding-2.1.6 │ │ │ └── sund2b.js │ │ └── sund2b-binding-2.1.8 │ │ │ └── sund2b.js │ └── index.html ├── pkgdown.yml ├── link.svg ├── sitemap.xml ├── bootstrap-toc.css ├── docsearch.js ├── pkgdown.js ├── LICENSE-text.html ├── bootstrap-toc.js ├── 404.html ├── CONDUCT.html └── authors.html ├── .Rbuildignore ├── CRAN-RELEASE ├── NAMESPACE ├── sunburstR.Rproj ├── cran-comments.md ├── R ├── converters.R ├── shiny.R └── d2b.R ├── man ├── d2b-shiny.Rd ├── sunburst-shiny.Rd ├── sund2bBreadcrumb.Rd ├── sund2bTooltip.Rd ├── add_shiny.Rd └── sund2b.Rd ├── CONDUCT.md ├── README.md ├── DESCRIPTION └── NEWS.md /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2017 2 | COPYRIGHT HOLDER: Kent Russell 3 | -------------------------------------------------------------------------------- /inst/examples/.gitignore: -------------------------------------------------------------------------------- 1 | example_flexdashboard.html 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | inst/doc 5 | -------------------------------------------------------------------------------- /javascript/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | node_modules 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /javascript/global/htmlwidgets.js: -------------------------------------------------------------------------------- 1 | export default window.HTMLWidgets; 2 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(sunburstR) 3 | 4 | test_check("sunburstR") 5 | -------------------------------------------------------------------------------- /inst/htmlwidgets/lib/d2b/d2b_custom.css: -------------------------------------------------------------------------------- 1 | .d2b-breadcrumb-icon:after { 2 | box-sizing: initial; 3 | } 4 | -------------------------------------------------------------------------------- /docs/reference/Rplot001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timelyportfolio/sunburstR/HEAD/docs/reference/Rplot001.png -------------------------------------------------------------------------------- /docs/articles/sunburst-2-0-0_files/d2b-0.5.1/d2b_custom.css: -------------------------------------------------------------------------------- 1 | .d2b-breadcrumb-icon:after { 2 | box-sizing: initial; 3 | } 4 | -------------------------------------------------------------------------------- /docs/articles/sunburst-2-0-0_files/d2b-1.0.9/d2b_custom.css: -------------------------------------------------------------------------------- 1 | .d2b-breadcrumb-icon:after { 2 | box-sizing: initial; 3 | } 4 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^CRAN-RELEASE$ 2 | ^.*\.Rproj$ 3 | ^\.Rproj\.user$ 4 | ^cran-comments\.md$ 5 | ^CONDUCT\.md$ 6 | ^docs$ 7 | ^javascript$ 8 | ^\.github$ 9 | -------------------------------------------------------------------------------- /CRAN-RELEASE: -------------------------------------------------------------------------------- 1 | This package was submitted to CRAN on 2020-10-07. 2 | Once it is accepted, delete this file and tag the release (commit d76aa0ea40). 3 | -------------------------------------------------------------------------------- /docs/pkgdown.yml: -------------------------------------------------------------------------------- 1 | pandoc: 2.19.2 2 | pkgdown: 2.0.7 3 | pkgdown_sha: ~ 4 | articles: 5 | sunburst-2-0-0: sunburst-2-0-0.html 6 | last_built: 2023-02-05T18:01Z 7 | 8 | -------------------------------------------------------------------------------- /inst/htmlwidgets/sund2b.yaml: -------------------------------------------------------------------------------- 1 | #dependencies: 2 | # - name: d2b 3 | # version: 0.5.1 4 | # src: htmlwidgets/lib/d2b 5 | # script: d2b.min.js 6 | # stylesheet: 7 | -------------------------------------------------------------------------------- /inst/htmlwidgets/sunburst.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: sequences 3 | version: 0.1 4 | src: htmlwidgets/lib/sequences 5 | # script: sequences.js 6 | stylesheet: sequences.css 7 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(add_shiny) 4 | export(renderSunburst) 5 | export(renderSund2b) 6 | export(sunburst) 7 | export(sunburstOutput) 8 | export(sund2b) 9 | export(sund2bBreadcrumb) 10 | export(sund2bOutput) 11 | export(sund2bTooltip) 12 | import(htmltools) 13 | import(htmlwidgets) 14 | -------------------------------------------------------------------------------- /inst/examples/example_sort.R: -------------------------------------------------------------------------------- 1 | # sorting example: place data in order of occurence 2 | 3 | library(sunburstR) 4 | 5 | df <- data.frame( 6 | group = c("foo", "bar", "xyz"), 7 | value = c(1, 3, 2) 8 | ) 9 | 10 | sunburst(df, 11 | # create a trivial sort function 12 | sortFunction = htmlwidgets::JS('function(x) {return x;}')) 13 | 14 | new_order <- c(3,2,1) 15 | sunburst(df[new_order,], 16 | sortFunction = htmlwidgets::JS('function(x) {return x;}')) 17 | 18 | -------------------------------------------------------------------------------- /sunburstR.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: XeLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageRoxygenize: rd,collate,namespace 22 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | ## Notes 2 | 3 | ## Test environments 4 | * local Windows 10 install, R 4.1.1 5 | * rhub check_for_cran 6 | * Github actions standard check 7 | * winbuilder devel and release 8 | 9 | ## R CMD check results 10 | 11 | 0 errors | 0 warnings | 1 notes 12 | 13 | R CMD check succeeded 14 | checking package dependencies ... NOTE 15 | Package which this enhances but not available for checking: ‘treemap’ 16 | 17 | ## Reverse dependencies 18 | 19 | There are no reverse dependencies. 20 | 21 | -------------------------------------------------------------------------------- /inst/examples/example_sund2bTooltip.R: -------------------------------------------------------------------------------- 1 | if(interactive()){ 2 | 3 | library(sunburstR) 4 | 5 | # use a sample of the sequences csv data 6 | sequences <- read.csv( 7 | system.file("examples/visit-sequences.csv",package="sunburstR") 8 | ,header = FALSE 9 | ,stringsAsFactors = FALSE 10 | )[1:200,] 11 | 12 | # change the tooltip 13 | sund2b( 14 | sequences, 15 | tooltip = sund2bTooltip( 16 | html = htmlwidgets::JS(" 17 | function(nodedata, size, percent) { 18 | return '' + nodedata.name + '' + ' ' + size 19 | } 20 | ") 21 | ) 22 | ) 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /docs/articles/sunburst-2-0-0_files/header-attrs-2.10/header-attrs.js: -------------------------------------------------------------------------------- 1 | // Pandoc 2.9 adds attributes on both header and div. We remove the former (to 2 | // be compatible with the behavior of Pandoc < 2.8). 3 | document.addEventListener('DOMContentLoaded', function(e) { 4 | var hs = document.querySelectorAll("div.section[class*='level'] > :first-child"); 5 | var i, h, a; 6 | for (i = 0; i < hs.length; i++) { 7 | h = hs[i]; 8 | if (!/^h[1-6]$/i.test(h.tagName)) continue; // it should be a header h1-h6 9 | a = h.attributes; 10 | while (a.length > 0) h.removeAttribute(a[0].name); 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /javascript/src/rebind.js: -------------------------------------------------------------------------------- 1 | // Copies a variable number of methods from source to target. 2 | export default function (target, source) { 3 | var i = 1, n = arguments.length, method; 4 | while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]); 5 | return target; 6 | }; 7 | 8 | // Method is assumed to be a standard D3 getter-setter: 9 | // If passed with no arguments, gets the value. 10 | // If passed with arguments, sets the value and returns the target. 11 | function d3_rebind(target, source, method) { 12 | return function() { 13 | var value = method.apply(source, arguments); 14 | return value === source ? target : value; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /inst/examples/example_sund2bBreadcrumb.R: -------------------------------------------------------------------------------- 1 | if(interactive()){ 2 | 3 | library(sunburstR) 4 | 5 | # use a sample of the sequences csv data 6 | sequences <- read.csv( 7 | system.file("examples/visit-sequences.csv",package="sunburstR") 8 | ,header = FALSE 9 | ,stringsAsFactors = FALSE 10 | )[1:200,] 11 | 12 | # disable the breadcrumb 13 | sund2b( 14 | sequences, 15 | breadcrumbs = sund2bBreadcrumb( 16 | enabled = FALSE 17 | ) 18 | ) 19 | 20 | # change the breadcrumb content 21 | sund2b( 22 | sequences, 23 | breadcrumbs = sund2bBreadcrumb( 24 | html = htmlwidgets::JS(" 25 | function(nodedata, size, percent) { 26 | return '' + nodedata.name + '' + ' ' + size 27 | } 28 | ") 29 | ) 30 | ) 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /docs/articles/sunburst-2-0-0_files/accessible-code-block-0.0.1/empty-anchor.js: -------------------------------------------------------------------------------- 1 | // Hide empty tag within highlighted CodeBlock for screen reader accessibility (see https://github.com/jgm/pandoc/issues/6352#issuecomment-626106786) --> 2 | // v0.0.1 3 | // Written by JooYoung Seo (jooyoung@psu.edu) and Atsushi Yasumoto on June 1st, 2020. 4 | 5 | document.addEventListener('DOMContentLoaded', function() { 6 | const codeList = document.getElementsByClassName("sourceCode"); 7 | for (var i = 0; i < codeList.length; i++) { 8 | var linkList = codeList[i].getElementsByTagName('a'); 9 | for (var j = 0; j < linkList.length; j++) { 10 | if (linkList[j].innerHTML === "") { 11 | linkList[j].setAttribute('aria-hidden', 'true'); 12 | } 13 | } 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /inst/htmlwidgets/lib/sequences/sequences.css: -------------------------------------------------------------------------------- 1 | .sunburst-main { 2 | width: 90%; 3 | height: 100%; 4 | } 5 | 6 | .sunburst-sidebar { 7 | position: absolute; 8 | right: 10px; 9 | top: 10px; 10 | } 11 | 12 | .sunburst-sequence { 13 | width: 100%; 14 | position: absolute; 15 | top: 0px; 16 | left: 0px; 17 | pointer-events: none; 18 | } 19 | 20 | .sunburst-legend { 21 | padding: 10px 0 0 3px; 22 | } 23 | 24 | .sunburst-chart { 25 | width: 100%; 26 | height: 100%; 27 | position: absolute; 28 | top: 70px; 29 | } 30 | 31 | .sunburst-chart path { 32 | stroke: #fff; 33 | } 34 | 35 | .sunburst-explanation { 36 | position: absolute; 37 | text-align: center; 38 | color: #666; 39 | z-index: 999; 40 | font-size: 2em; 41 | pointer-events: none; 42 | } 43 | -------------------------------------------------------------------------------- /docs/reference/libs/sequences-0.1/sequences.css: -------------------------------------------------------------------------------- 1 | .sunburst-main { 2 | width: 90%; 3 | height: 100%; 4 | } 5 | 6 | .sunburst-sidebar { 7 | position: absolute; 8 | right: 10px; 9 | top: 10px; 10 | } 11 | 12 | .sunburst-sequence { 13 | width: 100%; 14 | position: absolute; 15 | top: 0px; 16 | left: 0px; 17 | pointer-events: none; 18 | } 19 | 20 | .sunburst-legend { 21 | padding: 10px 0 0 3px; 22 | } 23 | 24 | .sunburst-chart { 25 | width: 100%; 26 | height: 100%; 27 | position: absolute; 28 | top: 70px; 29 | } 30 | 31 | .sunburst-chart path { 32 | stroke: #fff; 33 | } 34 | 35 | .sunburst-explanation { 36 | position: absolute; 37 | text-align: center; 38 | color: #666; 39 | z-index: 999; 40 | font-size: 2em; 41 | pointer-events: none; 42 | } 43 | -------------------------------------------------------------------------------- /docs/articles/sunburst-2-0-0_files/sequences-0.1/sequences.css: -------------------------------------------------------------------------------- 1 | .sunburst-main { 2 | width: 90%; 3 | height: 100%; 4 | } 5 | 6 | .sunburst-sidebar { 7 | position: absolute; 8 | right: 10px; 9 | top: 10px; 10 | } 11 | 12 | .sunburst-sequence { 13 | width: 100%; 14 | position: absolute; 15 | top: 0px; 16 | left: 0px; 17 | pointer-events: none; 18 | } 19 | 20 | .sunburst-legend { 21 | padding: 10px 0 0 3px; 22 | } 23 | 24 | .sunburst-chart { 25 | width: 100%; 26 | height: 100%; 27 | position: absolute; 28 | top: 70px; 29 | } 30 | 31 | .sunburst-chart path { 32 | stroke: #fff; 33 | } 34 | 35 | .sunburst-explanation { 36 | position: absolute; 37 | text-align: center; 38 | color: #666; 39 | z-index: 999; 40 | font-size: 2em; 41 | pointer-events: none; 42 | } 43 | -------------------------------------------------------------------------------- /inst/examples/example_treemap.R: -------------------------------------------------------------------------------- 1 | \dontrun{ 2 | library(treemap) 3 | library(sunburstR) 4 | library(d3r) 5 | 6 | # use example from ?treemap::treemap 7 | data(GNI2014) 8 | tm <- treemap(GNI2014, 9 | index=c("continent", "iso3"), 10 | vSize="population", 11 | vColor="continent", 12 | type="index") 13 | 14 | tm_nest <- d3_nest( 15 | tm$tm[,c("continent", "iso3", "vSize", "color")], 16 | value_cols = c("vSize", "color") 17 | ) 18 | 19 | sunburst( 20 | data = tm_nest, 21 | valueField = "vSize", 22 | count = TRUE, 23 | # to avoid double counting with pre-summed trees 24 | # use sumNodes = FALSE 25 | sumNodes = FALSE, 26 | colors = htmlwidgets::JS("function(d){return d3.select(this).datum().data.color;}"), 27 | withD3 = TRUE 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /docs/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /R/converters.R: -------------------------------------------------------------------------------- 1 | #' @keywords internal 2 | csv_to_hier <- function(csv, delim = "-", rootLabel = "root") { 3 | hier_col <- strsplit(as.character(csv[[1]]), delim) 4 | # determine max length of all the paths to build column names 5 | # issue 107 6 | max_path_length <- max(unlist(lapply(hier_col, length))) 7 | # build column names for path 8 | path_col_names <- paste0("X",seq_len(max_path_length)) 9 | df <- dplyr::bind_rows( 10 | lapply( 11 | hier_col, 12 | function(rw) { 13 | structure(rw, names = path_col_names[1:(length(rw))]) 14 | } 15 | ) 16 | ) 17 | # handle case where no delimiter in root 18 | if(any(is.na(df[,1]))) { 19 | idx_notna <- which(!is.na(df[,1])) 20 | df[idx_notna,2] <- df[idx_notna,1] 21 | df <- df[,-1] 22 | } 23 | df$size = csv[[2]] 24 | d3r::d3_nest(df, value_cols = "size", root = rootLabel) 25 | } 26 | -------------------------------------------------------------------------------- /inst/examples/example_TraMineR.R: -------------------------------------------------------------------------------- 1 | library(TraMineR) 2 | library(sunburstR) 3 | library(pipeR) 4 | 5 | # use example from TraMineR vignette 6 | data("mvad") 7 | mvad.alphab <- c( 8 | "employment", "FE", "HE", "joblessness", 9 | "school", "training" 10 | ) 11 | mvad.seq <- seqdef(mvad, 17:86, xtstep = 6, alphabet = mvad.alphab) 12 | 13 | # to make this work, we'll compress the sequences with seqdss 14 | # could also aggregate with dply later 15 | seqtab( seqdss(mvad.seq), tlim = 0, format = "SPS" ) %>>% 16 | attr("freq") %>>% 17 | ( 18 | data.frame( 19 | # appending "-end" is necessary for this to work 20 | sequence = paste0( 21 | gsub( 22 | x = names(.$Freq) 23 | , pattern = "(/[0-9]*)" 24 | , replacement = "" 25 | , perl = T 26 | ) 27 | ,"-end" 28 | ) 29 | ,freq = as.numeric(.$Freq) 30 | ,stringsAsFactors = FALSE 31 | ) 32 | ) %>>% 33 | sunburst 34 | -------------------------------------------------------------------------------- /man/d2b-shiny.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/d2b.R 3 | \name{d2b-shiny} 4 | \alias{d2b-shiny} 5 | \alias{sund2bOutput} 6 | \alias{renderSund2b} 7 | \title{Shiny bindings for d2b} 8 | \usage{ 9 | sund2bOutput(outputId, width = "100\%", height = "400px") 10 | 11 | renderSund2b(expr, env = parent.frame(), quoted = FALSE) 12 | } 13 | \arguments{ 14 | \item{outputId}{output variable to read from} 15 | 16 | \item{width, height}{Must be a valid CSS unit (like \code{'100\%'}, 17 | \code{'400px'}, \code{'auto'}) or a number, which will be coerced to a 18 | string and have \code{'px'} appended.} 19 | 20 | \item{expr}{An expression that generates a d2b} 21 | 22 | \item{env}{The environment in which to evaluate \code{expr}.} 23 | 24 | \item{quoted}{Is \code{expr} a quoted expression (with \code{quote()})? This 25 | is useful if you want to save an expression in a variable.} 26 | } 27 | \description{ 28 | Output and render functions for using d2b within Shiny 29 | applications and interactive Rmd documents. 30 | } 31 | -------------------------------------------------------------------------------- /man/sunburst-shiny.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/sunburst.R 3 | \name{sunburst-shiny} 4 | \alias{sunburst-shiny} 5 | \alias{sunburstOutput} 6 | \alias{renderSunburst} 7 | \title{Shiny bindings for sunburst} 8 | \usage{ 9 | sunburstOutput(outputId, width = "100\%", height = "400px") 10 | 11 | renderSunburst(expr, env = parent.frame(), quoted = FALSE) 12 | } 13 | \arguments{ 14 | \item{outputId}{output variable to read from} 15 | 16 | \item{width, height}{Must be a valid CSS unit (like \code{'100\%'}, 17 | \code{'400px'}, \code{'auto'}) or a number, which will be coerced to a 18 | string and have \code{'px'} appended.} 19 | 20 | \item{expr}{An expression that generates a sunburst} 21 | 22 | \item{env}{The environment in which to evaluate \code{expr}.} 23 | 24 | \item{quoted}{Is \code{expr} a quoted expression (with \code{quote()})? This 25 | is useful if you want to save an expression in a variable.} 26 | } 27 | \description{ 28 | Output and render functions for using sunburst within Shiny 29 | applications and interactive Rmd documents. 30 | } 31 | -------------------------------------------------------------------------------- /inst/examples/example_calendar.R: -------------------------------------------------------------------------------- 1 | # calendar sunburst example 2 | 3 | library(sunburstR) 4 | 5 | df <- data.frame( 6 | date = seq.Date( 7 | as.Date('2014-01-01'), 8 | as.Date('2016-12-31'), 9 | by = "days" 10 | ), 11 | stringsAsFactors = FALSE 12 | ) 13 | 14 | df$year = format(df$date, "%Y") 15 | df$quarter = paste0("Q", ceiling(as.numeric(format(df$date,"%m"))/3)) 16 | df$month = format(df$date, "%b") 17 | df$path = paste(df$year, df$quarter, df$month, sep="-") 18 | df$count = rep(1, nrow(df)) 19 | 20 | sunburst( 21 | data.frame(xtabs(count~path,df)), 22 | # added a degree of difficulty by providing 23 | # not easily sortable names 24 | sortFunction = htmlwidgets::JS( 25 | " 26 | function(a,b){ 27 | abb = { 28 | 2014:-7, 29 | 2015:-6, 30 | 2016:-5, 31 | Q1:-4, 32 | Q2:-3, 33 | Q3:-2, 34 | Q4:-1, 35 | Jan:1, 36 | Feb:2, 37 | Mar:3, 38 | Apr:4, 39 | May:5, 40 | Jun:6, 41 | Jul:7, 42 | Aug:8, 43 | Sep:9, 44 | Oct:10, 45 | Nov:11, 46 | Dec:12 47 | } 48 | return abb[a.data.name] - abb[b.data.name]; 49 | } 50 | " 51 | ) 52 | ) 53 | -------------------------------------------------------------------------------- /docs/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /404.html 5 | 6 | 7 | /articles/index.html 8 | 9 | 10 | /articles/sunburst-2-0-0.html 11 | 12 | 13 | /authors.html 14 | 15 | 16 | /CONDUCT.html 17 | 18 | 19 | /index.html 20 | 21 | 22 | /LICENSE-text.html 23 | 24 | 25 | /news/index.html 26 | 27 | 28 | /reference/add_shiny.html 29 | 30 | 31 | /reference/d2b-shiny.html 32 | 33 | 34 | /reference/index.html 35 | 36 | 37 | /reference/sunburst-shiny.html 38 | 39 | 40 | /reference/sunburst.html 41 | 42 | 43 | /reference/sund2b.html 44 | 45 | 46 | /reference/sund2bBreadcrumb.html 47 | 48 | 49 | /reference/sund2bTooltip.html 50 | 51 | 52 | -------------------------------------------------------------------------------- /tests/testthat/test-basic.R: -------------------------------------------------------------------------------- 1 | context("creation") 2 | 3 | test_that("sunburstR makes a htmlwidget", { 4 | expect_is(sunburst("{}"), "htmlwidget") 5 | expect_is(sunburst("{}"), "sunburst") 6 | expect_error(sunburst()) 7 | }) 8 | 9 | test_that("sunburstR handles options as expected", { 10 | # check default values 11 | expect_equal(sunburst("{}")$x$options$valueField, "size") 12 | expect_equal(sunburst("{}")$sizingPolicy, htmlwidgets::sizingPolicy(browser.fill=TRUE)) 13 | # check that arguments are passed 14 | expect_equal(sunburst("{}", width = 100)$width, 100) 15 | expect_equal(sunburst("{}", height = 100)$height, 100) 16 | expect_equal(sunburst("{}", legendOrder = toString(5:1))$x$options$legendOrder, toString(5:1)) 17 | expect_equal(sunburst("{}", colors = c("red", "blue"))$x$options$color, c("red", "blue")) 18 | }) 19 | 20 | sequences_csv <- read.csv( 21 | system.file("examples/visit-sequences.csv", package = "sunburstR") 22 | , header = FALSE 23 | , stringsAsFactors = FALSE 24 | )[1:100,] 25 | 26 | sequences_json <- jsonlite::fromJSON( 27 | system.file("examples/visit-sequences.json", package = "sunburstR") 28 | , simplifyDataFrame = FALSE 29 | ) 30 | 31 | test_that("sunburstR works with both csv and json data", { 32 | # csvdata and jsondata deprecated so expect warning 33 | expect_warning(sunburst(csvdata=sequences_csv)) 34 | expect_warning(sunburst(jsondata=sequences_json)) 35 | }) 36 | -------------------------------------------------------------------------------- /tests/testthat/test-conversion.R: -------------------------------------------------------------------------------- 1 | context("conversion") 2 | 3 | test_that("csv_to_hier handles paths of length one", { 4 | csv <- utils::read.table( 5 | header = FALSE, 6 | text = " 7 | a-b-c 1 8 | x 1 9 | d-e-f 1 10 | y 1 11 | g-h-i 1 12 | a3-b3-c3-d3 1" 13 | ) 14 | 15 | expect_identical( 16 | jsonlite::fromJSON(csv_to_hier(csv),simplifyVector = FALSE), 17 | list(children = list(list(name = "a", children = list(list(name = "b", 18 | children = list(list(name = "c", children = list(), size = 1L, 19 | colname = "X3")), colname = "X2")), colname = "X1"), 20 | list(name = "x", children = list(), size = 1L, colname = "X1"), 21 | list(name = "d", children = list(list(name = "e", children = list( 22 | list(name = "f", children = list(), size = 1L, colname = "X3")), 23 | colname = "X2")), colname = "X1"), list(name = "y", children = list(), 24 | size = 1L, colname = "X1"), list(name = "g", children = list( 25 | list(name = "h", children = list(list(name = "i", children = list(), 26 | size = 1L, colname = "X3")), colname = "X2")), colname = "X1"), 27 | list(name = "a3", children = list(list(name = "b3", children = list( 28 | list(name = "c3", children = list(list(name = "d3", size = 1L, 29 | colname = "X4")), colname = "X3")), colname = "X2")), 30 | colname = "X1")), name = "root") 31 | ) 32 | }) 33 | -------------------------------------------------------------------------------- /CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, we pledge to respect all people who 4 | contribute through reporting issues, posting feature requests, updating documentation, 5 | submitting pull requests or patches, and other activities. 6 | 7 | We are committed to making participation in this project a harassment-free experience for 8 | everyone, regardless of level of experience, gender, gender identity and expression, 9 | sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion. 10 | 11 | Examples of unacceptable behavior by participants include the use of sexual language or 12 | imagery, derogatory comments or personal attacks, trolling, public or private harassment, 13 | insults, or other unprofessional conduct. 14 | 15 | Project maintainers have the right and responsibility to remove, edit, or reject comments, 16 | commits, code, wiki edits, issues, and other contributions that are not aligned to this 17 | Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed 18 | from the project team. 19 | 20 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by 21 | opening an issue or contacting one or more of the project maintainers. 22 | 23 | This Code of Conduct is adapted from the Contributor Covenant 24 | (http:contributor-covenant.org), version 1.0.0, available at 25 | http://contributor-covenant.org/version/1/0/0/ 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![R-CMD-check](https://github.com/timelyportfolio/sunburstR/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/timelyportfolio/sunburstR/actions/workflows/R-CMD-check.yaml) 3 | [![CRAN status](https://www.r-pkg.org/badges/version/sunburstR)](https://CRAN.R-project.org/package=sunburstR) 4 | 5 | 6 | ### Help Me/Pay Me to Use, Improve, and Extend 7 | 8 | `sunburstR` has already seen extensive use in many projects across multiple domains. If you have any interest in collaborating with me on this project or applying `sunburstR`, please let me know (see [Time Isn't Money](https://buildingwidgets.com/2016-12-09_post_break/)). 9 | 10 | 11 | # sunburstR 12 | 13 | Easily make these interactive `d3.js` sequence sunburst charts in `R` originally modeled on this [example](https://gist.github.com/kerryrodden/7090426) from Kerry Rodden. In addition, `sunburstr` provides another beautiful and functional version using [`d2b`](https://github.com/d2bjs/d2b) from Kevin Warne. 14 | 15 | ### Examples 16 | 17 | See the [examples](https://github.com/timelyportfolio/sunburstR/tree/master/inst/examples). 18 | 19 | ### Improve/Iterate 20 | 21 | These are still far from feature-complete. I would love your input, feedback, and comments. 22 | 23 | ### Code of Conduct 24 | 25 | Please note that this project is released with a [Contributor Code of Conduct](https://github.com/timelyportfolio/sunburstR/blob/master/CONDUCT.md). By participating in this project you agree to abide by its terms. 26 | -------------------------------------------------------------------------------- /inst/examples/example_shiny.R: -------------------------------------------------------------------------------- 1 | \dontrun{ 2 | 3 | library(shiny) 4 | library(sunburstR) 5 | 6 | sequences <- read.csv( 7 | system.file("examples/visit-sequences.csv",package="sunburstR") 8 | ,header=F 9 | ,stringsAsFactors = FALSE 10 | ) 11 | 12 | 13 | server <- function(input,output,session){ 14 | 15 | output$sunburst <- renderSunburst({ 16 | #invalidateLater(1000, session) 17 | 18 | sequences <- sequences[sample(nrow(sequences),1000),] 19 | 20 | add_shiny(sunburst(sequences)) 21 | }) 22 | 23 | 24 | selection <- reactive({ 25 | input$sunburst_mouseover 26 | }) 27 | 28 | output$selection <- renderText(selection()) 29 | } 30 | 31 | 32 | ui<-fluidPage( 33 | sidebarLayout( 34 | sidebarPanel( 35 | 36 | ), 37 | 38 | # plot sunburst 39 | mainPanel( 40 | sunburstOutput("sunburst"), 41 | textOutput("selection") 42 | ) 43 | ) 44 | ) 45 | 46 | shinyApp(ui = ui, server = server) 47 | 48 | # an example with d2b sunburst and Shiny 49 | library(shiny) 50 | library(sunburstR) 51 | 52 | # use a sample of the sequences csv data 53 | sequences <- read.csv( 54 | system.file("examples/visit-sequences.csv",package="sunburstR") 55 | ,header = FALSE 56 | ,stringsAsFactors = FALSE 57 | )[1:200,] 58 | 59 | # create a d2b sunburst 60 | s2b <- sund2b(sequences) 61 | 62 | options(shiny.trace=TRUE) 63 | ui <- sund2bOutput("s2b") 64 | server <- function(input, output, session) { 65 | output$s2b <- renderSund2b({ 66 | add_shiny(s2b) 67 | }) 68 | } 69 | shinyApp(ui, server) 70 | } 71 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: R-CMD-check 10 | 11 | jobs: 12 | R-CMD-check: 13 | runs-on: ${{ matrix.config.os }} 14 | 15 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | config: 21 | - {os: macos-latest, r: 'release'} 22 | - {os: windows-latest, r: 'release'} 23 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 24 | - {os: ubuntu-latest, r: 'release'} 25 | - {os: ubuntu-latest, r: 'oldrel-1'} 26 | 27 | env: 28 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 29 | R_KEEP_PKG_SOURCE: yes 30 | 31 | steps: 32 | - uses: actions/checkout@v3 33 | 34 | - uses: r-lib/actions/setup-pandoc@v2 35 | 36 | - uses: r-lib/actions/setup-r@v2 37 | with: 38 | r-version: ${{ matrix.config.r }} 39 | http-user-agent: ${{ matrix.config.http-user-agent }} 40 | use-public-rspm: true 41 | 42 | - uses: r-lib/actions/setup-r-dependencies@v2 43 | with: 44 | extra-packages: any::rcmdcheck 45 | needs: check 46 | 47 | - uses: r-lib/actions/check-r-package@v2 48 | with: 49 | upload-snapshots: true 50 | -------------------------------------------------------------------------------- /inst/htmlwidgets/lib/d2b/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015-2019, Kevin Warne 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the author nor the names of contributors may be used to 15 | endorse or promote products derived from this software without specific prior 16 | written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /docs/articles/sunburst-2-0-0_files/d2b-0.5.1/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015-2018, Kevin Warne 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the author nor the names of contributors may be used to 15 | endorse or promote products derived from this software without specific prior 16 | written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /docs/articles/sunburst-2-0-0_files/d2b-1.0.9/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015-2019, Kevin Warne 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the author nor the names of contributors may be used to 15 | endorse or promote products derived from this software without specific prior 16 | written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /inst/examples/example_ngram.R: -------------------------------------------------------------------------------- 1 | \dontrun{ 2 | # use sunburst to analyze ngram data from Peter Norvig 3 | # http://norvig.com/mayzner.html 4 | 5 | library(sunburstR) 6 | library(pipeR) 7 | 8 | # read the csv data downloaded from the Google Fusion Table linked in the article 9 | ngrams2 <- read.csv( 10 | system.file( 11 | "examples/ngrams2.csv" 12 | ,package="sunburstR" 13 | ) 14 | , stringsAsFactors = FALSE 15 | ) 16 | 17 | ngrams2 %>>% 18 | # let's look at ngrams at the start of a word, so columns 1 and 3 19 | (.[,c(1,3)]) %>>% 20 | # split the ngrams into a sequence by splitting each letter and adding - 21 | ( 22 | data.frame( 23 | sequence = strsplit(.[,1],"") %>>% 24 | lapply( function(ng){ paste0(ng,collapse = "-") } ) %>>% 25 | unlist 26 | ,freq = .[,2] 27 | ,stringsAsFactors = FALSE 28 | ) 29 | ) %>>% 30 | sunburst 31 | 32 | 33 | library(htmltools) 34 | 35 | ngrams2 %>>% 36 | ( 37 | lapply( 38 | seq.int(3,ncol(.)) 39 | ,function(letpos){ 40 | (.[,c(1,letpos)]) %>>% 41 | # split the ngrams into a sequence by splitting each letter and adding - 42 | ( 43 | data.frame( 44 | sequence = strsplit(.[,1],"") %>>% 45 | lapply( function(ng){ paste0(ng,collapse = "-") } ) %>>% 46 | unlist 47 | ,freq = .[,2] 48 | ,stringsAsFactors = FALSE 49 | ) 50 | ) %>>% 51 | ( tags$div(style="float:left;",sunburst( ., height = 300, width = 300 )) ) 52 | } 53 | ) 54 | ) %>>% 55 | tagList %>>% 56 | browsable 57 | } 58 | -------------------------------------------------------------------------------- /inst/examples/example_flexdashboard.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Tabset Column Example" 3 | output: flexdashboard::flex_dashboard 4 | --- 5 | 6 | ```{r warning=FALSE, message=FALSE, echo=FALSE} 7 | knitr:::opts_knit$set(warning=FALSE,message=FALSE) 8 | library(sunburstR) 9 | ``` 10 | 11 | Column 12 | ------------------------------------- 13 | 14 | ### Sunburst 1 15 | 16 | ```{r} 17 | # read in sample visit-sequences.csv data provided in source 18 | # https://gist.github.com/kerryrodden/7090426#file-visit-sequences-csv 19 | sequences <- read.csv( 20 | system.file("examples/visit-sequences.csv",package="sunburstR") 21 | ,header=F 22 | ,stringsAsFactors = FALSE 23 | ) 24 | 25 | # explore some of the arguments 26 | sunburst( 27 | sequences 28 | ,count = TRUE 29 | ) 30 | ``` 31 | 32 | Column {.tabset} 33 | ------------------------------------- 34 | 35 | ### Sunburst 2 36 | 37 | ```{r} 38 | # try with csv data from this fork 39 | # https://gist.github.com/mkajava/7515402 40 | # great use for new breadbrumb wrapping 41 | sunburst( 42 | csvdata = read.csv( 43 | file = "https://gist.githubusercontent.com/mkajava/7515402/raw/9f80d28094dc9dfed7090f8fb3376ef1539f4fd2/comment-sequences.csv" 44 | ,header = FALSE 45 | ,stringsAsFactors = FALSE 46 | ) 47 | ) 48 | ``` 49 | 50 | ### Sunburst 3 51 | 52 | ```{r} 53 | # try with csv data from this fork 54 | # https://gist.github.com/rileycrane/92a2c36eb932b4f99e51/ 55 | sunburst( csvdata = read.csv( 56 | file = "https://gist.githubusercontent.com/rileycrane/92a2c36eb932b4f99e51/raw/a0212b4ca8043af47ec82369aa5f023530279aa3/visit-sequences.csv" 57 | ,header=FALSE 58 | ,stringsAsFactors = FALSE 59 | )) 60 | ``` 61 | -------------------------------------------------------------------------------- /javascript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "d3-sunburst-chart", 3 | "version": "0.0.2", 4 | "description": "D3 Plugin that draws sunburst charts.", 5 | "keywords": [ 6 | "d3", 7 | "d3-module", 8 | "sunburst chart", 9 | "data visualization" 10 | ], 11 | "author": { 12 | "name": "Kenton Russell", 13 | "url": "https://buildingwidgets.com" 14 | }, 15 | "license": "MIT", 16 | "main": "build/sunburst.js", 17 | "jsnext:main": "index", 18 | "homepage": "https://github.com/timelyportfolio/sunburstR", 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/timelyportfolio/sunburstR.git" 22 | }, 23 | "scripts": { 24 | "pretest": "rm -rf build && mkdir build && rollup -g d3-array:d3,d3-collection:d3,d3-dispatch:d3,d3-format:d3,d3-hierarchy:d3,d3-scale:d3,d3-selection:d3,d3-shape:d3,d3-transition:d3 -f umd -n d3 -o build/sunburst.js -- index.js", 25 | "test": "tape 'test/**/*-test.js' && eslint index.js src", 26 | "prepublish": "browserify build/sunburst.js > build/sunburst-bundle.js && uglifyjs build/sunburst-bundle.js -c -m -o build/sunburst.min.js && cp build/sunburst-bundle.js ../inst/htmlwidgets/sunburst.js" 27 | }, 28 | "dependencies": { 29 | "d3-array": "^1.2.0", 30 | "d3-collection": "^1.0.3", 31 | "d3-dispatch": "^1.0.3", 32 | "d3-format": "^1.2.0", 33 | "d3-hierarchy": "^1.1.5", 34 | "d3-scale": "^1.0.6", 35 | "d3-selection": "^1.1.0", 36 | "d3-shape": "^1.2.0", 37 | "d3-transition": "^1.1.0", 38 | "d3plus-color": "^0.6.2" 39 | }, 40 | "devDependencies": { 41 | "browserify": "^14.3.0", 42 | "rollup": "^0.34.13", 43 | "tape": "^4.6.0", 44 | "uglify-js": "^2.7.3" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /man/sund2bBreadcrumb.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/d2b.R 3 | \name{sund2bBreadcrumb} 4 | \alias{sund2bBreadcrumb} 5 | \title{Advanced Customization of 'd2b' Breadcrumb} 6 | \usage{ 7 | sund2bBreadcrumb(enabled = NULL, html = NULL, orient = NULL) 8 | } 9 | \arguments{ 10 | \item{enabled}{\code{boolean} to enable or disable the breadcrumbs.} 11 | 12 | \item{html}{\code{character} or \code{htmlwidgets::JS} to customize the content 13 | of the breadcrumb. To provide a function, the arguments for the 'JavaScript' 14 | function will be 'function(nodedata, size, percent)' and the function 15 | should return a string.} 16 | 17 | \item{orient}{\code{character} which should be one of "top", "left", "right", "bottom" 18 | to control the orientation of the breadcrumb relative to the chart.} 19 | } 20 | \value{ 21 | \code{list} 22 | } 23 | \description{ 24 | Advanced Customization of 'd2b' Breadcrumb 25 | } 26 | \examples{ 27 | if(interactive()){ 28 | 29 | library(sunburstR) 30 | 31 | # use a sample of the sequences csv data 32 | sequences <- read.csv( 33 | system.file("examples/visit-sequences.csv",package="sunburstR") 34 | ,header = FALSE 35 | ,stringsAsFactors = FALSE 36 | )[1:200,] 37 | 38 | # disable the breadcrumb 39 | sund2b( 40 | sequences, 41 | breadcrumbs = sund2bBreadcrumb( 42 | enabled = FALSE 43 | ) 44 | ) 45 | 46 | # change the breadcrumb content 47 | sund2b( 48 | sequences, 49 | breadcrumbs = sund2bBreadcrumb( 50 | html = htmlwidgets::JS(" 51 | function(nodedata, size, percent) { 52 | return '' + nodedata.name + '' + ' ' + size 53 | } 54 | ") 55 | ) 56 | ) 57 | 58 | 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /inst/examples/example_sund2b.R: -------------------------------------------------------------------------------- 1 | if(interactive()){ 2 | 3 | # The sund2b() API mirrors sunburst() with fewer arguments. 4 | 5 | library(sunburstR) 6 | 7 | # use a sample of the sequences csv data 8 | sequences <- read.csv( 9 | system.file("examples/visit-sequences.csv",package="sunburstR") 10 | ,header = FALSE 11 | ,stringsAsFactors = FALSE 12 | )[1:200,] 13 | 14 | # create a d2b sunburst 15 | sund2b(sequences) 16 | 17 | # show labels 18 | sund2b(sequences, showLabels = TRUE) 19 | 20 | # change the colors 21 | # using d3.js categorical color scheme 22 | sund2b( 23 | sequences, 24 | colors = htmlwidgets::JS("d3.scaleOrdinal(d3.schemeCategory20b)") 25 | ) 26 | } 27 | 28 | \dontrun{ 29 | # using RColorBrewer palette 30 | sund2b( 31 | sequences, 32 | colors = list(range = RColorBrewer::brewer.pal(9, "Set3")) 33 | ) 34 | # using a color column from the R dataset 35 | # treemap has an amazing treecolors ability 36 | library(treemap) 37 | library(d3r) 38 | rhd <- random.hierarchical.data() 39 | tm <- treemap( 40 | rhd, 41 | index = paste0("index", 1:3), 42 | vSize = "x", 43 | draw = FALSE 44 | )$tm 45 | sund2b( 46 | d3_nest(tm, value_cols = colnames(tm)[-(1:3)]), 47 | colors = htmlwidgets::JS( 48 | # yes this is a little different, so please pay attention 49 | # "function(d) {return d.color}" will not work 50 | "function(name, d){return d.color || '#ccc';}" 51 | ), 52 | valueField = "vSize" 53 | ) 54 | 55 | 56 | # use sund2b in Shiny 57 | library(shiny) 58 | ui <- sund2bOutput("sun") 59 | server <- function(input, output, session) { 60 | output$sun <- renderSund2b({ 61 | sund2b(sequences) 62 | }) 63 | } 64 | shinyApp(ui, server) 65 | 66 | } 67 | -------------------------------------------------------------------------------- /man/sund2bTooltip.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/d2b.R 3 | \name{sund2bTooltip} 4 | \alias{sund2bTooltip} 5 | \title{Advanced Customization of 'd2b' Tooltip} 6 | \usage{ 7 | sund2bTooltip(at = NULL, followMouse = NULL, html = NULL, my = NULL) 8 | } 9 | \arguments{ 10 | \item{at}{\code{character} which should be one of 11 | "top left", "top center", "top right", "center left", "center center", 12 | "center right", "bottom center", "bottom right" to specify 13 | where the tooltip will be positioned relative to the hovered item.} 14 | 15 | \item{followMouse}{\code{logical} controlling whether the tooltip 16 | will follow the mouse instead of being placed in a static 17 | position relative to the hovered element} 18 | 19 | \item{html}{\code{character} or \code{htmlwidgets::JS} to customize the content 20 | of the tooltip. To provide a function, the arguments for the 'JavaScript' 21 | function will be 'function(nodedata, size, percent)' and the function 22 | should return a string.} 23 | 24 | \item{my}{\code{character} which should be one of "top", "left", "right", "bottom" 25 | to control the orientation of the tooltip.} 26 | } 27 | \value{ 28 | \code{list} 29 | } 30 | \description{ 31 | Advanced Customization of 'd2b' Tooltip 32 | } 33 | \examples{ 34 | if(interactive()){ 35 | 36 | library(sunburstR) 37 | 38 | # use a sample of the sequences csv data 39 | sequences <- read.csv( 40 | system.file("examples/visit-sequences.csv",package="sunburstR") 41 | ,header = FALSE 42 | ,stringsAsFactors = FALSE 43 | )[1:200,] 44 | 45 | # change the tooltip 46 | sund2b( 47 | sequences, 48 | tooltip = sund2bTooltip( 49 | html = htmlwidgets::JS(" 50 | function(nodedata, size, percent) { 51 | return '' + nodedata.name + '' + ' ' + size 52 | } 53 | ") 54 | ) 55 | ) 56 | 57 | 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /inst/examples/example_replicate.R: -------------------------------------------------------------------------------- 1 | library(sunburstR) 2 | 3 | # read in sample visit-sequences.csv data provided in source 4 | # only use first 100 rows to speed package build and check 5 | # https://gist.github.com/kerryrodden/7090426#file-visit-sequences-csv 6 | sequences <- read.csv( 7 | system.file("examples/visit-sequences.csv",package="sunburstR") 8 | ,header = FALSE 9 | ,stringsAsFactors = FALSE 10 | )[1:100,] 11 | 12 | sunburst(sequences) 13 | 14 | \dontrun{ 15 | 16 | # explore some of the arguments 17 | sunburst( 18 | sequences 19 | ,count = TRUE 20 | ) 21 | 22 | sunburst( 23 | sequences 24 | # apply sort order to the legends 25 | ,legendOrder = unique(unlist(strsplit(sequences[,1],"-"))) 26 | # just provide the name in the explanation in the center 27 | ,explanation = "function(d){return d.data.name}" 28 | ) 29 | 30 | 31 | # try with json data 32 | sequence_json <- jsonlite::fromJSON( 33 | system.file("examples/visit-sequences.json",package="sunburstR"), 34 | simplifyDataFrame = FALSE 35 | ) 36 | sunburst(sequence_json) 37 | 38 | 39 | 40 | # try with csv data from this fork 41 | # https://gist.github.com/mkajava/7515402 42 | # great use for new breadbrumb wrapping 43 | sunburst( 44 | csvdata = read.csv( 45 | file = paste0( 46 | "https://gist.githubusercontent.com/mkajava/", 47 | "7515402/raw/9f80d28094dc9dfed7090f8fb3376ef1539f4fd2/", 48 | "comment-sequences.csv" 49 | ) 50 | ,header = TRUE 51 | ,stringsAsFactors = FALSE 52 | ) 53 | ) 54 | 55 | 56 | # try with csv data from this fork 57 | # https://gist.github.com/rileycrane/92a2c36eb932b4f99e51/ 58 | sunburst( csvdata = read.csv( 59 | file = paste0( 60 | "https://gist.githubusercontent.com/rileycrane/", 61 | "92a2c36eb932b4f99e51/raw/", 62 | "a0212b4ca8043af47ec82369aa5f023530279aa3/visit-sequences.csv" 63 | ) 64 | ,header=FALSE 65 | ,stringsAsFactors = FALSE 66 | )) 67 | } 68 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: sunburstR 2 | Type: Package 3 | Title: Sunburst 'Htmlwidget' 4 | Version: 2.1.8 5 | Date: 2023-02-05 6 | Authors@R: c( 7 | person( 8 | "Mike", "Bostock" 9 | , role = c("aut", "cph") 10 | , comment = "d3.js library, http://d3js.org" 11 | ), 12 | person( 13 | "Kerry", "Rodden" 14 | , role = c("aut", "cph") 15 | , comment = "sequences library in htmlwidgets/lib, https://gist.github.com/kerryrodden/7090426" 16 | ), 17 | person( 18 | "Kevin", "Warne" 19 | , role = c("aut", "cph") 20 | , comment = "d2b sunburst library in htmlwidgets/lib, https://github.com/d2bjs/d2b" 21 | ), 22 | person( 23 | "Kent", "Russell" 24 | , role = c("aut", "cre") 25 | , comment = "R interface" 26 | , email = "kent.russell@timelyportfolio.com" 27 | ), 28 | person( 29 | "Florian", "Breitwieser" 30 | , role = c("ctb") 31 | , comment = "R interface" 32 | ), 33 | person( 34 | "CJ", "Yetman" 35 | , role = c("ctb") 36 | , comment = c("R interface", ORCID = "0000-0001-5099-9500") 37 | , email = "cj@cjyetman.com" 38 | ) 39 | ) 40 | Maintainer: Kent Russell 41 | URL: https://github.com/timelyportfolio/sunburstR 42 | BugReports: https://github.com/timelyportfolio/sunburstR/issues 43 | Description: Make interactive 'd3.js' sequence sunburst diagrams in R with the 44 | convenience and infrastructure of an 'htmlwidget'. 45 | License: MIT + file LICENSE 46 | LazyData: TRUE 47 | Imports: 48 | d3r (>= 0.6.9), 49 | dplyr, 50 | htmlwidgets, 51 | htmltools 52 | Suggests: 53 | jsonlite, 54 | knitr, 55 | markdown, 56 | pipeR, 57 | testthat, 58 | tidyr (>= 0.7.0), 59 | rmarkdown 60 | Enhances: 61 | treemap 62 | RoxygenNote: 7.2.3 63 | VignetteBuilder: knitr 64 | -------------------------------------------------------------------------------- /man/add_shiny.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/shiny.R 3 | \name{add_shiny} 4 | \alias{add_shiny} 5 | \title{Add Shiny Events} 6 | \usage{ 7 | add_shiny(sunburst = NULL) 8 | } 9 | \arguments{ 10 | \item{sunburst}{\code{sunburst} htmlwidget to which you would 11 | like to add event handling} 12 | } 13 | \value{ 14 | \code{sunburst} htmlwidget 15 | } 16 | \description{ 17 | Add Shiny Events 18 | } 19 | \examples{ 20 | \dontrun{ 21 | 22 | library(shiny) 23 | library(sunburstR) 24 | 25 | sequences <- read.csv( 26 | system.file("examples/visit-sequences.csv",package="sunburstR") 27 | ,header=F 28 | ,stringsAsFactors = FALSE 29 | ) 30 | 31 | 32 | server <- function(input,output,session){ 33 | 34 | output$sunburst <- renderSunburst({ 35 | #invalidateLater(1000, session) 36 | 37 | sequences <- sequences[sample(nrow(sequences),1000),] 38 | 39 | add_shiny(sunburst(sequences)) 40 | }) 41 | 42 | 43 | selection <- reactive({ 44 | input$sunburst_mouseover 45 | }) 46 | 47 | output$selection <- renderText(selection()) 48 | } 49 | 50 | 51 | ui<-fluidPage( 52 | sidebarLayout( 53 | sidebarPanel( 54 | 55 | ), 56 | 57 | # plot sunburst 58 | mainPanel( 59 | sunburstOutput("sunburst"), 60 | textOutput("selection") 61 | ) 62 | ) 63 | ) 64 | 65 | shinyApp(ui = ui, server = server) 66 | 67 | # an example with d2b sunburst and Shiny 68 | library(shiny) 69 | library(sunburstR) 70 | 71 | # use a sample of the sequences csv data 72 | sequences <- read.csv( 73 | system.file("examples/visit-sequences.csv",package="sunburstR") 74 | ,header = FALSE 75 | ,stringsAsFactors = FALSE 76 | )[1:200,] 77 | 78 | # create a d2b sunburst 79 | s2b <- sund2b(sequences) 80 | 81 | options(shiny.trace=TRUE) 82 | ui <- sund2bOutput("s2b") 83 | server <- function(input, output, session) { 84 | output$s2b <- renderSund2b({ 85 | add_shiny(s2b) 86 | }) 87 | } 88 | shinyApp(ui, server) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /docs/bootstrap-toc.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Table of Contents v0.4.1 (http://afeld.github.io/bootstrap-toc/) 3 | * Copyright 2015 Aidan Feldman 4 | * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */ 5 | 6 | /* modified from https://github.com/twbs/bootstrap/blob/94b4076dd2efba9af71f0b18d4ee4b163aa9e0dd/docs/assets/css/src/docs.css#L548-L601 */ 7 | 8 | /* All levels of nav */ 9 | nav[data-toggle='toc'] .nav > li > a { 10 | display: block; 11 | padding: 4px 20px; 12 | font-size: 13px; 13 | font-weight: 500; 14 | color: #767676; 15 | } 16 | nav[data-toggle='toc'] .nav > li > a:hover, 17 | nav[data-toggle='toc'] .nav > li > a:focus { 18 | padding-left: 19px; 19 | color: #563d7c; 20 | text-decoration: none; 21 | background-color: transparent; 22 | border-left: 1px solid #563d7c; 23 | } 24 | nav[data-toggle='toc'] .nav > .active > a, 25 | nav[data-toggle='toc'] .nav > .active:hover > a, 26 | nav[data-toggle='toc'] .nav > .active:focus > a { 27 | padding-left: 18px; 28 | font-weight: bold; 29 | color: #563d7c; 30 | background-color: transparent; 31 | border-left: 2px solid #563d7c; 32 | } 33 | 34 | /* Nav: second level (shown on .active) */ 35 | nav[data-toggle='toc'] .nav .nav { 36 | display: none; /* Hide by default, but at >768px, show it */ 37 | padding-bottom: 10px; 38 | } 39 | nav[data-toggle='toc'] .nav .nav > li > a { 40 | padding-top: 1px; 41 | padding-bottom: 1px; 42 | padding-left: 30px; 43 | font-size: 12px; 44 | font-weight: normal; 45 | } 46 | nav[data-toggle='toc'] .nav .nav > li > a:hover, 47 | nav[data-toggle='toc'] .nav .nav > li > a:focus { 48 | padding-left: 29px; 49 | } 50 | nav[data-toggle='toc'] .nav .nav > .active > a, 51 | nav[data-toggle='toc'] .nav .nav > .active:hover > a, 52 | nav[data-toggle='toc'] .nav .nav > .active:focus > a { 53 | padding-left: 28px; 54 | font-weight: 500; 55 | } 56 | 57 | /* from https://github.com/twbs/bootstrap/blob/e38f066d8c203c3e032da0ff23cd2d6098ee2dd6/docs/assets/css/src/docs.css#L631-L634 */ 58 | nav[data-toggle='toc'] .nav > .active > ul { 59 | display: block; 60 | } 61 | -------------------------------------------------------------------------------- /inst/examples/example_baseball_shiny.R: -------------------------------------------------------------------------------- 1 | #### necessary packages #### 2 | #devtools::install_github("cpsievert/XML2R") 3 | #devtools::install_github("cpsievert/pitchRx") 4 | #devtools::install_github("timelyportfolio/sunburstR") 5 | #install.packages("dplyr") 6 | #install.packages("tidyr") 7 | #install.packages("stringr") 8 | #install.packages("rvest") 9 | 10 | #### library #### 11 | library(sunburstR) 12 | library(pitchRx) 13 | library(dplyr) 14 | 15 | #### data #### 16 | # get all data from 2016-08-25 17 | dat <- scrape(start = "2016-08-25", end = "2016-08-25") 18 | 19 | # use runner data to get idea of action with a runner on base 20 | # please note this will not be all action from a game 21 | # but I think it is an easier dataset to understand 22 | action <- dat$runner %>% 23 | group_by(event_num) %>% 24 | filter(row_number() == 1) %>% 25 | ungroup() %>% 26 | group_by(gameday_link, inning, inning_side) %>% 27 | summarize(event = paste(c(event),collapse="-")) 28 | 29 | sequences <- action %>% 30 | ungroup() %>% 31 | group_by(event) %>% 32 | summarize(count = n()) 33 | 34 | # sorry this is messy, but get data in a form 35 | # so sunburst can build hierarchy 36 | # which means we will sort in descending order of depth 37 | # note: this will eventually improve 38 | sequences$depth <- unlist(lapply(strsplit(sequences$event,"-"),length)) 39 | 40 | #### sunburst #### 41 | sb <- sequences %>% 42 | arrange(desc(depth), event) %>% 43 | sunburst(elementId = "sunburstgame") %>% 44 | add_shiny() 45 | 46 | #### shiny #### 47 | library(shiny) 48 | ui <- tagList( 49 | # can just use sunburst sb without shiny functions 50 | # if not expecting sunburst to be dynamic 51 | sb, 52 | textOutput("sbpath"), 53 | verbatimTextOutput("games") 54 | ) 55 | 56 | server <- function(input, output, session) { 57 | output$sbpath <- renderText({ 58 | input$sunburstgame_click 59 | }) 60 | 61 | output$games <- renderText({ 62 | path <- paste0(input$sunburstgame_click, collapse="-") 63 | action %>% 64 | ungroup() %>% 65 | filter(event == path) %>% 66 | select(gameday_link) %>% 67 | unlist() %>% 68 | paste0(collapse="\n") 69 | }) 70 | } 71 | 72 | shinyApp(ui, server) 73 | -------------------------------------------------------------------------------- /docs/docsearch.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | // register a handler to move the focus to the search bar 4 | // upon pressing shift + "/" (i.e. "?") 5 | $(document).on('keydown', function(e) { 6 | if (e.shiftKey && e.keyCode == 191) { 7 | e.preventDefault(); 8 | $("#search-input").focus(); 9 | } 10 | }); 11 | 12 | $(document).ready(function() { 13 | // do keyword highlighting 14 | /* modified from https://jsfiddle.net/julmot/bL6bb5oo/ */ 15 | var mark = function() { 16 | 17 | var referrer = document.URL ; 18 | var paramKey = "q" ; 19 | 20 | if (referrer.indexOf("?") !== -1) { 21 | var qs = referrer.substr(referrer.indexOf('?') + 1); 22 | var qs_noanchor = qs.split('#')[0]; 23 | var qsa = qs_noanchor.split('&'); 24 | var keyword = ""; 25 | 26 | for (var i = 0; i < qsa.length; i++) { 27 | var currentParam = qsa[i].split('='); 28 | 29 | if (currentParam.length !== 2) { 30 | continue; 31 | } 32 | 33 | if (currentParam[0] == paramKey) { 34 | keyword = decodeURIComponent(currentParam[1].replace(/\+/g, "%20")); 35 | } 36 | } 37 | 38 | if (keyword !== "") { 39 | $(".contents").unmark({ 40 | done: function() { 41 | $(".contents").mark(keyword); 42 | } 43 | }); 44 | } 45 | } 46 | }; 47 | 48 | mark(); 49 | }); 50 | }); 51 | 52 | /* Search term highlighting ------------------------------*/ 53 | 54 | function matchedWords(hit) { 55 | var words = []; 56 | 57 | var hierarchy = hit._highlightResult.hierarchy; 58 | // loop to fetch from lvl0, lvl1, etc. 59 | for (var idx in hierarchy) { 60 | words = words.concat(hierarchy[idx].matchedWords); 61 | } 62 | 63 | var content = hit._highlightResult.content; 64 | if (content) { 65 | words = words.concat(content.matchedWords); 66 | } 67 | 68 | // return unique words 69 | var words_uniq = [...new Set(words)]; 70 | return words_uniq; 71 | } 72 | 73 | function updateHitURL(hit) { 74 | 75 | var words = matchedWords(hit); 76 | var url = ""; 77 | 78 | if (hit.anchor) { 79 | url = hit.url_without_anchor + '?q=' + escape(words.join(" ")) + '#' + hit.anchor; 80 | } else { 81 | url = hit.url + '?q=' + escape(words.join(" ")); 82 | } 83 | 84 | return url; 85 | } 86 | -------------------------------------------------------------------------------- /R/shiny.R: -------------------------------------------------------------------------------- 1 | #' Add Shiny Events 2 | #' 3 | #' @param sunburst \code{sunburst} htmlwidget to which you would 4 | #' like to add event handling 5 | #' 6 | #' @return \code{sunburst} htmlwidget 7 | #' @export 8 | #' @example ./inst/examples/example_shiny.R 9 | add_shiny <- function(sunburst = NULL){ 10 | stopifnot(!is.null(sunburst),inherits(sunburst,c("sunburst","sund2b"))) 11 | 12 | if(is.null(sunburst$x$tasks)) sunburst$x$tasks <- list() 13 | 14 | if(inherits(sunburst,"sunburst")) { 15 | sunburst$x$tasks[length(sunburst$x$tasks)+1] <- list(htmlwidgets::JS( 16 | ' 17 | function(){ 18 | var chart = this.instance.chart; 19 | var el = this.el; 20 | if(!(typeof(Shiny)==="undefined")){ 21 | chart.on("mouseover.shiny", function(){ 22 | Shiny.onInputChange(el.id + "_mouseover",this) 23 | }); 24 | chart.on("mouseleave.shiny", function(){ 25 | Shiny.onInputChange(el.id + "_mouseleave",this) 26 | }); 27 | chart.on("click.shiny", function(){ 28 | Shiny.onInputChange(el.id + "_click",this) 29 | }); 30 | } 31 | } 32 | ' 33 | )) 34 | } 35 | 36 | if(inherits(sunburst,"sund2b")) { 37 | sunburst$x$tasks[length(sunburst$x$tasks)+1] <- list(htmlwidgets::JS( 38 | ' 39 | function(){ 40 | var el = d3.select(this.el); 41 | var chart = this.instance.chart; 42 | 43 | function getPath(d) { 44 | var dat = d.datum(); 45 | var path = [chart.label()(dat.data)]; 46 | while(dat.parent !== null) { 47 | dat = dat.parent; 48 | path.push(chart.label()(dat.data)); 49 | } 50 | return path.reverse(); 51 | } 52 | 53 | if(!(typeof(Shiny)==="undefined")){ 54 | el.on("mouseover.shiny", function(){ 55 | if(d3.select(d3.event.target).classed("d2b-sunburst-arc")) { 56 | Shiny.onInputChange(el.attr("id") + "_mouseover", getPath(d3.select(d3.event.target))) 57 | } 58 | }); 59 | el.on("mouseout.shiny", function(){ 60 | if(d3.select(d3.event.target).classed("d2b-sunburst-arc")) { 61 | Shiny.onInputChange(el.attr("id")+ "_mouseout", getPath(d3.select(d3.event.target))) 62 | } 63 | }); 64 | el.on("click.shiny", function(){ 65 | if(d3.select(d3.event.target).classed("d2b-sunburst-arc")) { 66 | Shiny.onInputChange(el.attr("id") + "_click", getPath(d3.select(d3.event.target))) 67 | } 68 | if(d3.select(d3.event.target).classed("d2b-sunburst-center")) { 69 | Shiny.onInputChange(el.attr("id") + "_click_center", getPath(d3.select(d3.event.target))) 70 | } 71 | }); 72 | } 73 | } 74 | ' 75 | )) 76 | } 77 | 78 | return(sunburst) 79 | } 80 | -------------------------------------------------------------------------------- /docs/articles/sunburst-2-0-0_files/d2b-0.5.1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "d2b", 3 | "version": "0.5.1", 4 | "description": "A d3-based reusable chart library.", 5 | "keywords": [ 6 | "d3", 7 | "d3-module", 8 | "chart", 9 | "charts", 10 | "pie", 11 | "axis", 12 | "line", 13 | "area", 14 | "bar", 15 | "stacked", 16 | "tooltip" 17 | ], 18 | "license": "MIT", 19 | "main": "build/d2b.cjs.js", 20 | "jsnext:main": "src/scripts/index.js", 21 | "homepage": "http://d2bjs.org", 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/d2bjs/d2b" 25 | }, 26 | "scripts": { 27 | "build:dev": "npm run build:package && rollup -c -g d3:d3,d3-sankey:d3,d3-interpolate-path:d3,d3-svg-annotation:d3 && npm run test", 28 | "build:prod": "npm run build:package && NODE_ENV=production rollup -c -g d3:d3,d3-sankey:d3,d3-interpolate-path:d3,d3-svg-annotation:d3", 29 | "build:server": "http-server -a 0.0.0.0 -p 8000", 30 | "build:watcher": "npm-watch", 31 | "build:package": "json2module package.json > build/package.js", 32 | "build:watch": "npm-run-all --parallel build:watcher build:server", 33 | "start": "npm run build:server", 34 | "test": "jasmine", 35 | "docs:prepare": "gitbook install", 36 | "docs:watch": "npm run docs:prepare && gitbook serve", 37 | "docs:build": "npm run docs:prepare && rm -rf _book && gitbook build", 38 | "docs:publish": "npm run docs:build && cp docs/cname _book && cd _book && git init && git commit --allow-empty -m 'Update docs' && git checkout master && git add . && git commit -am 'Update docs' && git push git@github.com:d2bjs/d2bjs.github.io master --force" 39 | }, 40 | "dependencies": { 41 | "d3": "^4.5.0", 42 | "d3-interpolate-path": "^2.0.1", 43 | "d3-sankey": "^0.7.1", 44 | "d3-svg-annotation": "^2.1.0", 45 | "flubber": "^0.4.0" 46 | }, 47 | "devDependencies": { 48 | "babel-preset-es2015-rollup": "^3.0.0", 49 | "cssnano": "^3.10.0", 50 | "http-server": "^0.9.0", 51 | "jasmine": "^2.5.3", 52 | "jsdom-no-contextify": "^3.1.0", 53 | "npm-run-all": "^4.0.1", 54 | "npm-watch": "^0.1.8", 55 | "postcss-cssnext": "^2.9.0", 56 | "postcss-triangle": "^1.0.1", 57 | "precss": "^1.4.0", 58 | "rollup": "0.27", 59 | "rollup-plugin-babel": "^2.7.1", 60 | "rollup-plugin-eslint": "^3.0.0", 61 | "rollup-plugin-postcss": "^0.2.0", 62 | "rollup-plugin-uglify": "^1.0.1", 63 | "rollup-watch": "^3.2.2", 64 | "uglify-js": "^3.2.2" 65 | }, 66 | "watch": { 67 | "build:dev": { 68 | "patterns": [ 69 | "src", 70 | "spec" 71 | ], 72 | "extensions": "js,css,scss" 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /inst/htmlwidgets/lib/sequences/README.md: -------------------------------------------------------------------------------- 1 | This example shows how it is possible to use a [D3 sunburst visualization](http://bl.ocks.org/mbostock/4063423) (partition layout) with data that describes sequences of events. 2 | 3 | A good use case is to summarize navigation paths through a web site, as in the sample synthetic data file (visit_sequences.csv). The visualization makes it easy to understand visits that start directly on a product page (e.g. after landing there from a search engine), compared to visits where users arrive on the site's home page and navigate from there. Where a funnel lets you understand a single pre-selected path, this allows you to see all possible paths. 4 | 5 | Features: 6 | 7 | * works with data that is in a CSV format (you don't need to pre-generate a hierarchical JSON file, unless your data file is very large) 8 | * interactive breadcrumb trail helps to emphasize the sequence, so that it is easy for a first-time user to understand what they are seeing 9 | * percentages are shown explicitly, to help overcome the distortion of the data that occurs when using a radial presentation 10 | 11 | If you want to simply reuse this with your own data, here are some tips for generating the CSV file: 12 | 13 | * no header is required (but it's OK if one is present) 14 | * use a hyphen to separate the steps in the sequence 15 | * the step names should be one word only, and ideally should be kept short. Non-alphanumeric characters will probably cause problems (I haven't tested this). 16 | * every sequence should have an "end" marker as the last element, *unless* it has been truncated because it is longer than the maximum sequence length (6, in the example). The purpose of the "end" marker is to distinguish a true end point (e.g. the user left the site) from an end point that has been forced by truncation. 17 | * each line should be a complete path from root to leaf - don't include counts for intermediate steps. For example, include "home-search-end" and "home-search-product-end" but not "home-search" - the latter is computed by the partition layout, by adding up the counts of all the sequences with that prefix. 18 | * to keep the number of permutations low, use a small number of unique step names, and a small maximum sequence length. Larger numbers of either of these will lead to a very large CSV that will be slow to process (and therefore require pre-processing into hierarchical JSON). 19 | 20 | I created this example in my work at Google, but it is not part of any Google product. It is covered by the Apache license: 21 | 22 | > Copyright 2013 Google Inc. All Rights Reserved. 23 | > 24 | > Licensed under the Apache License, Version 2.0 (the "License"); 25 | > you may not use this file except in compliance with the License. 26 | > You may obtain a copy of the License at 27 | > 28 | > http://www.apache.org/licenses/LICENSE-2.0 29 | > 30 | > Unless required by applicable law or agreed to in writing, software 31 | > distributed under the License is distributed on an "AS IS" BASIS, 32 | > WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 33 | > See the License for the specific language governing permissions and 34 | > limitations under the License. 35 | -------------------------------------------------------------------------------- /docs/reference/libs/sequences-0.1/README.md: -------------------------------------------------------------------------------- 1 | This example shows how it is possible to use a [D3 sunburst visualization](http://bl.ocks.org/mbostock/4063423) (partition layout) with data that describes sequences of events. 2 | 3 | A good use case is to summarize navigation paths through a web site, as in the sample synthetic data file (visit_sequences.csv). The visualization makes it easy to understand visits that start directly on a product page (e.g. after landing there from a search engine), compared to visits where users arrive on the site's home page and navigate from there. Where a funnel lets you understand a single pre-selected path, this allows you to see all possible paths. 4 | 5 | Features: 6 | 7 | * works with data that is in a CSV format (you don't need to pre-generate a hierarchical JSON file, unless your data file is very large) 8 | * interactive breadcrumb trail helps to emphasize the sequence, so that it is easy for a first-time user to understand what they are seeing 9 | * percentages are shown explicitly, to help overcome the distortion of the data that occurs when using a radial presentation 10 | 11 | If you want to simply reuse this with your own data, here are some tips for generating the CSV file: 12 | 13 | * no header is required (but it's OK if one is present) 14 | * use a hyphen to separate the steps in the sequence 15 | * the step names should be one word only, and ideally should be kept short. Non-alphanumeric characters will probably cause problems (I haven't tested this). 16 | * every sequence should have an "end" marker as the last element, *unless* it has been truncated because it is longer than the maximum sequence length (6, in the example). The purpose of the "end" marker is to distinguish a true end point (e.g. the user left the site) from an end point that has been forced by truncation. 17 | * each line should be a complete path from root to leaf - don't include counts for intermediate steps. For example, include "home-search-end" and "home-search-product-end" but not "home-search" - the latter is computed by the partition layout, by adding up the counts of all the sequences with that prefix. 18 | * to keep the number of permutations low, use a small number of unique step names, and a small maximum sequence length. Larger numbers of either of these will lead to a very large CSV that will be slow to process (and therefore require pre-processing into hierarchical JSON). 19 | 20 | I created this example in my work at Google, but it is not part of any Google product. It is covered by the Apache license: 21 | 22 | > Copyright 2013 Google Inc. All Rights Reserved. 23 | > 24 | > Licensed under the Apache License, Version 2.0 (the "License"); 25 | > you may not use this file except in compliance with the License. 26 | > You may obtain a copy of the License at 27 | > 28 | > http://www.apache.org/licenses/LICENSE-2.0 29 | > 30 | > Unless required by applicable law or agreed to in writing, software 31 | > distributed under the License is distributed on an "AS IS" BASIS, 32 | > WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 33 | > See the License for the specific language governing permissions and 34 | > limitations under the License. 35 | -------------------------------------------------------------------------------- /docs/articles/sunburst-2-0-0_files/sequences-0.1/README.md: -------------------------------------------------------------------------------- 1 | This example shows how it is possible to use a [D3 sunburst visualization](http://bl.ocks.org/mbostock/4063423) (partition layout) with data that describes sequences of events. 2 | 3 | A good use case is to summarize navigation paths through a web site, as in the sample synthetic data file (visit_sequences.csv). The visualization makes it easy to understand visits that start directly on a product page (e.g. after landing there from a search engine), compared to visits where users arrive on the site's home page and navigate from there. Where a funnel lets you understand a single pre-selected path, this allows you to see all possible paths. 4 | 5 | Features: 6 | 7 | * works with data that is in a CSV format (you don't need to pre-generate a hierarchical JSON file, unless your data file is very large) 8 | * interactive breadcrumb trail helps to emphasize the sequence, so that it is easy for a first-time user to understand what they are seeing 9 | * percentages are shown explicitly, to help overcome the distortion of the data that occurs when using a radial presentation 10 | 11 | If you want to simply reuse this with your own data, here are some tips for generating the CSV file: 12 | 13 | * no header is required (but it's OK if one is present) 14 | * use a hyphen to separate the steps in the sequence 15 | * the step names should be one word only, and ideally should be kept short. Non-alphanumeric characters will probably cause problems (I haven't tested this). 16 | * every sequence should have an "end" marker as the last element, *unless* it has been truncated because it is longer than the maximum sequence length (6, in the example). The purpose of the "end" marker is to distinguish a true end point (e.g. the user left the site) from an end point that has been forced by truncation. 17 | * each line should be a complete path from root to leaf - don't include counts for intermediate steps. For example, include "home-search-end" and "home-search-product-end" but not "home-search" - the latter is computed by the partition layout, by adding up the counts of all the sequences with that prefix. 18 | * to keep the number of permutations low, use a small number of unique step names, and a small maximum sequence length. Larger numbers of either of these will lead to a very large CSV that will be slow to process (and therefore require pre-processing into hierarchical JSON). 19 | 20 | I created this example in my work at Google, but it is not part of any Google product. It is covered by the Apache license: 21 | 22 | > Copyright 2013 Google Inc. All Rights Reserved. 23 | > 24 | > Licensed under the Apache License, Version 2.0 (the "License"); 25 | > you may not use this file except in compliance with the License. 26 | > You may obtain a copy of the License at 27 | > 28 | > http://www.apache.org/licenses/LICENSE-2.0 29 | > 30 | > Unless required by applicable law or agreed to in writing, software 31 | > distributed under the License is distributed on an "AS IS" BASIS, 32 | > WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 33 | > See the License for the specific language governing permissions and 34 | > limitations under the License. 35 | -------------------------------------------------------------------------------- /inst/htmlwidgets/lib/d2b/README.md: -------------------------------------------------------------------------------- 1 | # D2B: Data to DashBoards 2 | 3 | 4 | 5 | A reusable d3-based chart library. 6 | 7 | ## Installing 8 | 9 | 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/dist/d2b.min.js). 10 | 11 | ## API Reference 12 | 13 | You can see the d2b API references [here](http://docs.d2bjs.org). 14 | 15 | ## Optional Dependencies 16 | 17 | - [font awesome icons](http://fontawesome.io/get-started/): Many of the charts use this icon set. 18 | 19 | - [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. 20 | 21 | - [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. 22 | 23 | ## Examples 24 | 25 | You can try out many d2b live code examples [here](http://d2bjs.org). 26 | 27 | If you are using Vue.js there is a [vue-d2b](https://github.com/d2bjs/vue-d2b) plugin that makes using d2b even easier. 28 | 29 |
d2b.chartAxis()
Axis Chart

30 | 31 |
d2b.chartSunburst()
Sunburst Chart

32 | 33 |
d2b.chartSankey()
Sankey Chart

34 | 35 |
d2b.chartPie()
Pie Chart

36 | 37 | ### Some examples of mix and match d2b.chartAxis() generators. 38 | 39 |
40 | 41 |
d2b.svgBoxPlot()
Svg Box Plot

42 | 43 |
d2b.svgBubblePack()
Svg Bubble Pack

44 | 45 |
d2b.svgArea()
Svg Area

46 | 47 |
d2b.svgLine()
Svg Line

48 | 49 |
d2b.svgScatter()
Svg Scatter

50 | 51 |
d2b.svgBar()
Svg Bar

52 | -------------------------------------------------------------------------------- /docs/articles/sunburst-2-0-0_files/d2b-0.5.1/README.md: -------------------------------------------------------------------------------- 1 | # D2B: Data to DashBoards 2 | 3 | 4 | 5 | A reusable d3-based chart library. 6 | 7 | ## Installing 8 | 9 | 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). 10 | 11 | ## API Reference 12 | 13 | You can see the d2b API references [here](http://docs.d2bjs.org). 14 | 15 | ## Optional Dependencies 16 | 17 | - [font awesome icons](http://fontawesome.io/get-started/): Many of the charts use this icon set. 18 | 19 | - [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. 20 | 21 | - [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. 22 | 23 | ## Examples 24 | 25 | You can try out many d2b live code examples [here](http://d2bjs.org). 26 | 27 | If you are using Vue.js there is a [vue-d2b](https://github.com/d2bjs/vue-d2b) plugin that makes using d2b even easier. 28 | 29 |
d2b.chartAxis()
Axis Chart

30 | 31 |
d2b.chartSunburst()
Sunburst Chart

32 | 33 |
d2b.chartSankey()
Sankey Chart

34 | 35 |
d2b.chartPie()
Pie Chart

36 | 37 | ### Some examples of mix and match d2b.chartAxis() generators. 38 | 39 |
40 | 41 |
d2b.svgBoxPlot()
Svg Box Plot

42 | 43 |
d2b.svgBubblePack()
Svg Bubble Pack

44 | 45 |
d2b.svgArea()
Svg Area

46 | 47 |
d2b.svgLine()
Svg Line

48 | 49 |
d2b.svgScatter()
Svg Scatter

50 | 51 |
d2b.svgBar()
Svg Bar

52 | -------------------------------------------------------------------------------- /docs/articles/sunburst-2-0-0_files/d2b-1.0.9/README.md: -------------------------------------------------------------------------------- 1 | # D2B: Data to DashBoards 2 | 3 | 4 | 5 | A reusable d3-based chart library. 6 | 7 | ## Installing 8 | 9 | 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/dist/d2b.min.js). 10 | 11 | ## API Reference 12 | 13 | You can see the d2b API references [here](http://docs.d2bjs.org). 14 | 15 | ## Optional Dependencies 16 | 17 | - [font awesome icons](http://fontawesome.io/get-started/): Many of the charts use this icon set. 18 | 19 | - [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. 20 | 21 | - [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. 22 | 23 | ## Examples 24 | 25 | You can try out many d2b live code examples [here](http://d2bjs.org). 26 | 27 | If you are using Vue.js there is a [vue-d2b](https://github.com/d2bjs/vue-d2b) plugin that makes using d2b even easier. 28 | 29 |
d2b.chartAxis()
Axis Chart

30 | 31 |
d2b.chartSunburst()
Sunburst Chart

32 | 33 |
d2b.chartSankey()
Sankey Chart

34 | 35 |
d2b.chartPie()
Pie Chart

36 | 37 | ### Some examples of mix and match d2b.chartAxis() generators. 38 | 39 |
40 | 41 |
d2b.svgBoxPlot()
Svg Box Plot

42 | 43 |
d2b.svgBubblePack()
Svg Bubble Pack

44 | 45 |
d2b.svgArea()
Svg Area

46 | 47 |
d2b.svgLine()
Svg Line

48 | 49 |
d2b.svgScatter()
Svg Scatter

50 | 51 |
d2b.svgBar()
Svg Bar

52 | -------------------------------------------------------------------------------- /javascript/index.js: -------------------------------------------------------------------------------- 1 | import {dispatch} from 'd3-dispatch'; 2 | import {zip} from 'd3-array'; 3 | import {default as rebind} from './src/rebind.js'; 4 | import {default as draw} from './src/sunburst-chart.js'; 5 | import HTMLWidgets from './global/htmlwidgets'; 6 | 7 | HTMLWidgets.widget({ 8 | 9 | name: 'sunburst', 10 | 11 | type: 'output', 12 | 13 | factory: function(el, width, height) { 14 | 15 | var instance = {}; 16 | 17 | instance.chart = {}; 18 | 19 | var dispatch_ = dispatch("mouseover","mouseleave","click"); 20 | rebind(instance.chart, dispatch_, 'on'); 21 | 22 | // Take a 2-column CSV and transform it into a hierarchical structure suitable 23 | // for a partition layout. The first column is a sequence of step names, from 24 | // root to leaf, separated by hyphens. The second column is a count of how 25 | // often that sequence occurred. 26 | function buildHierarchy(csv) { 27 | var root = {"name": "root", "children": []}; 28 | for (var i = 0; i < csv.length; i++) { 29 | var sequence = csv[i][0]; 30 | var size = +csv[i][1]; 31 | if (isNaN(size)) { // e.g. if this is a header row 32 | continue; 33 | } 34 | var parts = sequence.split("-"); 35 | var currentNode = root; 36 | for (var j = 0; j < parts.length; j++) { 37 | var children = currentNode["children"]; 38 | var nodeName = parts[j]; 39 | var childNode; 40 | if (j + 1 < parts.length) { 41 | // Not yet at the end of the sequence; move down the tree. 42 | var foundChild = false; 43 | for (var k = 0; k < children.length; k++) { 44 | if (children[k]["name"] == nodeName) { 45 | childNode = children[k]; 46 | foundChild = true; 47 | break; 48 | } 49 | } 50 | // If we don't already have a child node for this branch, create it. 51 | if (!foundChild) { 52 | childNode = {"name": nodeName, "children": []}; 53 | children.push(childNode); 54 | } 55 | currentNode = childNode; 56 | } else { 57 | // Reached the end of the sequence; create a leaf node. 58 | childNode = {"name": nodeName, "size": size}; 59 | children.push(childNode); 60 | } 61 | } 62 | } 63 | return root; 64 | 65 | }; 66 | 67 | return { 68 | 69 | renderValue: function(x) { 70 | 71 | instance.x = x; 72 | 73 | // x.data should be a data.frame in R so an Javascript Object of Objects 74 | // but buildHierarchy expects an Array of Arrays 75 | // so use d3.zip and apply to do this 76 | var json = []; 77 | if(typeof(x.csvdata) !== "undefined"){ 78 | json = buildHierarchy( 79 | zip.apply( 80 | null, 81 | Object.keys(x.csvdata).map(function(ky){return x.csvdata[ky]}) 82 | ) 83 | ); 84 | } else { 85 | json = x.data 86 | } 87 | instance.json = json; 88 | 89 | draw(el, instance, dispatch_); 90 | 91 | }, 92 | 93 | resize: function(width, height) { 94 | 95 | draw(el, instance, dispatch_); 96 | 97 | }, 98 | 99 | instance: instance 100 | 101 | }; 102 | } 103 | }); 104 | -------------------------------------------------------------------------------- /vignettes/sunburst-2-0-0.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Sunburst 2.0.0" 3 | author: "Kent Russell" 4 | date: "`r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{Sunburst 2.0.0} 8 | %\VignetteEncoding{UTF-8} 9 | %\VignetteEngine{knitr::rmarkdown} 10 | editor_options: 11 | chunk_output_type: console 12 | --- 13 | 14 | ```{r setup, include = FALSE} 15 | knitr::opts_chunk$set( 16 | collapse = TRUE, 17 | comment = "#>" 18 | ) 19 | ``` 20 | 21 | `sunburstR` goes `2.0.0` with some new features and bug fixes motivated by the feedback and discussion in issues [60](https://github.com/timelyportfolio/sunburstR/issues/60) and [61](https://github.com/timelyportfolio/sunburstR/issues/61). I thought a vignette would be a good way to demonstrate. 22 | 23 | ## Setup and Data 24 | 25 | Let's get started by loading some packages and making some simple data to use throughout the post. 26 | 27 | ```{r} 28 | library(sunburstR) 29 | packageVersion("sunburstR") 30 | ``` 31 | 32 | Simple data should suffice for these examples. 33 | 34 | ```{r} 35 | library(htmltools) 36 | library(d3r) 37 | 38 | dat <- data.frame( 39 | level1 = rep(c("a", "b"), each=3), 40 | level2 = paste0(rep(c("a", "b"), each=3), 1:3), 41 | size = c(10,5,2,3,8,6), 42 | stringsAsFactors = FALSE 43 | ) 44 | 45 | knitr::kable(dat) 46 | ``` 47 | 48 | [`d3r`](https://github.com/timelyportfolio/d3r) will help us build our hierarchy. 49 | 50 | ```{r} 51 | library(d3r) 52 | tree <- d3_nest(dat, value_cols = "size") 53 | tree 54 | ``` 55 | 56 | ## `legend = FALSE` 57 | 58 | Often the legend in the sunburst becomes useless with lots of nodes and multiple levels. While a hack could turn it off, I don't like having to ask users to resort to hacks, so now the argument `legend = FALSE` will turn it off. 59 | 60 | ```{r} 61 | sb1 <- sunburst(tree, width="100%", height=400) 62 | sb2 <- sunburst( 63 | tree, 64 | legend = FALSE, 65 | width = "100%", 66 | height = 400 67 | ) 68 | 69 | # do side-by-side for comparison 70 | div( 71 | style="display: flex; align-items:center;", 72 | div(style="width:50%; border:1px solid #ccc;", sb1), 73 | div(style="width:50%; border:1px solid #ccc;", sb2) 74 | ) 75 | ``` 76 | 77 | ## `d2b` Sunburst 78 | 79 | Kevin Warne has built an incredible sunburst in `d2b`, so I just had to add it to `sunburstR`. I think Kevin's work definitely deserves a star on [Github](https://github.com/d2bjs), and with only 32, I encourage a quick detour to share the love. 80 | 81 | ```{r} 82 | sb3 <- sund2b(tree, width="100%") 83 | 84 | div( 85 | style="display: flex; align-items:center;", 86 | sb3 87 | ) 88 | ``` 89 | 90 | ## Sum Nodes 91 | 92 | I love `treemap`, and I enjoy using the results from `treemap` in a sunburst. However, recent changes resulted in double-counted sums as discussed in this [issue](https://github.com/timelyportfolio/sunburstR/issues/62). This double counting happens when the tree is pre-summed. I added a `sumNodes = FALSE` argument to disable the auto-sum. 93 | 94 | ## Bug Fix 95 | 96 | This is hard to see, but I discovered that the breadcrumbs were duplicated on each resize. This [commit](https://github.com/timelyportfolio/sunburstR/commit/fcb11b002456f2e522b0644d7df09a19332f7c96) shows the bug fix if anyone is interested. 97 | 98 | ## Ideas and Feedback 99 | 100 | Feedback and ideas led to these changes and improvements. Please keep them coming. 101 | -------------------------------------------------------------------------------- /docs/pkgdown.js: -------------------------------------------------------------------------------- 1 | /* http://gregfranko.com/blog/jquery-best-practices/ */ 2 | (function($) { 3 | $(function() { 4 | 5 | $('.navbar-fixed-top').headroom(); 6 | 7 | $('body').css('padding-top', $('.navbar').height() + 10); 8 | $(window).resize(function(){ 9 | $('body').css('padding-top', $('.navbar').height() + 10); 10 | }); 11 | 12 | $('[data-toggle="tooltip"]').tooltip(); 13 | 14 | var cur_path = paths(location.pathname); 15 | var links = $("#navbar ul li a"); 16 | var max_length = -1; 17 | var pos = -1; 18 | for (var i = 0; i < links.length; i++) { 19 | if (links[i].getAttribute("href") === "#") 20 | continue; 21 | // Ignore external links 22 | if (links[i].host !== location.host) 23 | continue; 24 | 25 | var nav_path = paths(links[i].pathname); 26 | 27 | var length = prefix_length(nav_path, cur_path); 28 | if (length > max_length) { 29 | max_length = length; 30 | pos = i; 31 | } 32 | } 33 | 34 | // Add class to parent
  • , and enclosing
  • if in dropdown 35 | if (pos >= 0) { 36 | var menu_anchor = $(links[pos]); 37 | menu_anchor.parent().addClass("active"); 38 | menu_anchor.closest("li.dropdown").addClass("active"); 39 | } 40 | }); 41 | 42 | function paths(pathname) { 43 | var pieces = pathname.split("/"); 44 | pieces.shift(); // always starts with / 45 | 46 | var end = pieces[pieces.length - 1]; 47 | if (end === "index.html" || end === "") 48 | pieces.pop(); 49 | return(pieces); 50 | } 51 | 52 | // Returns -1 if not found 53 | function prefix_length(needle, haystack) { 54 | if (needle.length > haystack.length) 55 | return(-1); 56 | 57 | // Special case for length-0 haystack, since for loop won't run 58 | if (haystack.length === 0) { 59 | return(needle.length === 0 ? 0 : -1); 60 | } 61 | 62 | for (var i = 0; i < haystack.length; i++) { 63 | if (needle[i] != haystack[i]) 64 | return(i); 65 | } 66 | 67 | return(haystack.length); 68 | } 69 | 70 | /* Clipboard --------------------------*/ 71 | 72 | function changeTooltipMessage(element, msg) { 73 | var tooltipOriginalTitle=element.getAttribute('data-original-title'); 74 | element.setAttribute('data-original-title', msg); 75 | $(element).tooltip('show'); 76 | element.setAttribute('data-original-title', tooltipOriginalTitle); 77 | } 78 | 79 | if(ClipboardJS.isSupported()) { 80 | $(document).ready(function() { 81 | var copyButton = ""; 82 | 83 | $("div.sourceCode").addClass("hasCopyButton"); 84 | 85 | // Insert copy buttons: 86 | $(copyButton).prependTo(".hasCopyButton"); 87 | 88 | // Initialize tooltips: 89 | $('.btn-copy-ex').tooltip({container: 'body'}); 90 | 91 | // Initialize clipboard: 92 | var clipboardBtnCopies = new ClipboardJS('[data-clipboard-copy]', { 93 | text: function(trigger) { 94 | return trigger.parentNode.textContent.replace(/\n#>[^\n]*/g, ""); 95 | } 96 | }); 97 | 98 | clipboardBtnCopies.on('success', function(e) { 99 | changeTooltipMessage(e.trigger, 'Copied!'); 100 | e.clearSelection(); 101 | }); 102 | 103 | clipboardBtnCopies.on('error', function() { 104 | changeTooltipMessage(e.trigger,'Press Ctrl+C or Command+C to copy'); 105 | }); 106 | }); 107 | } 108 | })(window.jQuery || window.$) 109 | -------------------------------------------------------------------------------- /man/sund2b.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/d2b.R 3 | \name{sund2b} 4 | \alias{sund2b} 5 | \title{Sunburst Using 'd2b'} 6 | \usage{ 7 | sund2b( 8 | data = NULL, 9 | colors = NULL, 10 | valueField = "size", 11 | tooltip = NULL, 12 | breadcrumbs = NULL, 13 | rootLabel = NULL, 14 | showLabels = FALSE, 15 | width = NULL, 16 | height = NULL, 17 | elementId = NULL 18 | ) 19 | } 20 | \arguments{ 21 | \item{data}{data in csv source,target form or in 22 | nested d3 JSON hierarchy with `{name:..., children:[];}`. \code{list}, \code{character}, 23 | or \code{connection} data will be assumed to be \code{JSON}. 24 | \code{data.frame} data will be assumed to be \code{csvdata} and converted 25 | to \code{JSON} by \code{sunburstR:::csv_to_hier()}.} 26 | 27 | \item{colors}{\code{vector} of strings representing colors as hexadecimal for 28 | manual colors. If you want precise control of colors, supply a \code{list} 29 | with \code{range} and/or \code{domain}. For advanced customization, supply 30 | a JavaScript \code{function}.} 31 | 32 | \item{valueField}{\code{character} for the field to use to calculate size. The default 33 | value is \code{"size"}.} 34 | 35 | \item{tooltip}{\code{list} of options for customizing the tooltip. See the helper 36 | function \code{\link{sund2bTooltip}} for more information.} 37 | 38 | \item{breadcrumbs}{\code{list} of options for customizing the breadcrumb. See the helper 39 | function \code{\link{sund2bBreadcrumb}} for more information.} 40 | 41 | \item{rootLabel}{\code{character} to label root node something other than 'root'.} 42 | 43 | \item{showLabels}{\code{logical} to show labels on the slices. The default is \code{FALSE}.} 44 | 45 | \item{height, width}{height and width of sunburst htmlwidget containing div 46 | specified in any valid \code{CSS} size unit.} 47 | 48 | \item{elementId}{string id as a valid \code{CSS} element id.} 49 | } 50 | \description{ 51 | Create interactive sunburst chart with the 'd2b' charting library. 52 | } 53 | \examples{ 54 | if(interactive()){ 55 | 56 | # The sund2b() API mirrors sunburst() with fewer arguments. 57 | 58 | library(sunburstR) 59 | 60 | # use a sample of the sequences csv data 61 | sequences <- read.csv( 62 | system.file("examples/visit-sequences.csv",package="sunburstR") 63 | ,header = FALSE 64 | ,stringsAsFactors = FALSE 65 | )[1:200,] 66 | 67 | # create a d2b sunburst 68 | sund2b(sequences) 69 | 70 | # show labels 71 | sund2b(sequences, showLabels = TRUE) 72 | 73 | # change the colors 74 | # using d3.js categorical color scheme 75 | sund2b( 76 | sequences, 77 | colors = htmlwidgets::JS("d3.scaleOrdinal(d3.schemeCategory20b)") 78 | ) 79 | } 80 | 81 | \dontrun{ 82 | # using RColorBrewer palette 83 | sund2b( 84 | sequences, 85 | colors = list(range = RColorBrewer::brewer.pal(9, "Set3")) 86 | ) 87 | # using a color column from the R dataset 88 | # treemap has an amazing treecolors ability 89 | library(treemap) 90 | library(d3r) 91 | rhd <- random.hierarchical.data() 92 | tm <- treemap( 93 | rhd, 94 | index = paste0("index", 1:3), 95 | vSize = "x", 96 | draw = FALSE 97 | )$tm 98 | sund2b( 99 | d3_nest(tm, value_cols = colnames(tm)[-(1:3)]), 100 | colors = htmlwidgets::JS( 101 | # yes this is a little different, so please pay attention 102 | # "function(d) {return d.color}" will not work 103 | "function(name, d){return d.color || '#ccc';}" 104 | ), 105 | valueField = "vSize" 106 | ) 107 | 108 | 109 | # use sund2b in Shiny 110 | library(shiny) 111 | ui <- sund2bOutput("sun") 112 | server <- function(input, output, session) { 113 | output$sun <- renderSund2b({ 114 | sund2b(sequences) 115 | }) 116 | } 117 | shinyApp(ui, server) 118 | 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /docs/articles/sunburst-2-0-0_files/sund2b-binding-2.1.1/sund2b.js: -------------------------------------------------------------------------------- 1 | HTMLWidgets.widget({ 2 | 3 | name: 'sund2b', 4 | 5 | type: 'output', 6 | 7 | factory: function(el, width, height) { 8 | 9 | var sunburst = d2b.chartSunburst(); 10 | // make this publicly available for later customization by user 11 | var instance = { 12 | chart: sunburst 13 | }; 14 | 15 | return { 16 | 17 | renderValue: function(x) { 18 | 19 | var colors = d3.scaleOrdinal(d3.schemeCategory20); 20 | 21 | if(x.options.colors !== null){ 22 | // if an array then we assume the colors 23 | // represent an array of hexadecimal colors to be used 24 | if(Array.isArray(x.options.colors)) { 25 | try{ 26 | colors.range(x.options.colors) 27 | } catch(e) { 28 | 29 | } 30 | } 31 | 32 | // if an object with range then we assume 33 | // that this is an array of colors to be used as range 34 | if(x.options.colors.range){ 35 | try{ 36 | colors.range(x.options.colors.range) 37 | } catch(e) { 38 | 39 | } 40 | } 41 | 42 | // if an object with domain then we assume 43 | // that this is an array of colors to be used as domain 44 | // for more precise control of the colors assigned 45 | if(x.options.colors.domain){ 46 | try{ 47 | colors.domain(x.options.colors.domain); 48 | } catch(e) { 49 | 50 | } 51 | } 52 | 53 | // if a function then set to the function 54 | if(typeof(x.options.colors) === "function") { 55 | colors = x.options.colors; 56 | } 57 | } 58 | 59 | sunburst.chartFrame().size({height: height}); 60 | sunburst.label(function(d) { 61 | return d.name; 62 | }); 63 | sunburst.color(function(d) { 64 | return colors.call(this, d.name, d); 65 | }); 66 | sunburst.sunburst().size(function(d) { 67 | return d[x.options.valueField || "size"]; 68 | }); 69 | 70 | d3.select(el).datum(x.data) 71 | .transition() 72 | .call(sunburst); 73 | 74 | 75 | // set up a container for tasks to perform after completion 76 | // one example would be add callbacks for event handling 77 | // styling 78 | if (!(typeof x.tasks === "undefined") ){ 79 | if ( (typeof x.tasks.length === "undefined") || 80 | (typeof x.tasks === "function" ) ) { 81 | // handle a function not enclosed in array 82 | // should be able to remove once using jsonlite 83 | x.tasks = [x.tasks]; 84 | } 85 | x.tasks.map(function(t){ 86 | // for each tasks call the task with el supplied as `this` 87 | t.call({el:el,x:x,instance:instance}); 88 | }); 89 | } 90 | 91 | }, 92 | 93 | resize: function(width, height) { 94 | 95 | sunburst.chartFrame().size({height: height}); 96 | d3.select(el).call(sunburst); 97 | 98 | // set up a container for tasks to perform after completion 99 | // one example would be add callbacks for event handling 100 | // styling 101 | if (!(typeof x.tasks === "undefined") ){ 102 | if ( (typeof x.tasks.length === "undefined") || 103 | (typeof x.tasks === "function" ) ) { 104 | // handle a function not enclosed in array 105 | // should be able to remove once using jsonlite 106 | x.tasks = [x.tasks]; 107 | } 108 | x.tasks.map(function(t){ 109 | // for each tasks call the task with el supplied as `this` 110 | t.call({el:el,x:x,instance:instance}); 111 | }); 112 | } 113 | }, 114 | 115 | instance: instance 116 | 117 | }; 118 | } 119 | }); 120 | -------------------------------------------------------------------------------- /docs/articles/sunburst-2-0-0_files/sund2b-binding-2.1.2/sund2b.js: -------------------------------------------------------------------------------- 1 | HTMLWidgets.widget({ 2 | 3 | name: 'sund2b', 4 | 5 | type: 'output', 6 | 7 | factory: function(el, width, height) { 8 | 9 | var sunburst = d2b.chartSunburst(); 10 | // make this publicly available for later customization by user 11 | var instance = { 12 | chart: sunburst 13 | }; 14 | 15 | return { 16 | 17 | renderValue: function(x) { 18 | 19 | var colors = d3.scaleOrdinal(d3.schemeCategory20); 20 | 21 | if(x.options.colors !== null){ 22 | // if an array then we assume the colors 23 | // represent an array of hexadecimal colors to be used 24 | if(Array.isArray(x.options.colors)) { 25 | try{ 26 | colors.range(x.options.colors) 27 | } catch(e) { 28 | 29 | } 30 | } 31 | 32 | // if an object with range then we assume 33 | // that this is an array of colors to be used as range 34 | if(x.options.colors.range){ 35 | try{ 36 | colors.range(x.options.colors.range) 37 | } catch(e) { 38 | 39 | } 40 | } 41 | 42 | // if an object with domain then we assume 43 | // that this is an array of colors to be used as domain 44 | // for more precise control of the colors assigned 45 | if(x.options.colors.domain){ 46 | try{ 47 | colors.domain(x.options.colors.domain); 48 | } catch(e) { 49 | 50 | } 51 | } 52 | 53 | // if a function then set to the function 54 | if(typeof(x.options.colors) === "function") { 55 | colors = x.options.colors; 56 | } 57 | } 58 | 59 | sunburst.chartFrame().size({height: height}); 60 | sunburst.label(function(d) { 61 | return d.name; 62 | }); 63 | sunburst.color(function(d) { 64 | return colors.call(this, d.name, d); 65 | }); 66 | sunburst.sunburst().size(function(d) { 67 | return d[x.options.valueField || "size"]; 68 | }); 69 | 70 | d3.select(el).datum(x.data) 71 | .transition() 72 | .call(sunburst.advanced); 73 | 74 | 75 | // set up a container for tasks to perform after completion 76 | // one example would be add callbacks for event handling 77 | // styling 78 | if (!(typeof x.tasks === "undefined") ){ 79 | if ( (typeof x.tasks.length === "undefined") || 80 | (typeof x.tasks === "function" ) ) { 81 | // handle a function not enclosed in array 82 | // should be able to remove once using jsonlite 83 | x.tasks = [x.tasks]; 84 | } 85 | x.tasks.map(function(t){ 86 | // for each tasks call the task with el supplied as `this` 87 | t.call({el:el,x:x,instance:instance}); 88 | }); 89 | } 90 | 91 | }, 92 | 93 | resize: function(width, height) { 94 | 95 | sunburst.chartFrame().size({height: height}); 96 | d3.select(el).call(sunburst); 97 | 98 | // set up a container for tasks to perform after completion 99 | // one example would be add callbacks for event handling 100 | // styling 101 | if (!(typeof x.tasks === "undefined") ){ 102 | if ( (typeof x.tasks.length === "undefined") || 103 | (typeof x.tasks === "function" ) ) { 104 | // handle a function not enclosed in array 105 | // should be able to remove once using jsonlite 106 | x.tasks = [x.tasks]; 107 | } 108 | x.tasks.map(function(t){ 109 | // for each tasks call the task with el supplied as `this` 110 | t.call({el:el,x:x,instance:instance}); 111 | }); 112 | } 113 | }, 114 | 115 | instance: instance 116 | 117 | }; 118 | } 119 | }); 120 | -------------------------------------------------------------------------------- /docs/articles/sunburst-2-0-0_files/sund2b-binding-2.1.3/sund2b.js: -------------------------------------------------------------------------------- 1 | HTMLWidgets.widget({ 2 | 3 | name: 'sund2b', 4 | 5 | type: 'output', 6 | 7 | factory: function(el, width, height) { 8 | 9 | var sunburst = d2b.chartSunburst(); 10 | // make this publicly available for later customization by user 11 | var instance = { 12 | chart: sunburst 13 | }; 14 | 15 | return { 16 | 17 | renderValue: function(x) { 18 | 19 | var colors = d3.scaleOrdinal(d3.schemeCategory20); 20 | 21 | if(x.options.colors !== null){ 22 | // if an array then we assume the colors 23 | // represent an array of hexadecimal colors to be used 24 | if(Array.isArray(x.options.colors)) { 25 | try{ 26 | colors.range(x.options.colors) 27 | } catch(e) { 28 | 29 | } 30 | } 31 | 32 | // if an object with range then we assume 33 | // that this is an array of colors to be used as range 34 | if(x.options.colors.range){ 35 | try{ 36 | colors.range(x.options.colors.range) 37 | } catch(e) { 38 | 39 | } 40 | } 41 | 42 | // if an object with domain then we assume 43 | // that this is an array of colors to be used as domain 44 | // for more precise control of the colors assigned 45 | if(x.options.colors.domain){ 46 | try{ 47 | colors.domain(x.options.colors.domain); 48 | } catch(e) { 49 | 50 | } 51 | } 52 | 53 | // if a function then set to the function 54 | if(typeof(x.options.colors) === "function") { 55 | colors = x.options.colors; 56 | } 57 | } 58 | 59 | sunburst.chartFrame().size({height: height}); 60 | sunburst.label(function(d) { 61 | return d.name; 62 | }); 63 | sunburst.color(function(d) { 64 | return colors.call(this, d.name, d); 65 | }); 66 | sunburst.sunburst().size(function(d) { 67 | return d[x.options.valueField || "size"]; 68 | }); 69 | 70 | if(x.options.hasOwnProperty("rootLabel") && x.options.rootLabel !== null) { 71 | x.data.root.label = x.options.rootLabel; 72 | } 73 | 74 | d3.select(el).datum(x.data) 75 | .transition() 76 | .call(sunburst.advanced); 77 | 78 | 79 | // set up a container for tasks to perform after completion 80 | // one example would be add callbacks for event handling 81 | // styling 82 | if (!(typeof x.tasks === "undefined") ){ 83 | if ( (typeof x.tasks.length === "undefined") || 84 | (typeof x.tasks === "function" ) ) { 85 | // handle a function not enclosed in array 86 | // should be able to remove once using jsonlite 87 | x.tasks = [x.tasks]; 88 | } 89 | x.tasks.map(function(t){ 90 | // for each tasks call the task with el supplied as `this` 91 | t.call({el:el,x:x,instance:instance}); 92 | }); 93 | } 94 | 95 | }, 96 | 97 | resize: function(width, height) { 98 | 99 | sunburst.chartFrame().size({height: height}); 100 | d3.select(el).call(sunburst); 101 | 102 | // set up a container for tasks to perform after completion 103 | // one example would be add callbacks for event handling 104 | // styling 105 | if (!(typeof x.tasks === "undefined") ){ 106 | if ( (typeof x.tasks.length === "undefined") || 107 | (typeof x.tasks === "function" ) ) { 108 | // handle a function not enclosed in array 109 | // should be able to remove once using jsonlite 110 | x.tasks = [x.tasks]; 111 | } 112 | x.tasks.map(function(t){ 113 | // for each tasks call the task with el supplied as `this` 114 | t.call({el:el,x:x,instance:instance}); 115 | }); 116 | } 117 | }, 118 | 119 | instance: instance 120 | 121 | }; 122 | } 123 | }); 124 | -------------------------------------------------------------------------------- /docs/articles/sunburst-2-0-0_files/sund2b-binding-2.1.4/sund2b.js: -------------------------------------------------------------------------------- 1 | HTMLWidgets.widget({ 2 | 3 | name: 'sund2b', 4 | 5 | type: 'output', 6 | 7 | factory: function(el, width, height) { 8 | 9 | var sunburst = d2b.chartSunburst(); 10 | // make this publicly available for later customization by user 11 | var instance = { 12 | chart: sunburst 13 | }; 14 | 15 | return { 16 | 17 | renderValue: function(x) { 18 | 19 | var colors = d3.scaleOrdinal(d3.schemeCategory20); 20 | 21 | if(x.options.colors !== null){ 22 | // if an array then we assume the colors 23 | // represent an array of hexadecimal colors to be used 24 | if(Array.isArray(x.options.colors)) { 25 | try{ 26 | colors.range(x.options.colors) 27 | } catch(e) { 28 | 29 | } 30 | } 31 | 32 | // if an object with range then we assume 33 | // that this is an array of colors to be used as range 34 | if(x.options.colors.range){ 35 | try{ 36 | colors.range(x.options.colors.range) 37 | } catch(e) { 38 | 39 | } 40 | } 41 | 42 | // if an object with domain then we assume 43 | // that this is an array of colors to be used as domain 44 | // for more precise control of the colors assigned 45 | if(x.options.colors.domain){ 46 | try{ 47 | colors.domain(x.options.colors.domain); 48 | } catch(e) { 49 | 50 | } 51 | } 52 | 53 | // if a function then set to the function 54 | if(typeof(x.options.colors) === "function") { 55 | colors = x.options.colors; 56 | } 57 | } 58 | 59 | sunburst.chartFrame().size({height: height}); 60 | sunburst.label(function(d) { 61 | return d.name; 62 | }); 63 | sunburst.color(function(d) { 64 | return colors.call(this, d.name, d); 65 | }); 66 | sunburst.sunburst().size(function(d) { 67 | return d[x.options.valueField || "size"]; 68 | }); 69 | if( 70 | x.options.hasOwnProperty("showLabels") && 71 | (x.options.showLabels===true || x.options.showLabels===false ) 72 | ) { 73 | sunburst.sunburst().showLabels(x.options.showLabels); 74 | } 75 | 76 | if(x.options.hasOwnProperty("rootLabel") && x.options.rootLabel !== null) { 77 | x.data.root.label = x.options.rootLabel; 78 | } 79 | 80 | d3.select(el).datum(x.data) 81 | .transition() 82 | .call(sunburst.advanced); 83 | 84 | 85 | // set up a container for tasks to perform after completion 86 | // one example would be add callbacks for event handling 87 | // styling 88 | if (!(typeof x.tasks === "undefined") ){ 89 | if ( (typeof x.tasks.length === "undefined") || 90 | (typeof x.tasks === "function" ) ) { 91 | // handle a function not enclosed in array 92 | // should be able to remove once using jsonlite 93 | x.tasks = [x.tasks]; 94 | } 95 | x.tasks.map(function(t){ 96 | // for each tasks call the task with el supplied as `this` 97 | t.call({el:el,x:x,instance:instance}); 98 | }); 99 | } 100 | 101 | }, 102 | 103 | resize: function(width, height) { 104 | 105 | sunburst.chartFrame().size({height: height}); 106 | d3.select(el).call(sunburst); 107 | 108 | // set up a container for tasks to perform after completion 109 | // one example would be add callbacks for event handling 110 | // styling 111 | if (!(typeof x.tasks === "undefined") ){ 112 | if ( (typeof x.tasks.length === "undefined") || 113 | (typeof x.tasks === "function" ) ) { 114 | // handle a function not enclosed in array 115 | // should be able to remove once using jsonlite 116 | x.tasks = [x.tasks]; 117 | } 118 | x.tasks.map(function(t){ 119 | // for each tasks call the task with el supplied as `this` 120 | t.call({el:el,x:x,instance:instance}); 121 | }); 122 | } 123 | }, 124 | 125 | instance: instance 126 | 127 | }; 128 | } 129 | }); 130 | -------------------------------------------------------------------------------- /inst/htmlwidgets/lib/d2b/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "d2b", 3 | "version": "1.0.9", 4 | "author": { 5 | "name": "Kevin Warne" 6 | }, 7 | "description": "A d3-based reusable chart library.", 8 | "keywords": [ 9 | "d3", 10 | "d3-module", 11 | "chart", 12 | "charts", 13 | "pie", 14 | "axis", 15 | "line", 16 | "area", 17 | "bar", 18 | "stacked", 19 | "tooltip" 20 | ], 21 | "license": "MIT", 22 | "main": "dist/d2b.cjs.js", 23 | "unpkg": "dist/d2b.min.js", 24 | "jsdelivr": "dist/d2b.min.js", 25 | "homepage": "http://d2bjs.org", 26 | "repository": { 27 | "type": "git", 28 | "url": "https://github.com/d2bjs/d2b" 29 | }, 30 | "scripts": { 31 | "rollup": "rollup -c -g d3:d3,d3-transition:d3,d3-sankey:d3,d3-interpolate-path:d3,d3-scale-chromatic:d3,d3-hierarchy:d3,d3-svg-annotation:d3,d3-format:d3,d3-selection:d3,d3-drag:d3,d3-scale:d3,d3-axis:d3,d3-collection:d3,d3-array:d3,d3-color:d3,d3-shape:d3,d3-dispatch:d3,d3-interpolate:d3", 32 | "build:dev": "npm run build:package && npm run rollup", 33 | "build:prod": "npm run build:package && NODE_ENV=production npm run rollup", 34 | "build": "npm run build:dev && npm run build:prod", 35 | "build:server": "http-server -a 0.0.0.0 -p 8000", 36 | "build:watcher": "npm-watch", 37 | "build:package": "json2module package.json > dist/package.js", 38 | "build:tsdemo": "watchify ./examples/chartAdvanced/index.ts -p [ tsify ] -o ./examples/chartAdvanced/index.js", 39 | "build:watch": "npm-run-all --parallel build:watcher build:server", 40 | "build:tsdocs": "typedoc ./src/types/index.ts --json ./docs/typedoc.json && node ./docs/typedocToMarkdown.js", 41 | "start": "npm run build:server", 42 | "test": "jasmine", 43 | "docs:prepare": "gitbook install && npm run build:tsdocs", 44 | "docs:watch": "npm run docs:prepare && gitbook serve", 45 | "docs:build": "npm run docs:prepare && rm -rf _book && gitbook build", 46 | "docs:publish": "npm run docs:build && cp docs/cname _book && cd _book && git init && git commit --allow-empty -m 'Update docs' && git checkout master && git add . && git commit -am 'Update docs' && git push git@github.com:d2bjs/d2bjs.github.io master --force" 47 | }, 48 | "dependencies": { 49 | "d3-array": "^2.0.3", 50 | "d3-axis": "^1.0.12", 51 | "d3-collection": "^1.0.7", 52 | "d3-color": "^1.2.3", 53 | "d3-dispatch": "^1.0.5", 54 | "d3-drag": "^1.2.3", 55 | "d3-format": "^1.3.2", 56 | "d3-hierarchy": "^1.1.8", 57 | "d3-interpolate": "^1.3.2", 58 | "d3-interpolate-path": ">2.0.0", 59 | "d3-queue": "^3.0.7", 60 | "d3-sankey": "^0.12.1", 61 | "d3-scale": "^3.0.0", 62 | "d3-scale-chromatic": "^1.3.3", 63 | "d3-selection": "^1.4.0", 64 | "d3-shape": "^1.3.5", 65 | "d3-svg-annotation": "^2.4.0", 66 | "d3-transition": "^1.2.0" 67 | }, 68 | "devDependencies": { 69 | "@babel/core": "^7.4.0", 70 | "@babel/preset-env": "^7.4.2", 71 | "@types/d3": "^5.7.1", 72 | "@types/node": "^11.12.0", 73 | "babelify": "^10.0.0", 74 | "browserify": "^16.2.3", 75 | "cssnano": "^4.1.10", 76 | "d3": "^5.9.2", 77 | "gitbook": "^3.2.3", 78 | "gitbook-plugin-custom-favicon": "0.0.4", 79 | "gitbook-plugin-ga": "^1.0.1", 80 | "gitbook-plugin-toggle-chapters": "0.0.3", 81 | "http-server": "^0.11.1", 82 | "jasmine": "2.5.3", 83 | "jsdom-no-contextify": "^3.1.0", 84 | "json2module": "^0.0.3", 85 | "node-sass": "^4.11.0", 86 | "npm-run-all": "^4.1.5", 87 | "npm-watch": "^0.1.8", 88 | "postcss-preset-env": "^6.6.0", 89 | "postcss-triangle": "^1.0.1", 90 | "postcss-utilities": "^0.8.0", 91 | "precss": "^1.4.0", 92 | "rollup": "^1.7.3", 93 | "rollup-plugin-babel": "^4.3.2", 94 | "rollup-plugin-eslint": "^5.1.0", 95 | "rollup-plugin-postcss": "^2.0.3", 96 | "rollup-plugin-terser": "^4.0.4", 97 | "rollup-plugin-uglify": "^6.0.2", 98 | "styled-jsx": "^3.2.1", 99 | "styled-jsx-plugin-postcss": "^2.0.0", 100 | "tsify": "^4.0.1", 101 | "typescript": "^3.3.4000", 102 | "uglify-js": "^3.5.2", 103 | "gitbook-cli": "^2.3.2" 104 | }, 105 | "files": [ 106 | "/dist", 107 | "/src" 108 | ], 109 | "watch": { 110 | "build:dev": { 111 | "patterns": [ 112 | "src", 113 | "spec" 114 | ], 115 | "extensions": "js,css,scss,ts" 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /docs/articles/sunburst-2-0-0_files/d2b-1.0.9/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "d2b", 3 | "version": "1.0.9", 4 | "author": { 5 | "name": "Kevin Warne" 6 | }, 7 | "description": "A d3-based reusable chart library.", 8 | "keywords": [ 9 | "d3", 10 | "d3-module", 11 | "chart", 12 | "charts", 13 | "pie", 14 | "axis", 15 | "line", 16 | "area", 17 | "bar", 18 | "stacked", 19 | "tooltip" 20 | ], 21 | "license": "MIT", 22 | "main": "dist/d2b.cjs.js", 23 | "unpkg": "dist/d2b.min.js", 24 | "jsdelivr": "dist/d2b.min.js", 25 | "homepage": "http://d2bjs.org", 26 | "repository": { 27 | "type": "git", 28 | "url": "https://github.com/d2bjs/d2b" 29 | }, 30 | "scripts": { 31 | "rollup": "rollup -c -g d3:d3,d3-transition:d3,d3-sankey:d3,d3-interpolate-path:d3,d3-scale-chromatic:d3,d3-hierarchy:d3,d3-svg-annotation:d3,d3-format:d3,d3-selection:d3,d3-drag:d3,d3-scale:d3,d3-axis:d3,d3-collection:d3,d3-array:d3,d3-color:d3,d3-shape:d3,d3-dispatch:d3,d3-interpolate:d3", 32 | "build:dev": "npm run build:package && npm run rollup", 33 | "build:prod": "npm run build:package && NODE_ENV=production npm run rollup", 34 | "build": "npm run build:dev && npm run build:prod", 35 | "build:server": "http-server -a 0.0.0.0 -p 8000", 36 | "build:watcher": "npm-watch", 37 | "build:package": "json2module package.json > dist/package.js", 38 | "build:tsdemo": "watchify ./examples/chartAdvanced/index.ts -p [ tsify ] -o ./examples/chartAdvanced/index.js", 39 | "build:watch": "npm-run-all --parallel build:watcher build:server", 40 | "build:tsdocs": "typedoc ./src/types/index.ts --json ./docs/typedoc.json && node ./docs/typedocToMarkdown.js", 41 | "start": "npm run build:server", 42 | "test": "jasmine", 43 | "docs:prepare": "gitbook install && npm run build:tsdocs", 44 | "docs:watch": "npm run docs:prepare && gitbook serve", 45 | "docs:build": "npm run docs:prepare && rm -rf _book && gitbook build", 46 | "docs:publish": "npm run docs:build && cp docs/cname _book && cd _book && git init && git commit --allow-empty -m 'Update docs' && git checkout master && git add . && git commit -am 'Update docs' && git push git@github.com:d2bjs/d2bjs.github.io master --force" 47 | }, 48 | "dependencies": { 49 | "d3-array": "^2.0.3", 50 | "d3-axis": "^1.0.12", 51 | "d3-collection": "^1.0.7", 52 | "d3-color": "^1.2.3", 53 | "d3-dispatch": "^1.0.5", 54 | "d3-drag": "^1.2.3", 55 | "d3-format": "^1.3.2", 56 | "d3-hierarchy": "^1.1.8", 57 | "d3-interpolate": "^1.3.2", 58 | "d3-interpolate-path": ">2.0.0", 59 | "d3-queue": "^3.0.7", 60 | "d3-sankey": "^0.12.1", 61 | "d3-scale": "^3.0.0", 62 | "d3-scale-chromatic": "^1.3.3", 63 | "d3-selection": "^1.4.0", 64 | "d3-shape": "^1.3.5", 65 | "d3-svg-annotation": "^2.4.0", 66 | "d3-transition": "^1.2.0" 67 | }, 68 | "devDependencies": { 69 | "@babel/core": "^7.4.0", 70 | "@babel/preset-env": "^7.4.2", 71 | "@types/d3": "^5.7.1", 72 | "@types/node": "^11.12.0", 73 | "babelify": "^10.0.0", 74 | "browserify": "^16.2.3", 75 | "cssnano": "^4.1.10", 76 | "d3": "^5.9.2", 77 | "gitbook": "^3.2.3", 78 | "gitbook-plugin-custom-favicon": "0.0.4", 79 | "gitbook-plugin-ga": "^1.0.1", 80 | "gitbook-plugin-toggle-chapters": "0.0.3", 81 | "http-server": "^0.11.1", 82 | "jasmine": "2.5.3", 83 | "jsdom-no-contextify": "^3.1.0", 84 | "json2module": "^0.0.3", 85 | "node-sass": "^4.11.0", 86 | "npm-run-all": "^4.1.5", 87 | "npm-watch": "^0.1.8", 88 | "postcss-preset-env": "^6.6.0", 89 | "postcss-triangle": "^1.0.1", 90 | "postcss-utilities": "^0.8.0", 91 | "precss": "^1.4.0", 92 | "rollup": "^1.7.3", 93 | "rollup-plugin-babel": "^4.3.2", 94 | "rollup-plugin-eslint": "^5.1.0", 95 | "rollup-plugin-postcss": "^2.0.3", 96 | "rollup-plugin-terser": "^4.0.4", 97 | "rollup-plugin-uglify": "^6.0.2", 98 | "styled-jsx": "^3.2.1", 99 | "styled-jsx-plugin-postcss": "^2.0.0", 100 | "tsify": "^4.0.1", 101 | "typescript": "^3.3.4000", 102 | "uglify-js": "^3.5.2", 103 | "gitbook-cli": "^2.3.2" 104 | }, 105 | "files": [ 106 | "/dist", 107 | "/src" 108 | ], 109 | "watch": { 110 | "build:dev": { 111 | "patterns": [ 112 | "src", 113 | "spec" 114 | ], 115 | "extensions": "js,css,scss,ts" 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /inst/htmlwidgets/sund2b.js: -------------------------------------------------------------------------------- 1 | HTMLWidgets.widget({ 2 | 3 | name: 'sund2b', 4 | 5 | type: 'output', 6 | 7 | factory: function(el, width, height) { 8 | 9 | var sunburst = d2b.chartSunburst(); 10 | // make this publicly available for later customization by user 11 | var instance = { 12 | chart: sunburst 13 | }; 14 | 15 | return { 16 | 17 | renderValue: function(x) { 18 | 19 | var colors = d3.scaleOrdinal(d3.schemeCategory20); 20 | 21 | if(x.options.colors !== null){ 22 | // if an array then we assume the colors 23 | // represent an array of hexadecimal colors to be used 24 | if(Array.isArray(x.options.colors)) { 25 | try{ 26 | colors.range(x.options.colors) 27 | } catch(e) { 28 | 29 | } 30 | } 31 | 32 | // if an object with range then we assume 33 | // that this is an array of colors to be used as range 34 | if(x.options.colors.range){ 35 | try{ 36 | colors.range(x.options.colors.range) 37 | } catch(e) { 38 | 39 | } 40 | } 41 | 42 | // if an object with domain then we assume 43 | // that this is an array of colors to be used as domain 44 | // for more precise control of the colors assigned 45 | if(x.options.colors.domain){ 46 | try{ 47 | colors.domain(x.options.colors.domain); 48 | } catch(e) { 49 | 50 | } 51 | } 52 | 53 | // if a function then set to the function 54 | if(typeof(x.options.colors) === "function") { 55 | colors = x.options.colors; 56 | } 57 | } 58 | 59 | sunburst.chartFrame().size({height: height}); 60 | sunburst.label(function(d) { 61 | return d.name; 62 | }); 63 | sunburst.color(function(d) { 64 | return colors.call(this, d.name, d); 65 | }); 66 | sunburst.sunburst().size(function(d) { 67 | return d[x.options.valueField || "size"]; 68 | }); 69 | if( 70 | x.options.hasOwnProperty("showLabels") && 71 | (x.options.showLabels===true || x.options.showLabels===false ) 72 | ) { 73 | sunburst.sunburst().showLabels(x.options.showLabels); 74 | } 75 | 76 | if(x.options.hasOwnProperty("rootLabel") && x.options.rootLabel !== null) { 77 | x.data.root.name = x.options.rootLabel; 78 | } 79 | 80 | d3.select(el).datum(x.data) 81 | .transition() 82 | .call(sunburst.advanced); 83 | 84 | 85 | // set up a container for tasks to perform after completion 86 | // one example would be add callbacks for event handling 87 | // styling 88 | if (!(typeof x.tasks === "undefined") ){ 89 | if ( (typeof x.tasks.length === "undefined") || 90 | (typeof x.tasks === "function" ) ) { 91 | // handle a function not enclosed in array 92 | // should be able to remove once using jsonlite 93 | x.tasks = [x.tasks]; 94 | } 95 | x.tasks.map(function(t){ 96 | // for each tasks call the task with el supplied as `this` 97 | t.call({el:el,x:x,instance:instance}); 98 | }); 99 | } 100 | 101 | // add x to instance for future reference 102 | instance.x = x; 103 | 104 | }, 105 | 106 | resize: function(width, height) { 107 | 108 | var x = instance.x; 109 | sunburst.chartFrame().size({height: height}); 110 | d3.select(el).call(sunburst); 111 | 112 | // set up a container for tasks to perform after completion 113 | // one example would be add callbacks for event handling 114 | // styling 115 | if (!(typeof x.tasks === "undefined") ){ 116 | if ( (typeof x.tasks.length === "undefined") || 117 | (typeof x.tasks === "function" ) ) { 118 | // handle a function not enclosed in array 119 | // should be able to remove once using jsonlite 120 | x.tasks = [x.tasks]; 121 | } 122 | x.tasks.map(function(t){ 123 | // for each tasks call the task with el supplied as `this` 124 | t.call({el:el,x:x,instance:instance}); 125 | }); 126 | } 127 | }, 128 | 129 | instance: instance 130 | 131 | }; 132 | } 133 | }); 134 | -------------------------------------------------------------------------------- /docs/articles/sunburst-2-0-0_files/sund2b-binding-2.1.6/sund2b.js: -------------------------------------------------------------------------------- 1 | HTMLWidgets.widget({ 2 | 3 | name: 'sund2b', 4 | 5 | type: 'output', 6 | 7 | factory: function(el, width, height) { 8 | 9 | var sunburst = d2b.chartSunburst(); 10 | // make this publicly available for later customization by user 11 | var instance = { 12 | chart: sunburst 13 | }; 14 | 15 | return { 16 | 17 | renderValue: function(x) { 18 | 19 | var colors = d3.scaleOrdinal(d3.schemeCategory20); 20 | 21 | if(x.options.colors !== null){ 22 | // if an array then we assume the colors 23 | // represent an array of hexadecimal colors to be used 24 | if(Array.isArray(x.options.colors)) { 25 | try{ 26 | colors.range(x.options.colors) 27 | } catch(e) { 28 | 29 | } 30 | } 31 | 32 | // if an object with range then we assume 33 | // that this is an array of colors to be used as range 34 | if(x.options.colors.range){ 35 | try{ 36 | colors.range(x.options.colors.range) 37 | } catch(e) { 38 | 39 | } 40 | } 41 | 42 | // if an object with domain then we assume 43 | // that this is an array of colors to be used as domain 44 | // for more precise control of the colors assigned 45 | if(x.options.colors.domain){ 46 | try{ 47 | colors.domain(x.options.colors.domain); 48 | } catch(e) { 49 | 50 | } 51 | } 52 | 53 | // if a function then set to the function 54 | if(typeof(x.options.colors) === "function") { 55 | colors = x.options.colors; 56 | } 57 | } 58 | 59 | sunburst.chartFrame().size({height: height}); 60 | sunburst.label(function(d) { 61 | return d.name; 62 | }); 63 | sunburst.color(function(d) { 64 | return colors.call(this, d.name, d); 65 | }); 66 | sunburst.sunburst().size(function(d) { 67 | return d[x.options.valueField || "size"]; 68 | }); 69 | if( 70 | x.options.hasOwnProperty("showLabels") && 71 | (x.options.showLabels===true || x.options.showLabels===false ) 72 | ) { 73 | sunburst.sunburst().showLabels(x.options.showLabels); 74 | } 75 | 76 | if(x.options.hasOwnProperty("rootLabel") && x.options.rootLabel !== null) { 77 | x.data.root.label = x.options.rootLabel; 78 | } 79 | 80 | d3.select(el).datum(x.data) 81 | .transition() 82 | .call(sunburst.advanced); 83 | 84 | 85 | // set up a container for tasks to perform after completion 86 | // one example would be add callbacks for event handling 87 | // styling 88 | if (!(typeof x.tasks === "undefined") ){ 89 | if ( (typeof x.tasks.length === "undefined") || 90 | (typeof x.tasks === "function" ) ) { 91 | // handle a function not enclosed in array 92 | // should be able to remove once using jsonlite 93 | x.tasks = [x.tasks]; 94 | } 95 | x.tasks.map(function(t){ 96 | // for each tasks call the task with el supplied as `this` 97 | t.call({el:el,x:x,instance:instance}); 98 | }); 99 | } 100 | 101 | // add x to instance for future reference 102 | instance.x = x; 103 | 104 | }, 105 | 106 | resize: function(width, height) { 107 | 108 | var x = instance.x; 109 | sunburst.chartFrame().size({height: height}); 110 | d3.select(el).call(sunburst); 111 | 112 | // set up a container for tasks to perform after completion 113 | // one example would be add callbacks for event handling 114 | // styling 115 | if (!(typeof x.tasks === "undefined") ){ 116 | if ( (typeof x.tasks.length === "undefined") || 117 | (typeof x.tasks === "function" ) ) { 118 | // handle a function not enclosed in array 119 | // should be able to remove once using jsonlite 120 | x.tasks = [x.tasks]; 121 | } 122 | x.tasks.map(function(t){ 123 | // for each tasks call the task with el supplied as `this` 124 | t.call({el:el,x:x,instance:instance}); 125 | }); 126 | } 127 | }, 128 | 129 | instance: instance 130 | 131 | }; 132 | } 133 | }); 134 | -------------------------------------------------------------------------------- /docs/articles/sunburst-2-0-0_files/sund2b-binding-2.1.8/sund2b.js: -------------------------------------------------------------------------------- 1 | HTMLWidgets.widget({ 2 | 3 | name: 'sund2b', 4 | 5 | type: 'output', 6 | 7 | factory: function(el, width, height) { 8 | 9 | var sunburst = d2b.chartSunburst(); 10 | // make this publicly available for later customization by user 11 | var instance = { 12 | chart: sunburst 13 | }; 14 | 15 | return { 16 | 17 | renderValue: function(x) { 18 | 19 | var colors = d3.scaleOrdinal(d3.schemeCategory20); 20 | 21 | if(x.options.colors !== null){ 22 | // if an array then we assume the colors 23 | // represent an array of hexadecimal colors to be used 24 | if(Array.isArray(x.options.colors)) { 25 | try{ 26 | colors.range(x.options.colors) 27 | } catch(e) { 28 | 29 | } 30 | } 31 | 32 | // if an object with range then we assume 33 | // that this is an array of colors to be used as range 34 | if(x.options.colors.range){ 35 | try{ 36 | colors.range(x.options.colors.range) 37 | } catch(e) { 38 | 39 | } 40 | } 41 | 42 | // if an object with domain then we assume 43 | // that this is an array of colors to be used as domain 44 | // for more precise control of the colors assigned 45 | if(x.options.colors.domain){ 46 | try{ 47 | colors.domain(x.options.colors.domain); 48 | } catch(e) { 49 | 50 | } 51 | } 52 | 53 | // if a function then set to the function 54 | if(typeof(x.options.colors) === "function") { 55 | colors = x.options.colors; 56 | } 57 | } 58 | 59 | sunburst.chartFrame().size({height: height}); 60 | sunburst.label(function(d) { 61 | return d.name; 62 | }); 63 | sunburst.color(function(d) { 64 | return colors.call(this, d.name, d); 65 | }); 66 | sunburst.sunburst().size(function(d) { 67 | return d[x.options.valueField || "size"]; 68 | }); 69 | if( 70 | x.options.hasOwnProperty("showLabels") && 71 | (x.options.showLabels===true || x.options.showLabels===false ) 72 | ) { 73 | sunburst.sunburst().showLabels(x.options.showLabels); 74 | } 75 | 76 | if(x.options.hasOwnProperty("rootLabel") && x.options.rootLabel !== null) { 77 | x.data.root.name = x.options.rootLabel; 78 | } 79 | 80 | d3.select(el).datum(x.data) 81 | .transition() 82 | .call(sunburst.advanced); 83 | 84 | 85 | // set up a container for tasks to perform after completion 86 | // one example would be add callbacks for event handling 87 | // styling 88 | if (!(typeof x.tasks === "undefined") ){ 89 | if ( (typeof x.tasks.length === "undefined") || 90 | (typeof x.tasks === "function" ) ) { 91 | // handle a function not enclosed in array 92 | // should be able to remove once using jsonlite 93 | x.tasks = [x.tasks]; 94 | } 95 | x.tasks.map(function(t){ 96 | // for each tasks call the task with el supplied as `this` 97 | t.call({el:el,x:x,instance:instance}); 98 | }); 99 | } 100 | 101 | // add x to instance for future reference 102 | instance.x = x; 103 | 104 | }, 105 | 106 | resize: function(width, height) { 107 | 108 | var x = instance.x; 109 | sunburst.chartFrame().size({height: height}); 110 | d3.select(el).call(sunburst); 111 | 112 | // set up a container for tasks to perform after completion 113 | // one example would be add callbacks for event handling 114 | // styling 115 | if (!(typeof x.tasks === "undefined") ){ 116 | if ( (typeof x.tasks.length === "undefined") || 117 | (typeof x.tasks === "function" ) ) { 118 | // handle a function not enclosed in array 119 | // should be able to remove once using jsonlite 120 | x.tasks = [x.tasks]; 121 | } 122 | x.tasks.map(function(t){ 123 | // for each tasks call the task with el supplied as `this` 124 | t.call({el:el,x:x,instance:instance}); 125 | }); 126 | } 127 | }, 128 | 129 | instance: instance 130 | 131 | }; 132 | } 133 | }); 134 | -------------------------------------------------------------------------------- /javascript/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "d3-sunburst-chart", 3 | "version": "0.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "d3-array": { 8 | "version": "1.2.0", 9 | "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.0.tgz", 10 | "integrity": "sha1-FH0mlyDhdMQFen9CvosPPyulMQg=" 11 | }, 12 | "d3-collection": { 13 | "version": "1.0.3", 14 | "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.3.tgz", 15 | "integrity": "sha1-AL3qlPvBYo1DWruuL03CFk433TQ=" 16 | }, 17 | "d3-color": { 18 | "version": "1.0.3", 19 | "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.0.3.tgz", 20 | "integrity": "sha1-vHZD/KjlOoNH4vva/6I2eWtYUJs=" 21 | }, 22 | "d3-dispatch": { 23 | "version": "1.0.3", 24 | "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz", 25 | "integrity": "sha1-RuFJHqqbWMNY/OW+TovtYm54cfg=" 26 | }, 27 | "d3-ease": { 28 | "version": "1.0.3", 29 | "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.3.tgz", 30 | "integrity": "sha1-aL+8NJM4o4DETYrMT7wzBKotjA4=" 31 | }, 32 | "d3-format": { 33 | "version": "1.2.0", 34 | "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.2.0.tgz", 35 | "integrity": "sha1-a0gLqohohdRlHcJIqPSsnaFtsHo=" 36 | }, 37 | "d3-hierarchy": { 38 | "version": "1.1.5", 39 | "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.5.tgz", 40 | "integrity": "sha1-ochFxC+Eoga88cAcAQmOpN2qeiY=" 41 | }, 42 | "d3-interpolate": { 43 | "version": "1.1.5", 44 | "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.1.5.tgz", 45 | "integrity": "sha1-aeCZ/zkhRxblY8muw+qdHqS4p58=", 46 | "requires": { 47 | "d3-color": "1.0.3" 48 | } 49 | }, 50 | "d3-path": { 51 | "version": "1.0.5", 52 | "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.5.tgz", 53 | "integrity": "sha1-JB6xhJvZ6egCHA0KeZ+KDo5EF2Q=" 54 | }, 55 | "d3-scale": { 56 | "version": "1.0.6", 57 | "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.6.tgz", 58 | "integrity": "sha1-vOGdqA06DPQiyVQ64zIghiILNO0=", 59 | "requires": { 60 | "d3-array": "1.2.0", 61 | "d3-collection": "1.0.3", 62 | "d3-color": "1.0.3", 63 | "d3-format": "1.2.0", 64 | "d3-interpolate": "1.1.5", 65 | "d3-time": "1.0.6", 66 | "d3-time-format": "2.0.5" 67 | } 68 | }, 69 | "d3-selection": { 70 | "version": "1.1.0", 71 | "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.1.0.tgz", 72 | "integrity": "sha1-GZhoSJZIj4OcoDchI9o08dMYgJw=" 73 | }, 74 | "d3-shape": { 75 | "version": "1.2.0", 76 | "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.2.0.tgz", 77 | "integrity": "sha1-RdAVOPBkuv0F6j1tLLdI/YxB93c=", 78 | "requires": { 79 | "d3-path": "1.0.5" 80 | } 81 | }, 82 | "d3-time": { 83 | "version": "1.0.6", 84 | "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.6.tgz", 85 | "integrity": "sha1-pVsT19FdOhYK6RcIIy4INfHV6UU=" 86 | }, 87 | "d3-time-format": { 88 | "version": "2.0.5", 89 | "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.0.5.tgz", 90 | "integrity": "sha1-nXeAIE98kRnJFwsaVttN6aivly4=", 91 | "requires": { 92 | "d3-time": "1.0.6" 93 | } 94 | }, 95 | "d3-timer": { 96 | "version": "1.0.6", 97 | "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.6.tgz", 98 | "integrity": "sha1-QES/FdcCXAbOfRFJ9zzQe1Tb14Q=" 99 | }, 100 | "d3-transition": { 101 | "version": "1.1.0", 102 | "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.1.0.tgz", 103 | "integrity": "sha1-z8hcdOUjkyQpBUZiNXKZBWDDlm8=", 104 | "requires": { 105 | "d3-color": "1.0.3", 106 | "d3-dispatch": "1.0.3", 107 | "d3-ease": "1.0.3", 108 | "d3-interpolate": "1.1.5", 109 | "d3-selection": "1.1.0", 110 | "d3-timer": "1.0.6" 111 | } 112 | }, 113 | "d3plus-color": { 114 | "version": "0.6.2", 115 | "resolved": "https://registry.npmjs.org/d3plus-color/-/d3plus-color-0.6.2.tgz", 116 | "integrity": "sha512-bYVr0y9ZzF7nRXO2sL2kHR1Ro9cCgz22yOE2ZwbX0u7Q3mzNBYk5a2o87FJK4rgEnaRFDexefvs1oixLuKJuSA==", 117 | "requires": { 118 | "d3-color": "1.0.3", 119 | "d3-scale": "1.0.6", 120 | "d3plus-common": "0.6.31" 121 | } 122 | }, 123 | "d3plus-common": { 124 | "version": "0.6.31", 125 | "resolved": "https://registry.npmjs.org/d3plus-common/-/d3plus-common-0.6.31.tgz", 126 | "integrity": "sha512-lZ4FpC+92CKfGr4tYJGN0+moRrPZlvrZbsBoxJumlyu7TXshJQZYHOaik1T/LiEsnoLNA6KWqniUP+NArfRsuQ==", 127 | "requires": { 128 | "d3-array": "1.2.0", 129 | "d3-collection": "1.0.3", 130 | "d3-selection": "1.1.0", 131 | "d3-transition": "1.1.0" 132 | } 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /docs/articles/index.html: -------------------------------------------------------------------------------- 1 | 2 | Articles • sunburstR 6 | 7 | 8 |
    9 |
    50 | 51 | 52 | 53 |
    54 |
    55 | 58 | 59 |
    60 |

    All vignettes

    61 |

    62 | 63 |
    Sunburst 2.0.0
    64 |
    65 |
    66 |
    67 |
    68 | 69 | 70 |
    73 | 74 |
    75 |

    Site built with pkgdown 2.0.7.

    76 |
    77 | 78 |
    79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /inst/examples/example_baseball_shiny.Rmd: -------------------------------------------------------------------------------- 1 | #devtools::install_github("cpsievert/XML2R") 2 | #devtools::install_github("cpsievert/pitchRx") 3 | #devtools::install_github("timelyportfolio/sunburstR") 4 | #install.packages("dplyr") 5 | #install.packages("tidyr") 6 | #install.packages("stringr") 7 | #install.packages("rvest") 8 | 9 | library(sunburstR) 10 | library(pitchRx) 11 | library(dplyr) 12 | 13 | # get all data from 2016-08-25 14 | dat <- scrape(start = "2016-08-25", end = "2016-08-25") 15 | 16 | ### Make the Data sunburst-able 17 | 18 | ```{r} 19 | # use runner data to get idea of action with a runner on base 20 | # please note this will not be all action from a game 21 | # but I think it is an easier dataset to understand 22 | action <- dat$runner %>% 23 | group_by(event_num) %>% 24 | filter(row_number() == 1) %>% 25 | ungroup() %>% 26 | group_by(gameday_link, inning, inning_side) %>% 27 | summarize(event = paste(c(event),collapse="-")) 28 | 29 | sequences <- action %>% 30 | ungroup() %>% 31 | group_by(event) %>% 32 | summarize(count = n()) 33 | 34 | # sorry this is messy, but get data in a form 35 | # so sunburst can build hierarchy 36 | # which means we will sort in descending order of depth 37 | # note: this will eventually improve 38 | sequences$depth <- unlist(lapply(strsplit(sequences$event,"-"),length)) 39 | ``` 40 | 41 | ### Create a Sunburst 42 | 43 | ```{r} 44 | sb <- sequences %>% 45 | arrange(desc(depth), event) %>% 46 | sunburst() 47 | sb 48 | ``` 49 | 50 | ### Use Sunburst Events 51 | 52 | In this [commit](https://github.com/timelyportfolio/sunburstR/commit/7f6879f779d0062699f758795231ecdcd9465777), we added some basic event dispatch to `sunburstR` that can be used in Shiny and non-Shiny contexts. We will use it to display a link to the games that fit the hovered paths. 53 | 54 | ```{r} 55 | # use sunburst event handling to provide games for hovered sequence 56 | library(htmltools) 57 | 58 | sb$x$tasks <- list(htmlwidgets::JS( 59 | ' 60 | function(){ 61 | debugger; 62 | var chart = this.instance.chart; 63 | chart.on("mouseover",mouseovered); 64 | } 65 | ' 66 | )) 67 | 68 | sb$height = 400 69 | sb$width = 600 70 | 71 | tagList( 72 | sb, 73 | tags$div(id="games", style="margin-top:100px"), 74 | tags$script(HTML( 75 | sprintf( 76 | ' 77 | var action = %s; 78 | 79 | function mouseovered(d){ 80 | var thiz = this; 81 | var games = action.filter(function(evt){ 82 | return evt.event === thiz.join("-"); 83 | }); 84 | 85 | var div = document.getElementById("games"); 86 | div.innerHTML = games.map(function(game){ 87 | return [ 88 | "", 91 | game.gameday_link, 92 | "
    " 93 | ].join(""); 94 | }).join("\\n"); 95 | } 96 | ', 97 | jsonlite::toJSON(action, auto_unbox=TRUE, dataframe="row") 98 | ) 99 | )) 100 | ) 101 | ``` 102 | 103 | 104 | ## Sequence of Pitches 105 | 106 | [Dan Malter](http://danmalter.github.io/) wrote a fantastic post [Using Markov Chains to Predict Pitches](http://danmalter.github.io/r/2016/03/28/Markov-chains.html). Let's visualize his pitch data for Jake Arrieta. 107 | 108 | ### Scrape the Data 109 | 110 | ```{r} 111 | # pitch sequence data from Markov Chain 112 | # http://danmalter.github.io/r/2016/03/28/Markov-chains.html 113 | library(sunburstR) 114 | library(rvest) 115 | library(stringr) 116 | library(tidyr) 117 | library(dplyr) 118 | 119 | # get table from post to avoid having to run all the code 120 | ht <- read_html("http://danmalter.github.io/r/2016/03/28/Markov-chains.html") 121 | # get pitch type as proportion of total pitches 122 | ht_pitch_arrieta <- html_table( 123 | xml_find_first(ht,'//*[@id="jake-arrieta---overall-pitch-proportions"]/table') 124 | ) 125 | # get markov table for pitch and following pitch 126 | ht_tbl_arrieta <- html_text( 127 | xml_find_all(ht,'//*[@id="jake-arrieta---multi-class-markov-chain"]/pre/code')[2] 128 | ) 129 | ht_tbl_arrieta <- str_replace_all( 130 | ht_tbl_arrieta, 131 | c("4-seam FB"="4seamFB", "Int. Ball"="IntBall") 132 | ) 133 | tbl <- read.table( 134 | textConnection(ht_tbl_arrieta), 135 | skip=2, 136 | header=FALSE, 137 | stringsAsFactors=FALSE 138 | ) 139 | colnames(tbl) <- c("pitch",tbl[,1]) 140 | 141 | # multiple by pct of total 142 | tbl[,-1] <- tbl[,-1] * t(ht_pitch_arrieta) 143 | 144 | tbl_long <- gather(tbl, key=pitch2, value=value, -pitch) 145 | ``` 146 | 147 | 148 | ### Draw the Sunburst 149 | 150 | ```{r} 151 | tbl_long %>% 152 | mutate(path = paste(pitch,pitch2,sep="-")) %>% 153 | select(path, value) %>% 154 | sunburst() 155 | ``` 156 | 157 | ## Thanks 158 | 159 | Thanks so much to [Kerry Rodden](https://twitter.com/kerryrodden) who provided the original [sunburst](http://bl.ocks.org/kerryrodden/7090426) on which this is based. 160 | 161 | Thanks [Mike Bostock](https://bost.ocks.org/mike/) for [d3.js](https://d3js.org) (*please give us a way to pay you*). 162 | 163 | Thanks [Tim Holman](http://tholman.com/) for the great [github-corners](http://tholman.com/github-corners/). 164 | 165 | Thanks [skeleton](http://getskeleton.com/). 166 | 167 | Thanks Carson Sievert for `pitchRx` and all your work on [`plotly`](https://github.com/ropensci/plotly). 168 | 169 | Thanks Dan Malter for the great markov pitch post. 170 | 171 | 172 | -------------------------------------------------------------------------------- /docs/LICENSE-text.html: -------------------------------------------------------------------------------- 1 | 2 | License • sunburstR 6 | 7 | 8 |
    9 |
    50 | 51 | 52 | 53 |
    54 |
    55 | 58 | 59 |
    YEAR: 2017
    60 | COPYRIGHT HOLDER: Kent Russell
    61 | 
    62 | 63 |
    64 | 65 | 68 | 69 |
    70 | 71 | 72 | 73 |
    76 | 77 |
    78 |

    Site built with pkgdown 2.0.7.

    79 |
    80 | 81 |
    82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /docs/bootstrap-toc.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Table of Contents v0.4.1 (http://afeld.github.io/bootstrap-toc/) 3 | * Copyright 2015 Aidan Feldman 4 | * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */ 5 | (function() { 6 | 'use strict'; 7 | 8 | window.Toc = { 9 | helpers: { 10 | // return all matching elements in the set, or their descendants 11 | findOrFilter: function($el, selector) { 12 | // http://danielnouri.org/notes/2011/03/14/a-jquery-find-that-also-finds-the-root-element/ 13 | // http://stackoverflow.com/a/12731439/358804 14 | var $descendants = $el.find(selector); 15 | return $el.filter(selector).add($descendants).filter(':not([data-toc-skip])'); 16 | }, 17 | 18 | generateUniqueIdBase: function(el) { 19 | var text = $(el).text(); 20 | var anchor = text.trim().toLowerCase().replace(/[^A-Za-z0-9]+/g, '-'); 21 | return anchor || el.tagName.toLowerCase(); 22 | }, 23 | 24 | generateUniqueId: function(el) { 25 | var anchorBase = this.generateUniqueIdBase(el); 26 | for (var i = 0; ; i++) { 27 | var anchor = anchorBase; 28 | if (i > 0) { 29 | // add suffix 30 | anchor += '-' + i; 31 | } 32 | // check if ID already exists 33 | if (!document.getElementById(anchor)) { 34 | return anchor; 35 | } 36 | } 37 | }, 38 | 39 | generateAnchor: function(el) { 40 | if (el.id) { 41 | return el.id; 42 | } else { 43 | var anchor = this.generateUniqueId(el); 44 | el.id = anchor; 45 | return anchor; 46 | } 47 | }, 48 | 49 | createNavList: function() { 50 | return $(''); 51 | }, 52 | 53 | createChildNavList: function($parent) { 54 | var $childList = this.createNavList(); 55 | $parent.append($childList); 56 | return $childList; 57 | }, 58 | 59 | generateNavEl: function(anchor, text) { 60 | var $a = $(''); 61 | $a.attr('href', '#' + anchor); 62 | $a.text(text); 63 | var $li = $('
  • '); 64 | $li.append($a); 65 | return $li; 66 | }, 67 | 68 | generateNavItem: function(headingEl) { 69 | var anchor = this.generateAnchor(headingEl); 70 | var $heading = $(headingEl); 71 | var text = $heading.data('toc-text') || $heading.text(); 72 | return this.generateNavEl(anchor, text); 73 | }, 74 | 75 | // Find the first heading level (`

    `, then `

    `, etc.) that has more than one element. Defaults to 1 (for `

    `). 76 | getTopLevel: function($scope) { 77 | for (var i = 1; i <= 6; i++) { 78 | var $headings = this.findOrFilter($scope, 'h' + i); 79 | if ($headings.length > 1) { 80 | return i; 81 | } 82 | } 83 | 84 | return 1; 85 | }, 86 | 87 | // returns the elements for the top level, and the next below it 88 | getHeadings: function($scope, topLevel) { 89 | var topSelector = 'h' + topLevel; 90 | 91 | var secondaryLevel = topLevel + 1; 92 | var secondarySelector = 'h' + secondaryLevel; 93 | 94 | return this.findOrFilter($scope, topSelector + ',' + secondarySelector); 95 | }, 96 | 97 | getNavLevel: function(el) { 98 | return parseInt(el.tagName.charAt(1), 10); 99 | }, 100 | 101 | populateNav: function($topContext, topLevel, $headings) { 102 | var $context = $topContext; 103 | var $prevNav; 104 | 105 | var helpers = this; 106 | $headings.each(function(i, el) { 107 | var $newNav = helpers.generateNavItem(el); 108 | var navLevel = helpers.getNavLevel(el); 109 | 110 | // determine the proper $context 111 | if (navLevel === topLevel) { 112 | // use top level 113 | $context = $topContext; 114 | } else if ($prevNav && $context === $topContext) { 115 | // create a new level of the tree and switch to it 116 | $context = helpers.createChildNavList($prevNav); 117 | } // else use the current $context 118 | 119 | $context.append($newNav); 120 | 121 | $prevNav = $newNav; 122 | }); 123 | }, 124 | 125 | parseOps: function(arg) { 126 | var opts; 127 | if (arg.jquery) { 128 | opts = { 129 | $nav: arg 130 | }; 131 | } else { 132 | opts = arg; 133 | } 134 | opts.$scope = opts.$scope || $(document.body); 135 | return opts; 136 | } 137 | }, 138 | 139 | // accepts a jQuery object, or an options object 140 | init: function(opts) { 141 | opts = this.helpers.parseOps(opts); 142 | 143 | // ensure that the data attribute is in place for styling 144 | opts.$nav.attr('data-toggle', 'toc'); 145 | 146 | var $topContext = this.helpers.createChildNavList(opts.$nav); 147 | var topLevel = this.helpers.getTopLevel(opts.$scope); 148 | var $headings = this.helpers.getHeadings(opts.$scope, topLevel); 149 | this.helpers.populateNav($topContext, topLevel, $headings); 150 | } 151 | }; 152 | 153 | $(function() { 154 | $('nav[data-toggle="toc"]').each(function(i, el) { 155 | var $nav = $(el); 156 | Toc.init($nav); 157 | }); 158 | }); 159 | })(); 160 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # sunburstR 2.1.8 2 | 3 | * maintenance and cleanup - replace old invalid urls 4 | * remove containing `tagList` from `sunburst_html` (see [issue 115](https://github.com/timelyportfolio/sunburstR/issues/115)) 5 | 6 | # sunburstR 2.1.7 7 | 8 | * allow specification of root label in internal `csv_to_hier` converter function 9 | * fix bug where `rootLabel` argument for `sund2b` was not working ; (see [issue 114](https://github.com/timelyportfolio/sunburstR/issues/114)) 10 | 11 | # sunburstR 2.1.6 12 | 13 | * add labels for slices based on https://bl.ocks.org/vasturiano/12da9071095fbd4df434e60d52d2d58d 14 | 15 | # sunburstR 2.1.5 16 | 17 | * fix bug in internal `csv_to_hier` converter; (see [issue 107](https://github.com/timelyportfolio/sunburstR/issues/107)) 18 | 19 | # sunburstR 2.1.4 20 | 21 | * add showLabels argument to d2b; (see [issue 71](https://github.com/timelyportfolio/sunburstR/issues/71)) 22 | 23 | # sunburstR 2.1.3 24 | 25 | * add breadcrumb customization to d2b; (see [issue 92](https://github.com/timelyportfolio/sunburstR/issues/92)) 26 | * add argument to allow user to change root label (see [issue 96](https://github.com/timelyportfolio/sunburstR/issues/96)) 27 | 28 | # sunburstR 2.1.2 29 | 30 | * add tooltip customization to d2b; (see [issue 92](https://github.com/timelyportfolio/sunburstR/issues/92)) 31 | 32 | # sunburstR 2.1.1 33 | 34 | * add Shiny to d2b; (see [issue 64](https://github.com/timelyportfolio/sunburstR/issues/64)) 35 | 36 | # sunburstR 2.1.0 37 | 38 | * update d2b so that pre-summed trees correctly handled; (see [issue 78](https://github.com/timelyportfolio/sunburstR/issues/78) and [issue 62](https://github.com/timelyportfolio/sunburstR/issues/62)) 39 | 40 | # sunburstR 2.0.0 41 | 42 | * 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)) 43 | 44 | * add `sund2b()` htmlwidget for a [d2b](https://github.com/d2bjs/d2b) sunburst chart 45 | 46 | * allow turning off the legend with argument `legend = FALSE` (see [issue](https://github.com/timelyportfolio/sunburstR/issues/61)) 47 | 48 | * fix bug to not duplicate svg for breadcrumbs on resize or re-render 49 | 50 | # sunburstR 1.0.3 51 | 52 | * change text color in legend and breadcrumb to white or black for legibility using d3plus 53 | 54 | # sunburstR 1.0.2 55 | 56 | * changes required in downstream d3r to work with new tidyr (0.7.0), so now working and tested 57 | 58 | # sunburstR 1.0.1 59 | 60 | * improve internal `csv_to_hier()` with delimiter argument and smart convert for root level 61 | 62 | # sunburstR 1.0.0 63 | 64 | see [sunburstR v1 Github Project](https://github.com/timelyportfolio/sunburstR/projects/1) 65 | 66 | ### Updates 67 | 68 | * update to `d3v4`; thanks @cjyetman (see [issue 36](https://github.com/timelyportfolio/sunburstR/issues/36)) 69 | * convert JS to standalone build to prevent conflicts with other non-d3v4 htmlwidgets (see [issue 40](https://github.com/timelyportfolio/sunburstR/issues/40)) 70 | * begin to modularize JavaScript (see [issue 37](https://github.com/timelyportfolio/sunburstR/issues/37)) 71 | 72 | 73 | ### API Changes 74 | 75 | * **BREAKING** `csvdata` and `jsondata` deprecated in favor of single data argument (see [issue 43](https://github.com/timelyportfolio/sunburstR/issues/43)) 76 | * add `dplyr` to IMPORTS 77 | * convert csv hierarchy on the R side instead of JS side; slightly slower but will work to improve 78 | 79 | # sunburstR 0.6.5 80 | 81 | ### Bug Fix 82 | 83 | * fix legend bug introduced with new color functionality; see [issue](https://github.com/timelyportfolio/sunburstR/issues/34) and [commit](https://github.com/timelyportfolio/sunburstR/commit/635ec7cd755d8d3ae417a402be65833725551cdf) 84 | 85 | # sunburstR 0.6.4 86 | 87 | see [treemap example](https://github.com/timelyportfolio/sunburstR/blob/master/inst/examples/example_treemap.R) 88 | 89 | * add `valueField` argument to `sunburst()` to allow fields in json 90 | to be something other than `"size"` [commit](https://github.com/timelyportfolio/sunburstR/commit/52bfc78cbfb1a8083584370aace863b674b53e32) 91 | * add ability to supply a JavaScript `function` to `colors` for 92 | advanced color customization or to use a color field from data 93 | as the color fill [commit](https://github.com/timelyportfolio/sunburstR/commit/4499a7c2bd5e57b729fbe2c562f1ef9932143f10) 94 | 95 | # sunburstR 0.6.3 96 | 97 | ### Updates 98 | 99 | * use the new CRAN package `d3r` for `d3.js` dependencies 100 | 101 | # sunburstR 0.6.2 102 | 103 | ### Updates 104 | 105 | * update d3.js to most recent tagged v3 release [commit](https://github.com/timelyportfolio/sunburstR/commit/5af0b29f08fad5e9ea8e28fb2c4e6eb9f09d1887) 106 | 107 | # sunburstR 0.6.1 108 | 109 | ### New Features 110 | 111 | * add click to dispatch for click event handling [commit](https://github.com/timelyportfolio/sunburstR/commit/d3239b42f7dc29dcbe9456523bccd601e25f0a20) 112 | * add click to `add_shiny` and provide examples [commit](https://github.com/timelyportfolio/sunburstR/commit/d3239b42f7dc29dcbe9456523bccd601e25f0a20) and [commit](https://github.com/timelyportfolio/sunburstR/commit/4f72b6e9c12d23ff24f40828747ccbdce9ce075b) 113 | 114 | ### Bug Fix 115 | 116 | * try to fix breadcrumb sizing bug for Firefox, Safari [commit](https://github.com/timelyportfolio/sunburstR/pull/24/commits/d3239b42f7dc29dcbe9456523bccd601e25f0a20) 117 | 118 | 119 | # sunburstR 0.6 120 | 121 | * CRAN release 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Page not found (404) • sunburstR 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 |
    24 |
    73 | 74 | 75 | 76 | 77 |
    78 |
    79 | 82 | 83 | Content not found. Please use links in the navbar. 84 | 85 |
    86 | 87 | 91 | 92 |
    93 | 94 | 95 | 96 |
    100 | 101 |
    102 |

    103 |

    Site built with pkgdown 2.0.7.

    104 |
    105 | 106 |
    107 |
    108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /docs/CONDUCT.html: -------------------------------------------------------------------------------- 1 | 2 | Contributor Code of Conduct • sunburstR 6 | 7 | 8 |
    9 |
    50 | 51 | 52 | 53 |
    54 |
    55 | 58 | 59 |
    60 | 61 |

    As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.

    62 |

    We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.

    63 |

    Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.

    64 |

    Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.

    65 |

    Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.

    66 |

    This Code of Conduct is adapted from the Contributor Covenant (http:contributor-covenant.org), version 1.0.0, available at http://contributor-covenant.org/version/1/0/0/

    67 |
    68 | 69 |
    70 | 71 | 74 | 75 |
    76 | 77 | 78 | 79 |
    82 | 83 |
    84 |

    Site built with pkgdown 2.0.7.

    85 |
    86 | 87 |
    88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /docs/reference/index.html: -------------------------------------------------------------------------------- 1 | 2 | Function reference • sunburstR 6 | 7 | 8 |
    9 |
    50 | 51 | 52 | 53 |
    54 |
    55 | 58 | 59 | 63 | 66 | 67 | 70 | 71 | 74 | 75 | 78 | 79 | 82 | 83 | 86 | 87 | 90 | 91 |
    60 |

    All functions

    61 |

    62 |
    64 |

    add_shiny()

    65 |

    Add Shiny Events

    68 |

    sund2bOutput() renderSund2b()

    69 |

    Shiny bindings for d2b

    72 |

    sunburstOutput() renderSunburst()

    73 |

    Shiny bindings for sunburst

    76 |

    sunburst()

    77 |

    `d3.js` Sequence Sunburst Diagrams

    80 |

    sund2b()

    81 |

    Sunburst Using 'd2b'

    84 |

    sund2bBreadcrumb()

    85 |

    Advanced Customization of 'd2b' Breadcrumb

    88 |

    sund2bTooltip()

    89 |

    Advanced Customization of 'd2b' Tooltip

    92 | 93 | 96 |
    97 | 98 | 99 |
    102 | 103 |
    104 |

    Site built with pkgdown 2.0.7.

    105 |
    106 | 107 |
    108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /R/d2b.R: -------------------------------------------------------------------------------- 1 | #' Sunburst Using 'd2b' 2 | #' 3 | #' Create interactive sunburst chart with the 'd2b' charting library. 4 | #' 5 | #' @param data data in csv source,target form or in 6 | #' nested d3 JSON hierarchy with `{name:..., children:[];}`. \code{list}, \code{character}, 7 | #' or \code{connection} data will be assumed to be \code{JSON}. 8 | #' \code{data.frame} data will be assumed to be \code{csvdata} and converted 9 | #' to \code{JSON} by \code{sunburstR:::csv_to_hier()}. 10 | #' @param colors \code{vector} of strings representing colors as hexadecimal for 11 | #' manual colors. If you want precise control of colors, supply a \code{list} 12 | #' with \code{range} and/or \code{domain}. For advanced customization, supply 13 | #' a JavaScript \code{function}. 14 | #' @param valueField \code{character} for the field to use to calculate size. The default 15 | #' value is \code{"size"}. 16 | #' @param tooltip \code{list} of options for customizing the tooltip. See the helper 17 | #' function \code{\link{sund2bTooltip}} for more information. 18 | #' @param breadcrumbs \code{list} of options for customizing the breadcrumb. See the helper 19 | #' function \code{\link{sund2bBreadcrumb}} for more information. 20 | #' @param rootLabel \code{character} to label root node something other than 'root'. 21 | #' @param showLabels \code{logical} to show labels on the slices. The default is \code{FALSE}. 22 | #' @param height,width height and width of sunburst htmlwidget containing div 23 | #' specified in any valid \code{CSS} size unit. 24 | #' @param elementId string id as a valid \code{CSS} element id. 25 | #' 26 | #' @example inst/examples/example_sund2b.R 27 | #' 28 | #' @import htmlwidgets 29 | #' 30 | #' @export 31 | sund2b <- function( 32 | data = NULL, 33 | colors = NULL, 34 | valueField = "size", 35 | tooltip = NULL, 36 | breadcrumbs = NULL, 37 | rootLabel = NULL, 38 | showLabels = FALSE, 39 | width = NULL, height = NULL, elementId = NULL 40 | ) { 41 | 42 | 43 | if(is.null(data)) stop("please provide data",call.=FALSE) 44 | 45 | # accept JSON string as data 46 | if( inherits(data,c("character","connection")) ){ 47 | data = jsonlite::toJSON( 48 | jsonlite::fromJSON( data ) 49 | , auto_unbox = TRUE 50 | , dataframe = "rows" 51 | ) 52 | } 53 | # accept list as data 54 | if( inherits(data, "list") ) { 55 | data = jsonlite::toJSON( 56 | data 57 | , auto_unbox = TRUE 58 | , dataframe = "rows" 59 | ) 60 | } 61 | # accept data.frame as data 62 | # and convert to JSON with csv_to_hier 63 | # this conversion should be more robust than 64 | # the JavaScript converter 65 | if( inherits(data, "data.frame") ) { 66 | data = csv_to_hier(data) 67 | } 68 | 69 | # forward options using x 70 | x = list( 71 | data = list( 72 | root = data, 73 | tooltip = Filter(Negate(is.null), tooltip), 74 | breadcrumbs = Filter(Negate(is.null), breadcrumbs) 75 | ), 76 | options = list( 77 | colors = colors, 78 | valueField = valueField, 79 | rootLabel = rootLabel, 80 | showLabels = showLabels 81 | ) 82 | ) 83 | 84 | # create widget 85 | htmlwidgets::createWidget( 86 | name = 'sund2b', 87 | x, 88 | width = width, 89 | height = height, 90 | package = 'sunburstR', 91 | elementId = elementId, 92 | dependencies = list( 93 | d3r::d3_dep_v4(), 94 | d2b_dep() 95 | ) 96 | ) 97 | } 98 | 99 | #' Advanced Customization of 'd2b' Tooltip 100 | #' 101 | #' @param at \code{character} which should be one of 102 | #' "top left", "top center", "top right", "center left", "center center", 103 | #' "center right", "bottom center", "bottom right" to specify 104 | #' where the tooltip will be positioned relative to the hovered item. 105 | #' @param followMouse \code{logical} controlling whether the tooltip 106 | #' will follow the mouse instead of being placed in a static 107 | #' position relative to the hovered element 108 | #' @param html \code{character} or \code{htmlwidgets::JS} to customize the content 109 | #' of the tooltip. To provide a function, the arguments for the 'JavaScript' 110 | #' function will be 'function(nodedata, size, percent)' and the function 111 | #' should return a string. 112 | #' @param my \code{character} which should be one of "top", "left", "right", "bottom" 113 | #' to control the orientation of the tooltip. 114 | #' 115 | #' @return \code{list} 116 | #' @export 117 | #' 118 | #' @example inst/examples/example_sund2bTooltip.R 119 | sund2bTooltip <- function( 120 | at = NULL, 121 | followMouse = NULL, 122 | html = NULL, 123 | my = NULL 124 | ) { 125 | list( 126 | at = at, 127 | followMouse = followMouse, 128 | html = html, 129 | my = my 130 | ) 131 | } 132 | 133 | #' Advanced Customization of 'd2b' Breadcrumb 134 | #' 135 | #' @param enabled \code{boolean} to enable or disable the breadcrumbs. 136 | #' @param html \code{character} or \code{htmlwidgets::JS} to customize the content 137 | #' of the breadcrumb. To provide a function, the arguments for the 'JavaScript' 138 | #' function will be 'function(nodedata, size, percent)' and the function 139 | #' should return a string. 140 | #' @param orient \code{character} which should be one of "top", "left", "right", "bottom" 141 | #' to control the orientation of the breadcrumb relative to the chart. 142 | #' 143 | #' @return \code{list} 144 | #' @export 145 | #' 146 | #' @example inst/examples/example_sund2bBreadcrumb.R 147 | sund2bBreadcrumb <- function( 148 | enabled = NULL, 149 | html = NULL, 150 | orient = NULL 151 | ) { 152 | list( 153 | enabled = enabled, 154 | html = html, 155 | orient = orient 156 | ) 157 | } 158 | 159 | #' Shiny bindings for d2b 160 | #' 161 | #' Output and render functions for using d2b within Shiny 162 | #' applications and interactive Rmd documents. 163 | #' 164 | #' @param outputId output variable to read from 165 | #' @param width,height Must be a valid CSS unit (like \code{'100\%'}, 166 | #' \code{'400px'}, \code{'auto'}) or a number, which will be coerced to a 167 | #' string and have \code{'px'} appended. 168 | #' @param expr An expression that generates a d2b 169 | #' @param env The environment in which to evaluate \code{expr}. 170 | #' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This 171 | #' is useful if you want to save an expression in a variable. 172 | #' 173 | #' @name d2b-shiny 174 | #' 175 | #' @export 176 | sund2bOutput <- function(outputId, width = '100%', height = '400px'){ 177 | htmlwidgets::shinyWidgetOutput(outputId, 'sund2b', width, height, package = 'sunburstR') 178 | } 179 | 180 | #' @rdname d2b-shiny 181 | #' @export 182 | renderSund2b <- function(expr, env = parent.frame(), quoted = FALSE) { 183 | if (!quoted) { expr <- substitute(expr) } # force quoted 184 | htmlwidgets::shinyRenderWidget(expr, sund2bOutput, env, quoted = TRUE) 185 | } 186 | 187 | #' @keywords internal 188 | d2b_dep <- function() { 189 | htmltools::htmlDependency( 190 | name = "d2b", 191 | version = "1.0.9", 192 | src = c( 193 | file = system.file("htmlwidgets/lib/d2b", package="sunburstR") 194 | ), 195 | script = "d2b.min.js", 196 | stylesheet = "d2b_custom.css" 197 | ) 198 | } 199 | -------------------------------------------------------------------------------- /docs/authors.html: -------------------------------------------------------------------------------- 1 | 2 | Authors and Citation • sunburstR 6 | 7 | 8 |
    9 |
    50 | 51 | 52 | 53 |
    54 |
    55 |
    56 | 59 | 60 | 61 |
    • 62 |

      Mike Bostock. Author, copyright holder. 63 |
      d3.js library, http://d3js.org

      64 |
    • 65 |
    • 66 |

      Kerry Rodden. Author, copyright holder. 67 |
      sequences library in htmlwidgets/lib, https://gist.github.com/kerryrodden/7090426

      68 |
    • 69 |
    • 70 |

      Kevin Warne. Author, copyright holder. 71 |
      d2b sunburst library in htmlwidgets/lib, https://github.com/d2bjs/d2b

      72 |
    • 73 |
    • 74 |

      Kent Russell. Author, maintainer. 75 |
      R interface

      76 |
    • 77 |
    • 78 |

      Florian Breitwieser. Contributor. 79 |
      R interface

      80 |
    • 81 |
    • 82 |

      CJ Yetman. Contributor. 83 |
      R interface

      84 |
    • 85 |
    86 |
    87 |
    88 |

    Citation

    89 | Source: DESCRIPTION 90 |
    91 |
    92 | 93 | 94 |

    Bostock M, Rodden K, Warne K, Russell K (2023). 95 | sunburstR: Sunburst 'Htmlwidget'. 96 | R package version 2.1.8, https://github.com/timelyportfolio/sunburstR. 97 |

    98 |
    @Manual{,
     99 |   title = {sunburstR: Sunburst 'Htmlwidget'},
    100 |   author = {Mike Bostock and Kerry Rodden and Kevin Warne and Kent Russell},
    101 |   year = {2023},
    102 |   note = {R package version 2.1.8},
    103 |   url = {https://github.com/timelyportfolio/sunburstR},
    104 | }
    105 | 106 |
    107 | 108 |
    109 | 110 | 111 | 112 |
    115 | 116 |
    117 |

    Site built with pkgdown 2.0.7.

    118 |
    119 | 120 |
    121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /docs/reference/d2b-shiny.html: -------------------------------------------------------------------------------- 1 | 2 | Shiny bindings for d2b — d2b-shiny • sunburstR 7 | 8 | 9 |
    10 |
    51 | 52 | 53 | 54 |
    55 |
    56 | 61 | 62 |
    63 |

    Output and render functions for using d2b within Shiny 64 | applications and interactive Rmd documents.

    65 |
    66 | 67 |
    68 |
    sund2bOutput(outputId, width = "100%", height = "400px")
     69 | 
     70 | renderSund2b(expr, env = parent.frame(), quoted = FALSE)
    71 |
    72 | 73 |
    74 |

    Arguments

    75 |
    outputId
    76 |

    output variable to read from

    77 | 78 | 79 |
    width, height
    80 |

    Must be a valid CSS unit (like '100%', 81 | '400px', 'auto') or a number, which will be coerced to a 82 | string and have 'px' appended.

    83 | 84 | 85 |
    expr
    86 |

    An expression that generates a d2b

    87 | 88 | 89 |
    env
    90 |

    The environment in which to evaluate expr.

    91 | 92 | 93 |
    quoted
    94 |

    Is expr a quoted expression (with quote())? This 95 | is useful if you want to save an expression in a variable.

    96 | 97 |
    98 | 99 |
    100 | 103 |
    104 | 105 | 106 |
    109 | 110 |
    111 |

    Site built with pkgdown 2.0.7.

    112 |
    113 | 114 |
    115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /docs/reference/sunburst-shiny.html: -------------------------------------------------------------------------------- 1 | 2 | Shiny bindings for sunburst — sunburst-shiny • sunburstR 7 | 8 | 9 |
    10 |
    51 | 52 | 53 | 54 |
    55 |
    56 | 61 | 62 |
    63 |

    Output and render functions for using sunburst within Shiny 64 | applications and interactive Rmd documents.

    65 |
    66 | 67 |
    68 |
    sunburstOutput(outputId, width = "100%", height = "400px")
     69 | 
     70 | renderSunburst(expr, env = parent.frame(), quoted = FALSE)
    71 |
    72 | 73 |
    74 |

    Arguments

    75 |
    outputId
    76 |

    output variable to read from

    77 | 78 | 79 |
    width, height
    80 |

    Must be a valid CSS unit (like '100%', 81 | '400px', 'auto') or a number, which will be coerced to a 82 | string and have 'px' appended.

    83 | 84 | 85 |
    expr
    86 |

    An expression that generates a sunburst

    87 | 88 | 89 |
    env
    90 |

    The environment in which to evaluate expr.

    91 | 92 | 93 |
    quoted
    94 |

    Is expr a quoted expression (with quote())? This 95 | is useful if you want to save an expression in a variable.

    96 | 97 |
    98 | 99 |
    100 | 103 |
    104 | 105 | 106 |
    109 | 110 |
    111 |

    Site built with pkgdown 2.0.7.

    112 |
    113 | 114 |
    115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /inst/examples/example_baseball.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Visualize Baseball with sunburstR" 3 | author: "Kenton Russell" 4 | date: "August 26, 2016" 5 | output: 6 | html_document: 7 | mathjax: null 8 | theme: null 9 | css: https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css 10 | --- 11 | 12 | ```{r setup, include=FALSE} 13 | knitr::opts_chunk$set(echo = TRUE, warning=FALSE, message=FALSE) 14 | ``` 15 | 16 | 17 | 18 | 19 | 20 |
    21 | 22 | Sunburst charts from [`sunburstR`](https://github.com/timelyportfolio/sunburstR) work well in sports. Let's look at a couple of simple examples of interactive sunburst visualizations with baseball data. We'll be using [`pitchRx`](https://github.com/cpsievert/pitchRx) from [Carson Sievert \@cpsievert](https://twitter.com/cpsievert) to scrape baseball game data. To replicate these examples, please install any missing packages you see in the following code. 23 | 24 | ```{r eval=FALSE} 25 | devtools::install_github("cpsievert/XML2R") 26 | devtools::install_github("cpsievert/pitchRx") 27 | devtools::install_github("timelyportfolio/sunburstR") 28 | install.packages("dplyr") 29 | install.packages("tidyr") 30 | install.packages("stringr") 31 | install.packages("rvest") 32 | ```` 33 | 34 | ## Sequence of Action 35 | 36 | `pitchRx` provides lots of sunburst-able sequence. I think the easiest sequence will be the data in `runner`. This allows us to see all the action that moves a runner to base or on base. Although this data is "easy", you might wonder later why we don't add to 3 outs, so remember this is a partial look at all action. 37 | 38 | ### Scrape with pitchRx 39 | 40 | ```{r} 41 | library(sunburstR) 42 | library(pitchRx) 43 | library(dplyr) 44 | 45 | # get all data from 2016-08-25 46 | dat <- scrape(start = "2016-08-25", end = "2016-08-25") 47 | ``` 48 | 49 | ### Make the Data sunburst-able 50 | 51 | ```{r} 52 | # use runner data to get idea of action with a runner on base 53 | # please note this will not be all action from a game 54 | # but I think it is an easier dataset to understand 55 | action <- dat$runner %>% 56 | group_by(event_num) %>% 57 | filter(row_number() == 1) %>% 58 | ungroup() %>% 59 | group_by(gameday_link, inning, inning_side) %>% 60 | summarize(event = paste(c(event),collapse="-")) 61 | 62 | sequences <- action %>% 63 | ungroup() %>% 64 | group_by(event) %>% 65 | summarize(count = n()) 66 | 67 | # sorry this is messy, but get data in a form 68 | # so sunburst can build hierarchy 69 | # which means we will sort in descending order of depth 70 | # note: this will eventually improve 71 | sequences$depth <- unlist(lapply(strsplit(sequences$event,"-"),length)) 72 | ``` 73 | 74 | ### Create a Sunburst 75 | 76 | ```{r} 77 | sb <- sequences %>% 78 | arrange(desc(depth), event) %>% 79 | sunburst() 80 | sb 81 | ``` 82 | 83 | ### Use Sunburst Events 84 | 85 | In this [commit](https://github.com/timelyportfolio/sunburstR/commit/7f6879f779d0062699f758795231ecdcd9465777), we added some basic event dispatch to `sunburstR` that can be used in Shiny and non-Shiny contexts. We will use it to display a link to the games that fit the hovered paths. 86 | 87 | ```{r} 88 | # use sunburst event handling to provide games for hovered sequence 89 | library(htmltools) 90 | 91 | sb$x$tasks <- list(htmlwidgets::JS( 92 | ' 93 | function(){ 94 | var chart = this.instance.chart; 95 | chart.on("mouseover",mouseovered); 96 | } 97 | ' 98 | )) 99 | 100 | sb$height = 400 101 | sb$width = 600 102 | 103 | tagList( 104 | sb, 105 | tags$div(id="games", style="margin-top:100px"), 106 | tags$script(HTML( 107 | sprintf( 108 | ' 109 | var action = %s; 110 | 111 | function mouseovered(d){ 112 | var thiz = this; 113 | var games = action.filter(function(evt){ 114 | return evt.event === thiz.join("-"); 115 | }); 116 | 117 | var div = document.getElementById("games"); 118 | div.innerHTML = games.map(function(game){ 119 | return [ 120 | "", 123 | game.gameday_link, 124 | "
    " 125 | ].join(""); 126 | }).join("\\n"); 127 | } 128 | ', 129 | jsonlite::toJSON(action, auto_unbox=TRUE, dataframe="row") 130 | ) 131 | )) 132 | ) 133 | ``` 134 | 135 | 136 | ## Sequence of Pitches 137 | 138 | [Dan Malter](http://danmalter.github.io/) wrote a fantastic post [Using Markov Chains to Predict Pitches](http://danmalter.github.io/r/2016/03/28/Markov-chains.html). Let's visualize his pitch data for Jake Arrieta. 139 | 140 | ### Scrape the Data 141 | 142 | ```{r} 143 | # pitch sequence data from Markov Chain 144 | # http://danmalter.github.io/r/2016/03/28/Markov-chains.html 145 | library(sunburstR) 146 | library(rvest) 147 | library(stringr) 148 | library(tidyr) 149 | library(dplyr) 150 | 151 | # get table from post to avoid having to run all the code 152 | ht <- read_html("http://danmalter.github.io/r/2016/03/28/Markov-chains.html") 153 | # get pitch type as proportion of total pitches 154 | ht_pitch_arrieta <- html_table( 155 | xml_find_first(ht,'//*[@id="jake-arrieta---overall-pitch-proportions"]/table') 156 | ) 157 | # get markov table for pitch and following pitch 158 | ht_tbl_arrieta <- html_text( 159 | xml_find_all(ht,'//*[@id="jake-arrieta---multi-class-markov-chain"]/pre/code')[2] 160 | ) 161 | ht_tbl_arrieta <- str_replace_all( 162 | ht_tbl_arrieta, 163 | c("4-seam FB"="4seamFB", "Int. Ball"="IntBall") 164 | ) 165 | tbl <- read.table( 166 | textConnection(ht_tbl_arrieta), 167 | skip=2, 168 | header=FALSE, 169 | stringsAsFactors=FALSE 170 | ) 171 | colnames(tbl) <- c("pitch",tbl[,1]) 172 | 173 | # multiple by pct of total 174 | tbl[,-1] <- tbl[,-1] * t(ht_pitch_arrieta) 175 | 176 | tbl_long <- gather(tbl, key=pitch2, value=value, -pitch) 177 | ``` 178 | 179 | 180 | ### Draw the Sunburst 181 | 182 | ```{r} 183 | tbl_long %>% 184 | mutate(path = paste(pitch,pitch2,sep="-")) %>% 185 | select(path, value) %>% 186 | sunburst() 187 | ``` 188 | 189 | ## Thanks 190 | 191 | Thanks so much to [Kerry Rodden](https://twitter.com/kerryrodden) who provided the original [sunburst](http://bl.ocks.org/kerryrodden/7090426) on which this is based. 192 | 193 | Thanks [Mike Bostock](https://bost.ocks.org/mike/) for [d3.js](https://d3js.org) (*please give us a way to pay you*). 194 | 195 | Thanks [Tim Holman](http://tholman.com/) for the great [github-corners](http://tholman.com/github-corners/). 196 | 197 | Thanks [skeleton](http://getskeleton.com/). 198 | 199 | Thanks Carson Sievert for `pitchRx` and all your work on [`plotly`](https://github.com/ropensci/plotly). 200 | 201 | Thanks Dan Malter for the great markov pitch post. 202 | 203 |
    204 | --------------------------------------------------------------------------------