├── inst ├── examples │ ├── .gitignore │ ├── shiny │ │ ├── ui.R │ │ └── server.R │ ├── examples.Rmd │ └── examples.R └── htmlwidgets │ ├── forceNetwork.yaml │ ├── chordNetwork.yaml │ ├── dendroNetwork.yaml │ ├── diagonalNetwork.yaml │ ├── radialNetwork.yaml │ ├── simpleNetwork.yaml │ ├── sankeyNetwork.yaml │ ├── chordNetwork.js │ ├── simpleNetwork.js │ ├── diagonalNetwork.js │ ├── radialNetwork.js │ ├── forceNetwork.js │ ├── dendroNetwork.js │ ├── sankeyNetwork.js │ └── lib │ └── sankey.js ├── .gitignore ├── data ├── MisLinks.rda └── MisNodes.rda ├── .Rbuildignore ├── man ├── networkD3-package.Rd ├── MisLinks.Rd ├── MisNodes.Rd ├── JS.Rd ├── saveNetwork.Rd ├── as.radialNetwork.Rd ├── networkD3-shiny.Rd ├── chordNetwork.Rd ├── dendroNetwork.Rd ├── simpleNetwork.Rd ├── sankeyNetwork.Rd ├── radialNetwork.Rd ├── diagonalNetwork.Rd └── forceNetwork.Rd ├── .travis.yml ├── NetworkD3.Rproj ├── R ├── saveNetwork.R ├── data-definitions.R ├── networkD3.R ├── utils.R ├── simpleNetwork.R ├── chordNetwork.R ├── dendroNetwork.R ├── diagonalNetwork.R ├── sankeyNetwork.R ├── radialNetwork.R └── forceNetwork.R ├── NAMESPACE ├── DESCRIPTION ├── README.md ├── JSONdata ├── energy.json ├── flare.json └── miserables.json └── NEWS /inst/examples/.gitignore: -------------------------------------------------------------------------------- 1 | examples.html 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .DS_Store 5 | .sass-cache 6 | -------------------------------------------------------------------------------- /data/MisLinks.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MangoTheCat/networkD3/master/data/MisLinks.rda -------------------------------------------------------------------------------- /data/MisNodes.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MangoTheCat/networkD3/master/data/MisNodes.rda -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^JSONdata 4 | .travis.yml 5 | .sass-cache 6 | ^.*\.o$ 7 | -------------------------------------------------------------------------------- /inst/htmlwidgets/forceNetwork.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: d3 3 | version: 3.5.2 4 | src: "htmlwidgets/lib/d3-3.5.2" 5 | script: d3.min.js 6 | -------------------------------------------------------------------------------- /inst/htmlwidgets/chordNetwork.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: d3 3 | version: 3.5.2 4 | src: "htmlwidgets/lib/d3-3.5.2" 5 | script: d3.min.js 6 | 7 | -------------------------------------------------------------------------------- /inst/htmlwidgets/dendroNetwork.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: d3 3 | version: 3.5.2 4 | src: "htmlwidgets/lib/d3-3.5.2" 5 | script: d3.min.js 6 | -------------------------------------------------------------------------------- /inst/htmlwidgets/diagonalNetwork.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: d3 3 | version: 3.5.2 4 | src: "htmlwidgets/lib/d3-3.5.2" 5 | script: d3.min.js 6 | -------------------------------------------------------------------------------- /inst/htmlwidgets/radialNetwork.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: d3 3 | version: 3.5.2 4 | src: "htmlwidgets/lib/d3-3.5.2" 5 | script: d3.min.js 6 | -------------------------------------------------------------------------------- /inst/htmlwidgets/simpleNetwork.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: d3 3 | version: 3.5.2 4 | src: "htmlwidgets/lib/d3-3.5.2" 5 | script: d3.min.js 6 | -------------------------------------------------------------------------------- /inst/htmlwidgets/sankeyNetwork.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: d3 3 | version: 3.5.2 4 | src: "htmlwidgets/lib/d3-3.5.2" 5 | script: d3.min.js 6 | - name: sankey 7 | version: 1.0 8 | src: "htmlwidgets/lib" 9 | script: sankey.js 10 | -------------------------------------------------------------------------------- /man/networkD3-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/networkD3.R 3 | \docType{package} 4 | \name{networkD3-package} 5 | \alias{networkD3} 6 | \alias{networkD3-package} 7 | \title{Tools for Creating D3 Network Graphs from R} 8 | \description{ 9 | Creates D3 JavaScript network, tree, dendrogram, and Sankey graphs from R. 10 | } 11 | 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | branches: 3 | only: 4 | - master 5 | script: ./travis-tool.sh run_tests 6 | before_script: 7 | - curl -OL http://raw.github.com/craigcitro/r-travis/master/scripts/travis-tool.sh 8 | - chmod 755 ./travis-tool.sh 9 | - ./travis-tool.sh bootstrap 10 | - ./travis-tool.sh install_deps 11 | - ./travis-tool.sh assertthat 12 | notifications: 13 | email: 14 | - christopher.gandrud@gmail.com 15 | -------------------------------------------------------------------------------- /NetworkD3.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: pdfLaTeX 14 | 15 | BuildType: Package 16 | PackageUseDevtools: Yes 17 | PackageInstallArgs: --no-multiarch --with-keep.source 18 | PackageCheckArgs: --as-cran 19 | PackageRoxygenize: rd,collate,namespace,vignette 20 | -------------------------------------------------------------------------------- /man/MisLinks.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data-definitions.R 3 | \docType{data} 4 | \name{MisLinks} 5 | \alias{MisLinks} 6 | \title{Les Miserables character links} 7 | \format{A data set with 254 observations of 3 variables.} 8 | \source{ 9 | See Mike Bostock \url{http://bl.ocks.org/mbostock/4062045}. 10 | } 11 | \usage{ 12 | MisLinks 13 | } 14 | \description{ 15 | A data file of links from Knuth's Les Miserables characters data base. 16 | } 17 | \keyword{datasets} 18 | 19 | -------------------------------------------------------------------------------- /man/MisNodes.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data-definitions.R 3 | \docType{data} 4 | \name{MisNodes} 5 | \alias{MisNodes} 6 | \title{Les Miserables character nodes} 7 | \format{A data set with 77 observations of 2 variables, plus made up 8 | node size variable.} 9 | \source{ 10 | See Mike Bostock \url{http://bl.ocks.org/mbostock/4062045}. 11 | } 12 | \usage{ 13 | MisNodes 14 | } 15 | \description{ 16 | A data file of nodes from Knuth's Les Miserables characters data base. 17 | } 18 | \keyword{datasets} 19 | 20 | -------------------------------------------------------------------------------- /R/saveNetwork.R: -------------------------------------------------------------------------------- 1 | #' Save a network graph to an HTML file 2 | #' 3 | #' Save a networkD3 graph to an HTML file for sharing with others. The HTML can 4 | #' include it's dependencies in an adjacent directory or can bundle all 5 | #' dependencies into the HTML file (via base64 encoding). 6 | #' 7 | #' @param network Network to save (e.g. result of calling the function 8 | #' \code{simpleNetwork}). 9 | #' 10 | #' @inheritParams htmlwidgets::saveWidget 11 | #' 12 | #' @export 13 | saveNetwork <- function(network, file, selfcontained = TRUE) { 14 | htmlwidgets::saveWidget(network, file, selfcontained) 15 | } 16 | -------------------------------------------------------------------------------- /man/JS.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{JS} 4 | \alias{JS} 5 | \title{Create character strings that will be evaluated as JavaScript} 6 | \source{ 7 | A direct import of \code{JS} from Ramnath Vaidyanathan, Yihui Xie, 8 | JJ Allaire, Joe Cheng and Kenton Russell (2015). \link{htmlwidgets}: HTML 9 | Widgets for R. R package version 0.4. 10 | } 11 | \usage{ 12 | JS(...) 13 | } 14 | \arguments{ 15 | \item{...}{character string to evaluate} 16 | } 17 | \description{ 18 | Create character strings that will be evaluated as JavaScript 19 | } 20 | 21 | -------------------------------------------------------------------------------- /R/data-definitions.R: -------------------------------------------------------------------------------- 1 | 2 | #' Les Miserables character nodes 3 | #' 4 | #' A data file of nodes from Knuth's Les Miserables characters data base. 5 | #' 6 | #' @format A data set with 77 observations of 2 variables, plus made up 7 | #' node size variable. 8 | #' @source See Mike Bostock \url{http://bl.ocks.org/mbostock/4062045}. 9 | "MisNodes" 10 | 11 | #' Les Miserables character links 12 | #' 13 | #' A data file of links from Knuth's Les Miserables characters data base. 14 | #' 15 | #' @format A data set with 254 observations of 3 variables. 16 | #' @source See Mike Bostock \url{http://bl.ocks.org/mbostock/4062045}. 17 | "MisLinks" 18 | -------------------------------------------------------------------------------- /inst/examples/shiny/ui.R: -------------------------------------------------------------------------------- 1 | library(shiny) 2 | library(networkD3) 3 | 4 | shinyUI(fluidPage( 5 | 6 | titlePanel("Shiny networkD3 "), 7 | 8 | sidebarLayout( 9 | sidebarPanel( 10 | numericInput("opacity", "Opacity", 0.6, min = 0.1, max = 1, step = .1) 11 | ), 12 | mainPanel( 13 | tabsetPanel( 14 | tabPanel("Simple Network", simpleNetworkOutput("simple")), 15 | tabPanel("Force Network", forceNetworkOutput("force")), 16 | tabPanel("Force Network with Legend & Radius", forceNetworkOutput("forceRadius")), 17 | tabPanel("Sankey Network", sankeyNetworkOutput("sankey")), 18 | tabPanel("Reingold-Tilford Tree", radialNetworkOutput("rt")) 19 | ) 20 | ) 21 | ) 22 | )) -------------------------------------------------------------------------------- /man/saveNetwork.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/saveNetwork.R 3 | \name{saveNetwork} 4 | \alias{saveNetwork} 5 | \title{Save a network graph to an HTML file} 6 | \usage{ 7 | saveNetwork(network, file, selfcontained = TRUE) 8 | } 9 | \arguments{ 10 | \item{network}{Network to save (e.g. result of calling the function 11 | \code{simpleNetwork}).} 12 | 13 | \item{file}{File to save HTML into} 14 | 15 | \item{selfcontained}{Whether to save the HTML as a single self-contained file 16 | (with external resources base64 encoded) or a file with external resources 17 | placed in an adjacent directory.} 18 | } 19 | \description{ 20 | Save a networkD3 graph to an HTML file for sharing with others. The HTML can 21 | include it's dependencies in an adjacent directory or can bundle all 22 | dependencies into the HTML file (via base64 encoding). 23 | } 24 | 25 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(JS) 4 | export(as.radialNetwork) 5 | export(chordNetwork) 6 | export(chordNetworkOutput) 7 | export(dendroNetwork) 8 | export(dendroNetworkOutput) 9 | export(diagonalNetwork) 10 | export(diagonalNetworkOutput) 11 | export(forceNetwork) 12 | export(forceNetworkOutput) 13 | export(radialNetwork) 14 | export(radialNetworkOutput) 15 | export(renderDendroNetwork) 16 | export(renderDiagonalNetwork) 17 | export(renderForceNetwork) 18 | export(renderRadialNetwork) 19 | export(renderSankeyNetwork) 20 | export(renderSimpleNetwork) 21 | export(renderchordNetwork) 22 | export(sankeyNetwork) 23 | export(sankeyNetworkOutput) 24 | export(saveNetwork) 25 | export(simpleNetwork) 26 | export(simpleNetworkOutput) 27 | importFrom(htmlwidgets,shinyRenderWidget) 28 | importFrom(htmlwidgets,shinyWidgetOutput) 29 | importFrom(stats,as.dendrogram) 30 | importFrom(stats,setNames) 31 | importFrom(utils,modifyList) 32 | -------------------------------------------------------------------------------- /man/as.radialNetwork.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/radialNetwork.R 3 | \name{as.radialNetwork} 4 | \alias{as.radialNetwork} 5 | \title{Convert an R hclust or dendrogram object into a radialNetwork list.} 6 | \usage{ 7 | as.radialNetwork(d, root) 8 | } 9 | \arguments{ 10 | \item{d}{An object of R class \code{hclust} or \code{dendrogram}.} 11 | 12 | \item{root}{An optional name for the root node. If missing, use the first 13 | argument variable name.} 14 | } 15 | \description{ 16 | \code{as.radialNetwork} converts an R hclust or dendrogram object into a list 17 | suitable for use by the \code{radialNetwork} function. 18 | } 19 | \details{ 20 | \code{as.radialNetwork} coverts R objects of class \code{hclust} or 21 | \code{dendrogram} into a list suitable for use with the \code{radialNetwork} 22 | function. 23 | } 24 | \examples{ 25 | # Create a hierarchical cluster object and display with radialNetwork 26 | ## dontrun 27 | hc <- hclust(dist(USArrests), "ave") 28 | radialNetwork(as.radialNetwork(hc)) 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: networkD3 2 | Type: Package 3 | Title: D3 JavaScript Network Graphs from R 4 | Description: Creates 'D3' 'JavaScript' network, tree, dendrogram, and Sankey 5 | graphs from 'R'. 6 | Version: 0.2.9 7 | Date: 2016-01-07 8 | Authors@R: c( 9 | person("Christopher", "Gandrud", email = "christopher.gandrud@gmail.com", 10 | role = c("aut", "cre")), 11 | person("J.J.", "Allaire", role = "aut"), 12 | person("Kenton", "Russell", role = "aut"), 13 | person("B.W.", "Lewis", role = "ctb"), 14 | person("Kevin", "Kuo", role = "ctb"), 15 | person("Charles", "Sese", role = "ctb"), 16 | person("Peter", "Ellis", role = "ctb"), 17 | person("Jonathan", "Owen", role = "ctb"), 18 | person("Jennifer","Rogers", role = "ctb") 19 | ) 20 | URL: http://cran.r-project.org/package=networkD3 21 | BugReports: https://github.com/christophergandrud/networkD3/issues 22 | License: GPL (>= 3) 23 | Depends: 24 | R (>= 3.0.0) 25 | Imports: 26 | htmlwidgets (>= 0.3.2) 27 | Suggests: 28 | htmltools (>= 0.2.6), 29 | jsonlite 30 | Enhances: knitr, shiny 31 | LazyData: true 32 | RoxygenNote: 5.0.1 33 | -------------------------------------------------------------------------------- /R/networkD3.R: -------------------------------------------------------------------------------- 1 | 2 | #' Tools for Creating D3 Network Graphs from R 3 | #' 4 | #' Creates D3 JavaScript network, tree, dendrogram, and Sankey graphs from R. 5 | #' 6 | #' @name networkD3-package 7 | #' @aliases networkD3 8 | #' @docType package 9 | NULL 10 | 11 | 12 | #' Shiny bindings for networkD3 widgets 13 | #' 14 | #' Output and render functions for using networkD3 widgets within Shiny 15 | #' applications and interactive Rmd documents. 16 | #' 17 | #' @param outputId output variable to read from 18 | #' @param width,height Must be a valid CSS unit (like \code{"100\%"}, 19 | #' \code{"400px"}, \code{"auto"}) or a number, which will be coerced to a 20 | #' string and have \code{"px"} appended. 21 | #' @param expr An expression that generates a networkD3 graph 22 | #' @param env The environment in which to evaluate \code{expr}. 23 | #' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This 24 | #' is useful if you want to save an expression in a variable. 25 | #' 26 | #' @importFrom htmlwidgets shinyWidgetOutput 27 | #' @importFrom htmlwidgets shinyRenderWidget 28 | #' 29 | #' @name networkD3-shiny 30 | NULL 31 | -------------------------------------------------------------------------------- /inst/examples/shiny/server.R: -------------------------------------------------------------------------------- 1 | library(shiny) 2 | library(networkD3) 3 | 4 | data(MisLinks) 5 | data(MisNodes) 6 | 7 | shinyServer(function(input, output) { 8 | 9 | output$simple <- renderSimpleNetwork({ 10 | src <- c("A", "A", "A", "A", "B", "B", "C", "C", "D") 11 | target <- c("B", "C", "D", "J", "E", "F", "G", "H", "I") 12 | networkData <- data.frame(src, target) 13 | simpleNetwork(networkData, opacity = input$opacity) 14 | }) 15 | 16 | output$force <- renderForceNetwork({ 17 | forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source", 18 | Target = "target", Value = "value", NodeID = "name", 19 | Group = "group", opacity = input$opacity) 20 | }) 21 | 22 | output$forceRadius <- renderForceNetwork({ 23 | forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source", 24 | Target = "target", Value = "value", NodeID = "name", 25 | Nodesize = 'size', radiusCalculation = " Math.sqrt(d.nodesize)+6", 26 | Group = "group", opacity = input$opacity, legend = T) 27 | }) 28 | 29 | output$sankey <- renderSankeyNetwork({ 30 | URL <- "https://cdn.rawgit.com/christophergandrud/networkD3/master/JSONdata/energy.json" 31 | Energy <- jsonlite::fromJSON(URL) 32 | sankeyNetwork(Links = Energy$links, Nodes = Energy$nodes, Source = "source", 33 | Target = "target", Value = "value", NodeID = "name", 34 | fontSize = 12, nodeWidth = 30) 35 | }) 36 | 37 | output$rt <- renderRadialNetwork({ 38 | Flare <- jsonlite::fromJSON( 39 | "https://gist.githubusercontent.com/mbostock/4063550/raw/a05a94858375bd0ae023f6950a2b13fac5127637/flare.json", 40 | simplifyDataFrame = FALSE 41 | ) 42 | radialNetwork(List = Flare, fontSize = 10, opacity = 0.9, margin=0) 43 | }) 44 | 45 | }) 46 | -------------------------------------------------------------------------------- /inst/examples/examples.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "R Markdown networkD3" 3 | output: 4 | html_document: 5 | toc: true 6 | --- 7 | 8 | ## simpleNetwork 9 | 10 | ```{r} 11 | library(networkD3) 12 | src <- c("A", "A", "A", "A", "B", "B", "C", "C", "D") 13 | target <- c("B", "C", "D", "J", "E", "F", "G", "H", "I") 14 | networkData <- data.frame(src, target) 15 | simpleNetwork(networkData) 16 | ``` 17 | 18 | 19 | ## forceNetwork 20 | 21 | ```{r} 22 | data(MisLinks) 23 | data(MisNodes) 24 | forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source", 25 | Target = "target", Value = "value", NodeID = "name", 26 | Group = "group", opacity = 0.4) 27 | ``` 28 | 29 | 30 | # sankeyNetwork 31 | 32 | ```{r} 33 | URL <- "https://raw.githubusercontent.com/christophergandrud/d3Network/sankey/JSONdata/energy.json" 34 | Energy <- RCurl::getURL(URL, ssl.verifypeer = FALSE) 35 | EngLinks <- JSONtoDF(jsonStr = Energy, array = "links") 36 | EngNodes <- JSONtoDF(jsonStr = Energy, array = "nodes") 37 | 38 | sankeyNetwork(Links = EngLinks, Nodes = EngNodes, Source = "source", 39 | Target = "target", Value = "value", NodeID = "name", 40 | fontSize = 12, nodeWidth = 30) 41 | ``` 42 | 43 | # diagonalNetwork 44 | 45 | ```{r} 46 | Flare <- RCurl::getURL("https://gist.githubusercontent.com/mbostock/4063550/raw/a05a94858375bd0ae023f6950a2b13fac5127637/flare.json") 47 | Flare <- rjson::fromJSON(Flare) 48 | diagonalNetwork(List = Flare, fontSize = 10, opacity = 0.9, margin=0) 49 | ``` 50 | 51 | # radialNetwork 52 | 53 | ```{r} 54 | Flare <- RCurl::getURL("https://gist.githubusercontent.com/mbostock/4063550/raw/a05a94858375bd0ae023f6950a2b13fac5127637/flare.json") 55 | Flare <- rjson::fromJSON(Flare) 56 | radialNetwork(List = Flare, fontSize = 10, opacity = 0.9, margin=0) 57 | ``` 58 | 59 | # dendroNetwork 60 | 61 | ```{r} 62 | hc <- hclust(dist(USArrests), "ave") 63 | dendroNetwork(hc, height = 600) 64 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # D3 JavaScript Network Graphs from R 2 | 3 | Version 0.2.9 4 | [![CRAN Version](http://www.r-pkg.org/badges/version/networkD3)](http://cran.r-project.org/package=networkD3) 5 | [![Build Status](https://travis-ci.org/christophergandrud/networkD3.svg?branch=master)](https://travis-ci.org/christophergandrud/networkD3) 6 | ![CRAN Monthly Downloads](http://cranlogs.r-pkg.org/badges/last-month/networkD3) 7 | ![CRAN Total Downloads](http://cranlogs.r-pkg.org/badges/grand-total/networkD3) 8 | 9 | This README includes information on set up and a number of basic examples. 10 | For more information see the package's [main page](http://christophergandrud.github.io/networkD3/). 11 | 12 | ## Usage 13 | 14 | Here's an example of `simpleNetwork`: 15 | 16 | ```R 17 | # Create fake data 18 | src <- c("A", "A", "A", "A", "B", "B", "C", "C", "D") 19 | target <- c("B", "C", "D", "J", "E", "F", "G", "H", "I") 20 | networkData <- data.frame(src, target) 21 | 22 | # Plot 23 | simpleNetwork(networkData) 24 | ``` 25 | 26 | Here's `forceNetwork`: 27 | 28 | ```R 29 | # Load data 30 | data(MisLinks) 31 | data(MisNodes) 32 | 33 | # Plot 34 | forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source", 35 | Target = "target", Value = "value", NodeID = "name", 36 | Group = "group", opacity = 0.4, 37 | colourScale = "d3.scale.category20b()") 38 | ``` 39 | 40 | Here's `sankeyNetwork` using a downloaded JSON data file: 41 | 42 | ```R 43 | # Recreate Bostock Sankey diagram: http://bost.ocks.org/mike/sankey/ 44 | # Load energy projection data 45 | URL <- paste0("https://cdn.rawgit.com/christophergandrud/networkD3/", 46 | "master/JSONdata/energy.json") 47 | Energy <- jsonlite::fromJSON(URL) 48 | 49 | # Plot 50 | sankeyNetwork(Links = Energy$links, Nodes = Energy$nodes, Source = "source", 51 | Target = "target", Value = "value", NodeID = "name", 52 | units = "TWh", fontSize = 12, nodeWidth = 30) 53 | ``` 54 | 55 | ### Saving to an external file 56 | 57 | Use `saveNetwork` to save a network to stand alone HTML file: 58 | 59 | ```R 60 | library(magrittr) 61 | 62 | simpleNetwork(networkData) %>% saveNetwork(file = 'Net1.html') 63 | ``` 64 | 65 | ## Note 66 | 67 | networkD3 began as a port of 68 | [d3Network](http://christophergandrud.github.io/d3Network/) package to the 69 | [htmlwidgets](https://github.com/ramnathv/htmlwidgets) framework. d3Network is no longer supported. 70 | -------------------------------------------------------------------------------- /man/networkD3-shiny.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/chordNetwork.R, R/dendroNetwork.R, R/diagonalNetwork.R, R/forceNetwork.R, R/networkD3.R, R/radialNetwork.R, R/sankeyNetwork.R, R/simpleNetwork.R 3 | \name{chordNetworkOutput} 4 | \alias{chordNetworkOutput} 5 | \alias{dendroNetworkOutput} 6 | \alias{diagonalNetworkOutput} 7 | \alias{forceNetworkOutput} 8 | \alias{networkD3-shiny} 9 | \alias{radialNetworkOutput} 10 | \alias{renderDendroNetwork} 11 | \alias{renderDiagonalNetwork} 12 | \alias{renderForceNetwork} 13 | \alias{renderRadialNetwork} 14 | \alias{renderSankeyNetwork} 15 | \alias{renderSimpleNetwork} 16 | \alias{renderchordNetwork} 17 | \alias{sankeyNetworkOutput} 18 | \alias{simpleNetworkOutput} 19 | \title{Shiny bindings for networkD3 widgets} 20 | \usage{ 21 | chordNetworkOutput(outputId, width = "100\%", height = "500px") 22 | 23 | renderchordNetwork(expr, env = parent.frame(), quoted = FALSE) 24 | 25 | dendroNetworkOutput(outputId, width = "100\%", height = "800px") 26 | 27 | renderDendroNetwork(expr, env = parent.frame(), quoted = FALSE) 28 | 29 | diagonalNetworkOutput(outputId, width = "100\%", height = "800px") 30 | 31 | renderDiagonalNetwork(expr, env = parent.frame(), quoted = FALSE) 32 | 33 | forceNetworkOutput(outputId, width = "100\%", height = "500px") 34 | 35 | renderForceNetwork(expr, env = parent.frame(), quoted = FALSE) 36 | 37 | radialNetworkOutput(outputId, width = "100\%", height = "800px") 38 | 39 | renderRadialNetwork(expr, env = parent.frame(), quoted = FALSE) 40 | 41 | sankeyNetworkOutput(outputId, width = "100\%", height = "500px") 42 | 43 | renderSankeyNetwork(expr, env = parent.frame(), quoted = FALSE) 44 | 45 | simpleNetworkOutput(outputId, width = "100\%", height = "500px") 46 | 47 | renderSimpleNetwork(expr, env = parent.frame(), quoted = FALSE) 48 | } 49 | \arguments{ 50 | \item{outputId}{output variable to read from} 51 | 52 | \item{width, height}{Must be a valid CSS unit (like \code{"100\%"}, 53 | \code{"400px"}, \code{"auto"}) or a number, which will be coerced to a 54 | string and have \code{"px"} appended.} 55 | 56 | \item{expr}{An expression that generates a networkD3 graph} 57 | 58 | \item{env}{The environment in which to evaluate \code{expr}.} 59 | 60 | \item{quoted}{Is \code{expr} a quoted expression (with \code{quote()})? This 61 | is useful if you want to save an expression in a variable.} 62 | } 63 | \description{ 64 | Output and render functions for using networkD3 widgets within Shiny 65 | applications and interactive Rmd documents. 66 | } 67 | 68 | -------------------------------------------------------------------------------- /man/chordNetwork.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/chordNetwork.R 3 | \name{chordNetwork} 4 | \alias{chordNetwork} 5 | \title{Create Reingold-Tilford Tree network diagrams.} 6 | \source{ 7 | Mike Bostock: \url{https://github.com/mbostock/d3/wiki/Chord-Layout}. 8 | } 9 | \usage{ 10 | chordNetwork(Data, height = 500, width = 500, initialOpacity = 0.8, 11 | useTicks = 0, colourScale = c("#1f77b4", "#aec7e8", "#ff7f0e", "#ffbb78", 12 | "#2ca02c", "#98df8a", "#d62728", "#ff9896", "#9467bd", "#c5b0d5", "#8c564b", 13 | "#c49c94", "#e377c2", "#f7b6d2", "#7f7f7f", "#c7c7c7", "#bcbd22", "#dbdb8d", 14 | "#17becf", "#9edae5"), padding = 0.1, fontSize = 14, 15 | fontFamily = "sans-serif", labels = c(), labelDistance = 30) 16 | } 17 | \arguments{ 18 | \item{Data}{A square matrix or data frame whose (n, m) entry represents 19 | the strength of the link from group n to group m} 20 | 21 | \item{height}{height for the network graph's frame area in pixels (if 22 | \code{NULL} then height is automatically determined based on context)} 23 | 24 | \item{width}{numeric width for the network graph's frame area in pixels (if 25 | \code{NULL} then width is automatically determined based on context)} 26 | 27 | \item{initialOpacity}{specify the opacity before the user mouses over 28 | the link} 29 | 30 | \item{useTicks}{integer number of ticks on the radial axis. 31 | The default is `0` which means no ticks will be drawn.} 32 | 33 | \item{colourScale}{specify the hexadecimal colours in which to display 34 | the different categories. If there are fewer colours than categories, 35 | the last colour is repeated as necessary (if \code{NULL} then defaults 36 | to D3 colour scale)} 37 | 38 | \item{padding}{specify the amount of space between adjacent categories 39 | on the outside of the graph} 40 | 41 | \item{fontSize}{numeric font size in pixels for the node text labels.} 42 | 43 | \item{fontFamily}{font family for the node text labels.} 44 | 45 | \item{labels}{vector containing labels of the categories} 46 | 47 | \item{labelDistance}{integer distance in pixels (px) between 48 | text labels and outer radius. The default is `30`.} 49 | } 50 | \description{ 51 | Create Reingold-Tilford Tree network diagrams. 52 | } 53 | \examples{ 54 | \dontrun{ 55 | #### Data about hair colour preferences, from 56 | ## https://github.com/mbostock/d3/wiki/Chord-Layout 57 | 58 | hairColourData <- matrix(c(11975, 1951, 8010, 1013, 59 | 5871, 10048, 16145, 990, 60 | 8916, 2060, 8090, 940, 61 | 2868, 6171, 8045, 6907), 62 | nrow = 4) 63 | 64 | chordNetwork(Data = hairColourData, 65 | width = 500, 66 | height = 500, 67 | colourScale = c("#000000", 68 | "#FFDD89", 69 | "#957244", 70 | "#F26223"), 71 | labels = c("red", "brown", "blond", "gray")) 72 | 73 | } 74 | 75 | } 76 | 77 | -------------------------------------------------------------------------------- /man/dendroNetwork.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dendroNetwork.R 3 | \name{dendroNetwork} 4 | \alias{dendroNetwork} 5 | \title{Create hierarchical cluster network diagrams.} 6 | \source{ 7 | Mike Bostock: \url{http://bl.ocks.org/mbostock/4063570}. 8 | 9 | Fabio Nelli: \url{http://www.meccanismocomplesso.org/en/dendrogramma-d3-parte1/} 10 | } 11 | \usage{ 12 | dendroNetwork(hc, height = 500, width = 800, fontSize = 10, 13 | linkColour = "#ccc", nodeColour = "#fff", nodeStroke = "steelblue", 14 | textColour = "#111", textOpacity = 0.9, textRotate = NULL, 15 | opacity = 0.9, margins = NULL, linkType = c("elbow", "diagonal"), 16 | treeOrientation = c("horizontal", "vertical"), zoom = FALSE) 17 | } 18 | \arguments{ 19 | \item{hc}{a hierarchical (\code{hclust}) cluster object.} 20 | 21 | \item{height}{height for the network graph's frame area in pixels} 22 | 23 | \item{width}{numeric width for the network graph's frame area in pixels} 24 | 25 | \item{fontSize}{numeric font size in pixels for the node text labels.} 26 | 27 | \item{linkColour}{character string specifying the colour you want the link 28 | lines to be. Multiple formats supported (e.g. hexadecimal).} 29 | 30 | \item{nodeColour}{character string specifying the colour you want the node 31 | circles to be. Multiple formats supported (e.g. hexadecimal).} 32 | 33 | \item{nodeStroke}{character string specifying the colour you want the node 34 | perimeter to be. Multiple formats supported (e.g. hexadecimal).} 35 | 36 | \item{textColour}{character vector or scalar specifying the colour you want 37 | the text to be before they are clicked. Order should match the order of 38 | \code{hclust$labels}. Multiple formats supported (e.g. hexadecimal).} 39 | 40 | \item{textOpacity}{numeric vector or scalar of the proportion opaque you 41 | would like the text to be before they are clicked. rder should match the 42 | order of \code{hclust$labels}.} 43 | 44 | \item{textRotate}{numeric degress to rotate text for node text. Default 45 | is 0 for horizontal and 65 degrees for vertical.} 46 | 47 | \item{opacity}{numeric value of the proportion opaque you would like the 48 | graph elements to be.} 49 | 50 | \item{margins}{numeric value or named list of plot margins 51 | (top, right, bottom, left). Set the margin appropriately to accomodate 52 | long text labels.} 53 | 54 | \item{linkType}{character specifying the link type between points. Options 55 | are 'elbow' and 'diagonal'.} 56 | 57 | \item{treeOrientation}{character specifying the tree orientation, Options 58 | are 'vertical' and 'horizontal'.} 59 | 60 | \item{zoom}{logical enabling plot zoom and pan} 61 | } 62 | \description{ 63 | Create hierarchical cluster network diagrams. 64 | } 65 | \examples{ 66 | \dontrun{ 67 | hc <- hclust(dist(USArrests), "ave") 68 | 69 | dendroNetwork(hc, height = 600) 70 | dendroNetwork(hc, treeOrientation = "vertical") 71 | 72 | dendroNetwork(hc, height = 600, linkType = "diagonal") 73 | dendroNetwork(hc, treeOrientation = "vertical", linkType = "diagonal") 74 | 75 | dendroNetwork(hc, textColour = c("red", "green", "orange")[cutree(hc, 3)], 76 | height = 600) 77 | dendroNetwork(hc, textColour = c("red", "green", "orange")[cutree(hc, 3)], 78 | treeOrientation = "vertical") 79 | } 80 | 81 | } 82 | 83 | -------------------------------------------------------------------------------- /man/simpleNetwork.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/simpleNetwork.R 3 | \name{simpleNetwork} 4 | \alias{simpleNetwork} 5 | \title{Function for creating simple D3 JavaScript force directed network graphs.} 6 | \source{ 7 | D3.js was created by Michael Bostock. See \url{http://d3js.org/} and, 8 | more specifically for directed networks 9 | \url{https://github.com/mbostock/d3/wiki/Force-Layout} 10 | } 11 | \usage{ 12 | simpleNetwork(Data, Source = NULL, Target = NULL, height = NULL, 13 | width = NULL, linkDistance = 50, charge = -200, fontSize = 7, 14 | fontFamily = "serif", linkColour = "#666", nodeColour = "#3182bd", 15 | nodeClickColour = "#E34A33", textColour = "#3182bd", opacity = 0.6, 16 | zoom = F) 17 | } 18 | \arguments{ 19 | \item{Data}{a data frame object with three columns. The first two are the 20 | names of the linked units. The third records an edge value. (Currently the 21 | third column doesn't affect the graph.)} 22 | 23 | \item{Source}{character string naming the network source variable in the data 24 | frame. If \code{Source = NULL} then the first column of the data frame is 25 | treated as the source.} 26 | 27 | \item{Target}{character string naming the network target variable in the data 28 | frame. If \code{Target = NULL} then the second column of the data frame is 29 | treated as the target.} 30 | 31 | \item{height}{height for the network graph's frame area in pixels (if 32 | \code{NULL} then height is automatically determined based on context)} 33 | 34 | \item{width}{numeric width for the network graph's frame area in pixels (if 35 | \code{NULL} then width is automatically determined based on context)} 36 | 37 | \item{linkDistance}{numeric distance between the links in pixels (actually 38 | arbitrary relative to the diagram's size).} 39 | 40 | \item{charge}{numeric value indicating either the strength of the node 41 | repulsion (negative value) or attraction (positive value).} 42 | 43 | \item{fontSize}{numeric font size in pixels for the node text labels.} 44 | 45 | \item{fontFamily}{font family for the node text labels.} 46 | 47 | \item{linkColour}{character string specifying the colour you want the link 48 | lines to be. Multiple formats supported (e.g. hexadecimal).} 49 | 50 | \item{nodeColour}{character string specifying the colour you want the node 51 | circles to be. Multiple formats supported (e.g. hexadecimal).} 52 | 53 | \item{nodeClickColour}{character string specifying the colour you want the 54 | node circles to be when they are clicked. Also changes the colour of the 55 | text. Multiple formats supported (e.g. hexadecimal).} 56 | 57 | \item{textColour}{character string specifying the colour you want the text to 58 | be before they are clicked. Multiple formats supported (e.g. hexadecimal).} 59 | 60 | \item{opacity}{numeric value of the proportion opaque you would like the 61 | graph elements to be.} 62 | 63 | \item{zoom}{logical value to enable (\code{TRUE}) or disable (\code{FALSE}) 64 | zooming} 65 | } 66 | \description{ 67 | \code{simpleNetwork} creates simple D3 JavaScript force directed network 68 | graphs. 69 | } 70 | \examples{ 71 | # Fake data 72 | Source <- c("A", "A", "A", "A", "B", "B", "C", "C", "D") 73 | Target <- c("B", "C", "D", "J", "E", "F", "G", "H", "I") 74 | NetworkData <- data.frame(Source, Target) 75 | 76 | # Create graph 77 | simpleNetwork(NetworkData) 78 | simpleNetwork(NetworkData, fontFamily = "sans-serif") 79 | 80 | } 81 | 82 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | #' Create character strings that will be evaluated as JavaScript 2 | #' 3 | #' @param ... character string to evaluate 4 | #' 5 | #' @source A direct import of \code{JS} from Ramnath Vaidyanathan, Yihui Xie, 6 | #' JJ Allaire, Joe Cheng and Kenton Russell (2015). \link{htmlwidgets}: HTML 7 | #' Widgets for R. R package version 0.4. 8 | #' 9 | #' @export 10 | 11 | JS <- function (...) 12 | { 13 | x <- c(...) 14 | if (is.null(x)) 15 | return() 16 | if (!is.character(x)) 17 | stop("The arguments for JS() must be a chraracter vector") 18 | x <- paste(x, collapse = "\n") 19 | structure(x, class = unique(c("JS_EVAL", oldClass(x)))) 20 | } 21 | 22 | 23 | 24 | #' Internal function from Wei Luo to convert a data frame to a JSON array 25 | #' 26 | #' @param dtf a data frame object. 27 | #' @source Function from: 28 | #' \url{http://theweiluo.wordpress.com/2011/09/30/r-to-json-for-d3-js-and-protovis/} 29 | #' @keywords internal 30 | #' @noRd 31 | toJSONarray <- function(dtf){ 32 | clnms <- colnames(dtf) 33 | 34 | name.value <- function(i){ 35 | quote <- ''; 36 | if(class(dtf[, i])!='numeric' && class(dtf[, i])!='integer'){ 37 | quote <- '"'; 38 | } 39 | paste('"', i, '" : ', quote, dtf[,i], quote, sep='') 40 | } 41 | objs <- apply(sapply(clnms, name.value), 1, function(x){paste(x, 42 | collapse=', ')}) 43 | objs <- paste('{', objs, '}') 44 | 45 | res <- paste('[', paste(objs, collapse=', '), ']') 46 | 47 | return(res) 48 | } 49 | 50 | 51 | #' Read a text file into a single string 52 | #' 53 | #' @source Code taken directly from Ramnath Vaidyanathan's Slidify 54 | #' \url{https://github.com/ramnathv/slidify}. 55 | #' @param doc path to text document 56 | #' @return string with document contents 57 | #' @keywords internal 58 | #' @noRd 59 | read_file <- function(doc, ...){ 60 | paste(readLines(doc, ...), collapse = '\n') 61 | } 62 | 63 | 64 | #' Utility function to handle margins 65 | #' @param margin an \code{integer}, a named \code{vector} of integers, 66 | #' or a named \code{list} of integers specifying the margins 67 | #' (top, right, bottom, and left) 68 | #' in \code{px}/\code{pixels} for our htmlwidget. If only a single 69 | #' \code{integer} is provided, then the value will be assumed to be 70 | #' the \code{right} margin. 71 | #' @return named \code{list} with top, right, bottom, left margins 72 | #' @noRd 73 | margin_handler <- function(margin){ 74 | # margin can be either a single value or a list with any of 75 | # top, right, bottom, left 76 | # if margin is a single value, then we will stick 77 | # with the original behavior of networkD3 and use it for the right margin 78 | if(!is.null(margin) && length(margin) == 1 && is.null(names(margin))){ 79 | margin <- list( 80 | top = NULL, 81 | right = margin, 82 | bottom = NULL, 83 | left = NULL 84 | ) 85 | } else if(!is.null(margin)){ 86 | # if margin is a named vector then convert to list 87 | if(!is.list(margin) && !is.null(names(margin))){ 88 | margin <- as.list(margin) 89 | } 90 | # if we are here then margin should be a list and 91 | # we will use the values supplied with NULL as default 92 | margin <- modifyList( 93 | list(top = NULL, right = NULL, bottom = NULL, left = NULL), 94 | margin 95 | ) 96 | } else { 97 | # if margin is null, then make it a list of nulls for each position 98 | margin <- list(top = NULL, right = NULL, bottom = NULL, left = NULL) 99 | } 100 | } -------------------------------------------------------------------------------- /man/sankeyNetwork.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/sankeyNetwork.R 3 | \name{sankeyNetwork} 4 | \alias{sankeyNetwork} 5 | \title{Create a D3 JavaScript Sankey diagram} 6 | \source{ 7 | D3.js was created by Michael Bostock. See \url{http://d3js.org/} and, more 8 | specifically for Sankey diagrams \url{http://bost.ocks.org/mike/sankey/}. 9 | } 10 | \usage{ 11 | sankeyNetwork(Links, Nodes, Source, Target, Value, NodeID, NodeGroup = NodeID, 12 | LinkGroup = NULL, units = "", colourScale = JS("d3.scale.category20()"), 13 | fontSize = 7, fontFamily = NULL, nodeWidth = 15, nodePadding = 10, 14 | margin = NULL, height = NULL, width = NULL, iterations = 32) 15 | } 16 | \arguments{ 17 | \item{Links}{a data frame object with the links between the nodes. It should 18 | have include the \code{Source} and \code{Target} for each link. An optional 19 | \code{Value} variable can be included to specify how close the nodes are to 20 | one another.} 21 | 22 | \item{Nodes}{a data frame containing the node id and properties of the nodes. 23 | If no ID is specified then the nodes must be in the same order as the 24 | \code{Source} variable column in the \code{Links} data frame. Currently only 25 | grouping variable is allowed.} 26 | 27 | \item{Source}{character string naming the network source variable in the 28 | \code{Links} data frame.} 29 | 30 | \item{Target}{character string naming the network target variable in the 31 | \code{Links} data frame.} 32 | 33 | \item{Value}{character string naming the variable in the \code{Links} data 34 | frame for how far away the nodes are from one another.} 35 | 36 | \item{NodeID}{character string specifying the node IDs in the \code{Nodes}. 37 | data frame. Must be 0-indexed.} 38 | 39 | \item{NodeGroup}{character string specifying the node groups in the 40 | \code{Nodes}. Used to color the nodes in the network.} 41 | 42 | \item{LinkGroup}{character string specifying the groups in the 43 | \code{Links}. Used to color the links in the network.} 44 | 45 | \item{units}{character string describing physical units (if any) for Value} 46 | 47 | \item{colourScale}{character string specifying the categorical colour 48 | scale for the nodes. See 49 | \url{https://github.com/mbostock/d3/wiki/Ordinal-Scales}.} 50 | 51 | \item{fontSize}{numeric font size in pixels for the node text labels.} 52 | 53 | \item{fontFamily}{font family for the node text labels.} 54 | 55 | \item{nodeWidth}{numeric width of each node.} 56 | 57 | \item{nodePadding}{numeric essentially influences the width height.} 58 | 59 | \item{margin}{an integer or a named \code{list}/\code{vector} of integers 60 | for the plot margins. If using a named \code{list}/\code{vector}, 61 | the positions \code{top}, \code{right}, \code{bottom}, \code{left} 62 | are valid. If a single integer is provided, then the value will be 63 | assigned to the right margin. Set the margin appropriately 64 | to accomodate long text labels.} 65 | 66 | \item{height}{numeric height for the network graph's frame area in pixels.} 67 | 68 | \item{width}{numeric width for the network graph's frame area in pixels.} 69 | 70 | \item{iterations}{number iterations to perform in the node placement 71 | optimization.} 72 | } 73 | \description{ 74 | Create a D3 JavaScript Sankey diagram 75 | } 76 | \examples{ 77 | \dontrun{ 78 | # Recreate Bostock Sankey diagram: http://bost.ocks.org/mike/sankey/ 79 | # Load energy projection data 80 | URL <- paste0('https://cdn.rawgit.com/christophergandrud/networkD3/', 81 | 'master/JSONdata/energy.json') 82 | energy <- jsonlite::fromJSON(URL) 83 | # Plot 84 | sankeyNetwork(Links = energy$links, Nodes = energy$nodes, Source = 'source', 85 | Target = 'target', Value = 'value', NodeID = 'name', 86 | units = 'TWh', fontSize = 12, nodeWidth = 30) 87 | 88 | # Colour links 89 | energy$links$energy_type <- sub(' .*', '', 90 | energy$nodes[energy$links$source + 1, 'name']) 91 | 92 | sankeyNetwork(Links = energy$links, Nodes = energy$nodes, Source = 'source', 93 | Target = 'target', Value = 'value', NodeID = 'name', 94 | LinkGroup = 'energy_type', NodeGroup = NULL) 95 | 96 | } 97 | } 98 | \seealso{ 99 | \code{\link{JS}} 100 | } 101 | 102 | -------------------------------------------------------------------------------- /JSONdata/energy.json: -------------------------------------------------------------------------------- 1 | {"nodes":[ 2 | {"name":"Agricultural 'waste'"}, 3 | {"name":"Bio-conversion"}, 4 | {"name":"Liquid"}, 5 | {"name":"Losses"}, 6 | {"name":"Solid"}, 7 | {"name":"Gas"}, 8 | {"name":"Biofuel imports"}, 9 | {"name":"Biomass imports"}, 10 | {"name":"Coal imports"}, 11 | {"name":"Coal"}, 12 | {"name":"Coal reserves"}, 13 | {"name":"District heating"}, 14 | {"name":"Industry"}, 15 | {"name":"Heating and cooling - commercial"}, 16 | {"name":"Heating and cooling - homes"}, 17 | {"name":"Electricity grid"}, 18 | {"name":"Over generation / exports"}, 19 | {"name":"H2 conversion"}, 20 | {"name":"Road transport"}, 21 | {"name":"Agriculture"}, 22 | {"name":"Rail transport"}, 23 | {"name":"Lighting & appliances - commercial"}, 24 | {"name":"Lighting & appliances - homes"}, 25 | {"name":"Gas imports"}, 26 | {"name":"Ngas"}, 27 | {"name":"Gas reserves"}, 28 | {"name":"Thermal generation"}, 29 | {"name":"Geothermal"}, 30 | {"name":"H2"}, 31 | {"name":"Hydro"}, 32 | {"name":"International shipping"}, 33 | {"name":"Domestic aviation"}, 34 | {"name":"International aviation"}, 35 | {"name":"National navigation"}, 36 | {"name":"Marine algae"}, 37 | {"name":"Nuclear"}, 38 | {"name":"Oil imports"}, 39 | {"name":"Oil"}, 40 | {"name":"Oil reserves"}, 41 | {"name":"Other waste"}, 42 | {"name":"Pumped heat"}, 43 | {"name":"Solar PV"}, 44 | {"name":"Solar Thermal"}, 45 | {"name":"Solar"}, 46 | {"name":"Tidal"}, 47 | {"name":"UK land based bioenergy"}, 48 | {"name":"Wave"}, 49 | {"name":"Wind"} 50 | ], 51 | "links":[ 52 | {"source":0,"target":1,"value":124.729}, 53 | {"source":1,"target":2,"value":0.597}, 54 | {"source":1,"target":3,"value":26.862}, 55 | {"source":1,"target":4,"value":280.322}, 56 | {"source":1,"target":5,"value":81.144}, 57 | {"source":6,"target":2,"value":35}, 58 | {"source":7,"target":4,"value":35}, 59 | {"source":8,"target":9,"value":11.606}, 60 | {"source":10,"target":9,"value":63.965}, 61 | {"source":9,"target":4,"value":75.571}, 62 | {"source":11,"target":12,"value":10.639}, 63 | {"source":11,"target":13,"value":22.505}, 64 | {"source":11,"target":14,"value":46.184}, 65 | {"source":15,"target":16,"value":104.453}, 66 | {"source":15,"target":14,"value":113.726}, 67 | {"source":15,"target":17,"value":27.14}, 68 | {"source":15,"target":12,"value":342.165}, 69 | {"source":15,"target":18,"value":37.797}, 70 | {"source":15,"target":19,"value":4.412}, 71 | {"source":15,"target":13,"value":40.858}, 72 | {"source":15,"target":3,"value":56.691}, 73 | {"source":15,"target":20,"value":7.863}, 74 | {"source":15,"target":21,"value":90.008}, 75 | {"source":15,"target":22,"value":93.494}, 76 | {"source":23,"target":24,"value":40.719}, 77 | {"source":25,"target":24,"value":82.233}, 78 | {"source":5,"target":13,"value":0.129}, 79 | {"source":5,"target":3,"value":1.401}, 80 | {"source":5,"target":26,"value":151.891}, 81 | {"source":5,"target":19,"value":2.096}, 82 | {"source":5,"target":12,"value":48.58}, 83 | {"source":27,"target":15,"value":7.013}, 84 | {"source":17,"target":28,"value":20.897}, 85 | {"source":17,"target":3,"value":6.242}, 86 | {"source":28,"target":18,"value":20.897}, 87 | {"source":29,"target":15,"value":6.995}, 88 | {"source":2,"target":12,"value":121.066}, 89 | {"source":2,"target":30,"value":128.69}, 90 | {"source":2,"target":18,"value":135.835}, 91 | {"source":2,"target":31,"value":14.458}, 92 | {"source":2,"target":32,"value":206.267}, 93 | {"source":2,"target":19,"value":3.64}, 94 | {"source":2,"target":33,"value":33.218}, 95 | {"source":2,"target":20,"value":4.413}, 96 | {"source":34,"target":1,"value":4.375}, 97 | {"source":24,"target":5,"value":122.952}, 98 | {"source":35,"target":26,"value":839.978}, 99 | {"source":36,"target":37,"value":504.287}, 100 | {"source":38,"target":37,"value":107.703}, 101 | {"source":37,"target":2,"value":611.99}, 102 | {"source":39,"target":4,"value":56.587}, 103 | {"source":39,"target":1,"value":77.81}, 104 | {"source":40,"target":14,"value":193.026}, 105 | {"source":40,"target":13,"value":70.672}, 106 | {"source":41,"target":15,"value":59.901}, 107 | {"source":42,"target":14,"value":19.263}, 108 | {"source":43,"target":42,"value":19.263}, 109 | {"source":43,"target":41,"value":59.901}, 110 | {"source":4,"target":19,"value":0.882}, 111 | {"source":4,"target":26,"value":400.12}, 112 | {"source":4,"target":12,"value":46.477}, 113 | {"source":26,"target":15,"value":525.531}, 114 | {"source":26,"target":3,"value":787.129}, 115 | {"source":26,"target":11,"value":79.329}, 116 | {"source":44,"target":15,"value":9.452}, 117 | {"source":45,"target":1,"value":182.01}, 118 | {"source":46,"target":15,"value":19.013}, 119 | {"source":47,"target":15,"value":289.366} 120 | ]} 121 | -------------------------------------------------------------------------------- /man/radialNetwork.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/radialNetwork.R 3 | \name{radialNetwork} 4 | \alias{radialNetwork} 5 | \title{Create Reingold-Tilford Tree network diagrams.} 6 | \source{ 7 | Reingold. E. M., and Tilford, J. S. (1981). Tidier Drawings of Trees. 8 | IEEE Transactions on Software Engineering, SE-7(2), 223-228. 9 | 10 | Mike Bostock: \url{http://bl.ocks.org/mbostock/4063550}. 11 | } 12 | \usage{ 13 | radialNetwork(List, height = NULL, width = NULL, fontSize = 10, 14 | fontFamily = "serif", linkColour = "#ccc", nodeColour = "#fff", 15 | nodeStroke = "steelblue", textColour = "#111", opacity = 0.9, 16 | margin = NULL) 17 | } 18 | \arguments{ 19 | \item{List}{a hierarchical list object with a root node and children.} 20 | 21 | \item{height}{height for the network graph's frame area in pixels (if 22 | \code{NULL} then height is automatically determined based on context)} 23 | 24 | \item{width}{numeric width for the network graph's frame area in pixels (if 25 | \code{NULL} then width is automatically determined based on context)} 26 | 27 | \item{fontSize}{numeric font size in pixels for the node text labels.} 28 | 29 | \item{fontFamily}{font family for the node text labels.} 30 | 31 | \item{linkColour}{character string specifying the colour you want the link 32 | lines to be. Multiple formats supported (e.g. hexadecimal).} 33 | 34 | \item{nodeColour}{character string specifying the colour you want the node 35 | circles to be. Multiple formats supported (e.g. hexadecimal).} 36 | 37 | \item{nodeStroke}{character string specifying the colour you want the node 38 | perimeter to be. Multiple formats supported (e.g. hexadecimal).} 39 | 40 | \item{textColour}{character string specifying the colour you want the text to 41 | be before they are clicked. Multiple formats supported (e.g. hexadecimal).} 42 | 43 | \item{opacity}{numeric value of the proportion opaque you would like the 44 | graph elements to be.} 45 | 46 | \item{margin}{an integer or a named \code{list}/\code{vector} of integers 47 | for the plot margins. If using a named \code{list}/\code{vector}, 48 | the positions \code{top}, \code{right}, \code{bottom}, \code{left} 49 | are valid. If a single integer is provided, then the value will be 50 | assigned to the right margin. Set the margin appropriately 51 | to accomodate long text labels.} 52 | } 53 | \description{ 54 | Create Reingold-Tilford Tree network diagrams. 55 | } 56 | \examples{ 57 | \dontrun{ 58 | #### Create tree from JSON formatted data 59 | ## Download JSON data 60 | # Create URL. paste0 used purely to keep within line width. 61 | URL <- paste0("https://cdn.rawgit.com/christophergandrud/networkD3/", 62 | "master/JSONdata//flare.json") 63 | 64 | ## Convert to list format 65 | Flare <- jsonlite::fromJSON(URL, simplifyDataFrame = FALSE) 66 | 67 | ## Recreate Bostock example from http://bl.ocks.org/mbostock/4063550 68 | radialNetwork(List = Flare, fontSize = 10, opacity = 0.9) 69 | 70 | #### Create a tree dendrogram from an R hclust object 71 | hc <- hclust(dist(USArrests), "ave") 72 | radialNetwork(as.radialNetwork(hc)) 73 | radialNetwork(as.radialNetwork(hc), fontFamily = "cursive") 74 | 75 | #### Create tree from a hierarchical R list 76 | For an alternative structure see: http://stackoverflow.com/a/30747323/1705044 77 | CanadaPC <- list(name = "Canada", children = list(list(name = "Newfoundland", 78 | children = list(list(name = "St. John's"))), 79 | list(name = "PEI", 80 | children = list(list(name = "Charlottetown"))), 81 | list(name = "Nova Scotia", 82 | children = list(list(name = "Halifax"))), 83 | list(name = "New Brunswick", 84 | children = list(list(name = "Fredericton"))), 85 | list(name = "Quebec", 86 | children = list(list(name = "Montreal"), 87 | list(name = "Quebec City"))), 88 | list(name = "Ontario", 89 | children = list(list(name = "Toronto"), 90 | list(name = "Ottawa"))), 91 | list(name = "Manitoba", 92 | children = list(list(name = "Winnipeg"))), 93 | list(name = "Saskatchewan", 94 | children = list(list(name = "Regina"))), 95 | list(name = "Nunavuet", 96 | children = list(list(name = "Iqaluit"))), 97 | list(name = "NWT", 98 | children = list(list(name = "Yellowknife"))), 99 | list(name = "Alberta", 100 | children = list(list(name = "Edmonton"))), 101 | list(name = "British Columbia", 102 | children = list(list(name = "Victoria"), 103 | list(name = "Vancouver"))), 104 | list(name = "Yukon", 105 | children = list(list(name = "Whitehorse"))) 106 | )) 107 | 108 | radialNetwork(List = CanadaPC, fontSize = 10) 109 | } 110 | 111 | } 112 | 113 | -------------------------------------------------------------------------------- /man/diagonalNetwork.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/diagonalNetwork.R 3 | \name{diagonalNetwork} 4 | \alias{diagonalNetwork} 5 | \title{Create Reingold-Tilford Tree network diagrams.} 6 | \source{ 7 | Reingold. E. M., and Tilford, J. S. (1981). Tidier Drawings of Trees. 8 | IEEE Transactions on Software Engineering, SE-7(2), 223-228. 9 | 10 | Mike Bostock: \url{http://bl.ocks.org/mbostock/4339083}. 11 | } 12 | \usage{ 13 | diagonalNetwork(List, height = NULL, width = NULL, fontSize = 10, 14 | fontFamily = "serif", linkColour = "#ccc", nodeColour = "#fff", 15 | nodeStroke = "steelblue", textColour = "#111", opacity = 0.9, 16 | margin = NULL) 17 | } 18 | \arguments{ 19 | \item{List}{a hierarchical list object with a root node and children.} 20 | 21 | \item{height}{height for the network graph's frame area in pixels (if 22 | \code{NULL} then height is automatically determined based on context)} 23 | 24 | \item{width}{numeric width for the network graph's frame area in pixels (if 25 | \code{NULL} then width is automatically determined based on context)} 26 | 27 | \item{fontSize}{numeric font size in pixels for the node text labels.} 28 | 29 | \item{fontFamily}{font family for the node text labels.} 30 | 31 | \item{linkColour}{character string specifying the colour you want the link 32 | lines to be. Multiple formats supported (e.g. hexadecimal).} 33 | 34 | \item{nodeColour}{character string specifying the colour you want the node 35 | circles to be. Multiple formats supported (e.g. hexadecimal).} 36 | 37 | \item{nodeStroke}{character string specifying the colour you want the node 38 | perimeter to be. Multiple formats supported (e.g. hexadecimal).} 39 | 40 | \item{textColour}{character string specifying the colour you want the text to 41 | be before they are clicked. Multiple formats supported (e.g. hexadecimal).} 42 | 43 | \item{opacity}{numeric value of the proportion opaque you would like the 44 | graph elements to be.} 45 | 46 | \item{margin}{an integer or a named \code{list}/\code{vector} of integers 47 | for the plot margins. If using a named \code{list}/\code{vector}, 48 | the positions \code{top}, \code{right}, \code{bottom}, \code{left} 49 | are valid. If a single integer is provided, then the value will be 50 | assigned to the right margin. Set the margin appropriately 51 | to accomodate long text labels.} 52 | } 53 | \description{ 54 | Create Reingold-Tilford Tree network diagrams. 55 | } 56 | \examples{ 57 | \dontrun{ 58 | #### Create tree from JSON formatted data 59 | ## Download JSON data 60 | # Create URL. paste0 used purely to keep within line width. 61 | URL <- paste0("https://cdn.rawgit.com/christophergandrud/networkD3/", 62 | "master/JSONdata//flare.json") 63 | 64 | ## Convert to list format 65 | Flare <- jsonlite::fromJSON(URL, simplifyDataFrame = FALSE) 66 | 67 | ## Recreate Bostock example from http://bl.ocks.org/mbostock/4063550 68 | diagonalNetwork(List = Flare, fontSize = 10, opacity = 0.9) 69 | 70 | #### Create a tree dendrogram from an R hclust object 71 | hc <- hclust(dist(USArrests), "ave") 72 | diagonalNetwork(as.radialNetwork(hc)) 73 | diagonalNetwork(as.radialNetwork(hc), fontFamily = "cursive") 74 | 75 | #### Create tree from a hierarchical R list 76 | For an alternative structure see: http://stackoverflow.com/a/30747323/1705044 77 | CanadaPC <- list(name = "Canada", children = list(list(name = "Newfoundland", 78 | children = list(list(name = "St. John's"))), 79 | list(name = "PEI", 80 | children = list(list(name = "Charlottetown"))), 81 | list(name = "Nova Scotia", 82 | children = list(list(name = "Halifax"))), 83 | list(name = "New Brunswick", 84 | children = list(list(name = "Fredericton"))), 85 | list(name = "Quebec", 86 | children = list(list(name = "Montreal"), 87 | list(name = "Quebec City"))), 88 | list(name = "Ontario", 89 | children = list(list(name = "Toronto"), 90 | list(name = "Ottawa"))), 91 | list(name = "Manitoba", 92 | children = list(list(name = "Winnipeg"))), 93 | list(name = "Saskatchewan", 94 | children = list(list(name = "Regina"))), 95 | list(name = "Nunavuet", 96 | children = list(list(name = "Iqaluit"))), 97 | list(name = "NWT", 98 | children = list(list(name = "Yellowknife"))), 99 | list(name = "Alberta", 100 | children = list(list(name = "Edmonton"))), 101 | list(name = "British Columbia", 102 | children = list(list(name = "Victoria"), 103 | list(name = "Vancouver"))), 104 | list(name = "Yukon", 105 | children = list(list(name = "Whitehorse"))) 106 | )) 107 | 108 | diagonalNetwork(List = CanadaPC, fontSize = 10) 109 | } 110 | 111 | } 112 | 113 | -------------------------------------------------------------------------------- /inst/htmlwidgets/chordNetwork.js: -------------------------------------------------------------------------------- 1 | HTMLWidgets.widget({ 2 | 3 | name: "chordNetwork", 4 | type: "output", 5 | 6 | initialize: function(el, width, height) { 7 | var x = width + 50; 8 | var y = height + 50; 9 | 10 | d3.select(el).append("svg") 11 | .attr("width", x) 12 | .attr("height", y) 13 | .append("g") 14 | .attr("transform", "translate(" + x / 2 + "," + y / 2 + ")"); 15 | 16 | return d3.layout.chord(); 17 | }, 18 | 19 | resize: function(el, width, height, chord) { 20 | var x = width + 50; 21 | var y = height + 50; 22 | d3.select(el).selectAll("svg").attr("width", x).attr("height", y); 23 | d3.select(el).selectAll("svg").select("g") 24 | .attr("transform", "translate(" + x / 2 + "," + y / 2 + ")"); 25 | }, 26 | 27 | renderValue: function(el, x, chord) { 28 | 29 | // Returns an event handler for fading a given chord group. 30 | function fade(opacity) { 31 | return function(g, i) { 32 | s.selectAll(".chord path") 33 | .filter(function(d) { return d.source.index != i && d.target.index != i; }) 34 | .transition() 35 | .style("opacity", opacity); 36 | }; 37 | } 38 | 39 | var para = document.createElement("style"); 40 | para.innerHTML = ".chord path { fill-opacity: "+ x.options.initial_opacity +"; stroke: #000; stroke-width: .0px; }" 41 | document.getElementsByTagName("head")[0].appendChild(para); 42 | 43 | chord.padding(x.options.padding) 44 | .sortSubgroups(d3.descending) 45 | .matrix(x.matrix); 46 | 47 | var s = d3.select(el).select("g"); 48 | var diameter = Math.min(x.options.width, x.options.height); 49 | var innerRadius = Math.min(x.options.width, x.options.height) * .31; 50 | var outerRadius = innerRadius * 1.1; 51 | 52 | var fill = x.options.colour_scale 53 | ?d3.scale.ordinal().domain(x.matrix.length).range(x.options.colour_scale) 54 | :(x.matrix.length>10?d3.scale.category20():d3.scale.category10()); 55 | 56 | s.append("g").selectAll("path") 57 | .data(chord.groups) 58 | .enter().append("g") 59 | .attr("class", "pie-slice") 60 | .append("path") 61 | .style("fill", function(d) { 62 | return fill(d.index); 63 | }) 64 | .style("stroke", function(d) { return fill(d.index); }) 65 | .attr("d", d3.svg.arc().innerRadius(innerRadius).outerRadius(outerRadius)) 66 | .on("mouseover", fade(.1)) 67 | .on("mouseout", fade(1)); 68 | 69 | if(x.options.labels) { 70 | // Forumulas taken from http://sdk.gooddata.com/gooddata-js/example/chord-chart-to-analyze-sales/ 71 | s.append("g").selectAll(".arc") 72 | .data(chord.groups) 73 | .enter().append("svg:text") 74 | .attr("dy", ".35em") 75 | .attr("text-anchor", function(d) { return ((d.startAngle + d.endAngle) / 2) > Math.PI ? "end" : null; }) 76 | .attr("transform", function(d) { 77 | return "rotate(" + (((d.startAngle + d.endAngle) / 2) * 180 / Math.PI - 90) + ")" 78 | + "translate(" + (x.options.height / 2 - x.options.label_distance) + ")" 79 | + (((d.startAngle + d.endAngle) / 2) > Math.PI ? "rotate(180)" : ""); 80 | }).text(function(d) { 81 | return x.options.labels[d.index]; 82 | }).attr("font-size", x.options.font_size + "px") 83 | .attr("font-family", x.options.font_family); 84 | } 85 | 86 | if(x.options.use_ticks) { 87 | var ticks = s.append("g").selectAll("g") 88 | .data(chord.groups) 89 | .enter().append("g").selectAll("g") 90 | .data(groupTicks) 91 | .enter().append("g") 92 | .attr("transform", function(d) { 93 | return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")" 94 | + "translate(" + outerRadius + ",0)"; 95 | }); 96 | 97 | ticks.append("line") 98 | .attr("x1", 1) 99 | .attr("y1", 0) 100 | .attr("x2", 5) 101 | .attr("y2", 0) 102 | .style("stroke", "#000"); 103 | 104 | ticks.append("text") 105 | .attr("x", 8) 106 | .attr("dy", ".35em") 107 | .attr("transform", function(d) { return d.angle > Math.PI ? "rotate(180)translate(-16)" : null; }) 108 | .style("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; }) 109 | .text(function(d) { return d.label; }) 110 | .attr("font-size", x.options.font_size + "px") 111 | .attr("font-family", x.options.font_family); 112 | } 113 | 114 | s.append("g") 115 | .attr("class", "chord") 116 | .selectAll("path") 117 | .data(chord.chords) 118 | .enter().append("path") 119 | .attr("d", d3.svg.chord().radius(innerRadius)) 120 | .style("fill", function(d) { return fill(d.target.index); }) 121 | .style("fill-opacity", x.initial_opacity); 122 | 123 | function groupTicks(d) { 124 | var k = (d.endAngle - d.startAngle) / d.value; 125 | return d3.range(0, d.value, 1000).map(function(v, i) { 126 | return { 127 | angle: v * k + d.startAngle, 128 | label: i % 5 ? null : v / 1000 + "k" 129 | }; 130 | }); 131 | } 132 | 133 | }, 134 | }); 135 | -------------------------------------------------------------------------------- /R/simpleNetwork.R: -------------------------------------------------------------------------------- 1 | #' Function for creating simple D3 JavaScript force directed network graphs. 2 | #' 3 | #' \code{simpleNetwork} creates simple D3 JavaScript force directed network 4 | #' graphs. 5 | #' 6 | #' @param Data a data frame object with three columns. The first two are the 7 | #' names of the linked units. The third records an edge value. (Currently the 8 | #' third column doesn't affect the graph.) 9 | #' @param Source character string naming the network source variable in the data 10 | #' frame. If \code{Source = NULL} then the first column of the data frame is 11 | #' treated as the source. 12 | #' @param Target character string naming the network target variable in the data 13 | #' frame. If \code{Target = NULL} then the second column of the data frame is 14 | #' treated as the target. 15 | #' @param height height for the network graph's frame area in pixels (if 16 | #' \code{NULL} then height is automatically determined based on context) 17 | #' @param width numeric width for the network graph's frame area in pixels (if 18 | #' \code{NULL} then width is automatically determined based on context) 19 | #' @param linkDistance numeric distance between the links in pixels (actually 20 | #' arbitrary relative to the diagram's size). 21 | #' @param charge numeric value indicating either the strength of the node 22 | #' repulsion (negative value) or attraction (positive value). 23 | #' @param fontSize numeric font size in pixels for the node text labels. 24 | #' @param fontFamily font family for the node text labels. 25 | #' @param linkColour character string specifying the colour you want the link 26 | #' lines to be. Multiple formats supported (e.g. hexadecimal). 27 | #' @param nodeColour character string specifying the colour you want the node 28 | #' circles to be. Multiple formats supported (e.g. hexadecimal). 29 | #' @param nodeClickColour character string specifying the colour you want the 30 | #' node circles to be when they are clicked. Also changes the colour of the 31 | #' text. Multiple formats supported (e.g. hexadecimal). 32 | #' @param textColour character string specifying the colour you want the text to 33 | #' be before they are clicked. Multiple formats supported (e.g. hexadecimal). 34 | #' @param opacity numeric value of the proportion opaque you would like the 35 | #' graph elements to be. 36 | #' @param zoom logical value to enable (\code{TRUE}) or disable (\code{FALSE}) 37 | #' zooming 38 | #' 39 | #' @examples 40 | #' # Fake data 41 | #' Source <- c("A", "A", "A", "A", "B", "B", "C", "C", "D") 42 | #' Target <- c("B", "C", "D", "J", "E", "F", "G", "H", "I") 43 | #' NetworkData <- data.frame(Source, Target) 44 | #' 45 | #' # Create graph 46 | #' simpleNetwork(NetworkData) 47 | #' simpleNetwork(NetworkData, fontFamily = "sans-serif") 48 | #' 49 | #' @source D3.js was created by Michael Bostock. See \url{http://d3js.org/} and, 50 | #' more specifically for directed networks 51 | #' \url{https://github.com/mbostock/d3/wiki/Force-Layout} 52 | #' 53 | #' @export 54 | simpleNetwork <- function(Data, 55 | Source = NULL, 56 | Target = NULL, 57 | height = NULL, 58 | width = NULL, 59 | linkDistance = 50, 60 | charge = -200, 61 | fontSize = 7, 62 | fontFamily = "serif", 63 | linkColour = "#666", 64 | nodeColour = "#3182bd", 65 | nodeClickColour = "#E34A33", 66 | textColour = "#3182bd", 67 | opacity = 0.6, 68 | zoom = F) 69 | { 70 | # validate input 71 | if (!is.data.frame(Data)) 72 | stop("data must be a data frame class object.") 73 | 74 | # create links data 75 | if (is.null(Source) && is.null(Target)) 76 | links <- Data[, 1:2] 77 | else if (!is.null(Source) && !is.null(Target)) 78 | links <- data.frame(Data[, Source], Data[, Target]) 79 | names(links) <- c("source", "target") 80 | 81 | # create options 82 | options = list( 83 | linkDistance = linkDistance, 84 | charge = charge, 85 | fontSize = fontSize, 86 | fontFamily = fontFamily, 87 | linkColour = linkColour, 88 | nodeColour = nodeColour, 89 | nodeClickColour = nodeClickColour, 90 | textColour = textColour, 91 | opacity = opacity, 92 | zoom = zoom 93 | ) 94 | 95 | # create widget 96 | htmlwidgets::createWidget( 97 | name = "simpleNetwork", 98 | x = list(links = links, options = options), 99 | width = width, 100 | height = height, 101 | htmlwidgets::sizingPolicy(padding = 10, browser.fill = TRUE), 102 | package = "networkD3" 103 | ) 104 | } 105 | 106 | #' @rdname networkD3-shiny 107 | #' @export 108 | simpleNetworkOutput <- function(outputId, width = "100%", height = "500px") { 109 | shinyWidgetOutput(outputId, "simpleNetwork", width, height, 110 | package = "networkD3") 111 | } 112 | 113 | #' @rdname networkD3-shiny 114 | #' @export 115 | renderSimpleNetwork <- function(expr, env = parent.frame(), quoted = FALSE) { 116 | if (!quoted) { expr <- substitute(expr) } # force quoted 117 | shinyRenderWidget(expr, simpleNetworkOutput, env, quoted = TRUE) 118 | } 119 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | # All changes to networkD3 are documented here. 2 | 3 | ## Version 0.2.9 4 | 5 | - `forceNetwork` now allows you to supply a vector of colours to the 6 | `linkColour`argument. This enables the user to highlight links to specific 7 | nodes. Thanks to Garth Tarr. 8 | 9 | - Minor documentation improvements. 10 | 11 | ## Version 0.2.8 12 | 13 | - Added `NodeGroup` and `LinkGroup` parameters to `sankeyNetwork` so links 14 | can be colourised. Thanks to Edwin de Jonge. 15 | 16 | - `fontFamily` is now applied to legends in `forceNetwork`. Thanks to Casey 17 | Shannon. 18 | 19 | - Improved bounding behaviour with `forceNetwork`. Now bounds both the links and 20 | nodes. Thanks to Koba Khitalishvili. 21 | 22 | ## Version 0.2.7 23 | 24 | - Fixed an issue with sankey viewBox sizing. 25 | 26 | ## Version 0.2.6 27 | 28 | - `sankeyNetwork` fully supports cycles 29 | 30 | - `sankeyNetwork` gets same responsive sizing and better fitting introduced 31 | in 0.2.4 for `diagonalNetwork` and `radialNetwork`. 32 | 33 | - `sankeyNetwork` gets same full margin control introduced 34 | in 0.2.4 for `diagonalNetwork` and `radialNetwork`. 35 | 36 | ## Version 0.2.5 37 | 38 | - Added `chordDiagram` to show directed relationships among entities. 39 | 40 | ## Version 0.2.4 41 | 42 | - More robust margin argument for `diagonalNetwork` and `radialNetwork` allows 43 | for a single value or specification of `top`, `right`, `bottom`, and `left` by 44 | named `vector` or `list`. 45 | 46 | - Responsive sizing using the `viewBox` attribute of `svg` for `diagonalNetwork` 47 | and `radialNetwork` should allow for fitting in the container with no fiddling. 48 | 49 | ## Version 0.2.3 50 | 51 | - Internal improvements to reduce dependencies: no longer depends on RCurl, 52 | plyr, and rjson. 53 | 54 | - Updated examples: 55 | 56 | + Using jsonlite makes JSONtoDF obsolete with the `fromJSON` function. 57 | 58 | + All Github data links now use the CDN link from 59 | [rawgit](https://rawgit.com/), so the examples should be more inline with 60 | Github raw policies. 61 | 62 | ## Version 0.2.2 63 | 64 | - `sankeyNetwork` changes: 65 | 66 | + Removes forced font family in favor of inherited from css or specified 67 | through the `fontFamily` argument. 68 | 69 | + Fixes issue with backslashes in the label for links. 70 | 71 | + Puts unicode right arrow in the tooltip. 72 | Adds argument to specify units for the tooltip label. 73 | 74 | + Handles cycles by updating this forked repo: . 75 | 76 | + Makes assumptions that `Source` is column 1 and `Target` is column 2 if 77 | not provided. 78 | 79 | ## Version 0.2.1 80 | 81 | - Fixed an issue with `forceNetwork` on Firefox. Thanks to @agoldst. 82 | 83 | - Fixed an issue where `forceNetwork` node size would not return to 84 | default after mouseover. Thanks to Pascal Pernot. 85 | 86 | ## Version 0.2 87 | 88 | - Added `dendroNetwork` to create hierarchical cluster network diagrams 89 | (dendrograms). 90 | 91 | - `treeNetwork` is DEPRECATED. 92 | 93 | - Placed functionality from `treeNetwork` to `radialNetwork`. The new 94 | `radialNetwork` function has the same functionality, but has been renamed to be 95 | more accurately descriptive and avoid confusion with `dendroNetwork`. 96 | 97 | - Added `diagonalNetwork`, which creates tree network diagrams using diagonal 98 | instead of radial nodes. 99 | 100 | Thanks to Jonathan Owen. 101 | 102 | ## Version 0.1.8 103 | 104 | - Added `clickAction` argument to `forceNetwork` to allow the user to 105 | pass a JavaScript expression through to be activated on click of a node. 106 | Thanks to Peter Ellis. 107 | 108 | - Added `bounded` argument to `forceNetwork` to allow the user to create a 109 | bounding box for the plot. See http://bl.ocks.org/mbostock/1129492. 110 | Thanks to Charles Sese. 111 | 112 | - Added `fontFamily` argument to `forceNetwork`, `simpleNetwork`, 113 | `sankeyNetwork`, and `treeNetwork`. Thanks to Peter Ellis. 114 | 115 | - Added `opacityNoHover` argument to `forceNetwork` to set the opacity of node 116 | labels when nodes are not hovered over. Thanks to Peter Ellis. 117 | 118 | ## Version 0.1.7 119 | 120 | - Include `JS` from htmlwidgets, to make it easier for users to pass arbitrary 121 | JS to more arguments. 122 | 123 | - Other internal code and documentation improvements. 124 | 125 | ## Version 0.1.6 126 | 127 | - `fontSize` used for all functions rather than `fontsize`. Thank you to 128 | @Hunter-Github for spotting this inconsistency. 129 | 130 | ## Version 0.1.5 131 | 132 | - Minor improvement to treeNetwork documentation. 133 | Thanks to Steven Beaupré and MrFlick. 134 | 135 | ## Version 0.1.4 136 | 137 | - `forceNetwork` gains three new arguments. 138 | 139 | + `legend` allows you to add a node colour legend. 140 | 141 | + `radiusCalculation` and `Nodesize` allow you to vary node radius by 142 | some values. 143 | 144 | Thank you to Charles Sese for these additions. 145 | 146 | ## Version 0.1.3 147 | 148 | - `zoom` argument added to `simpleNetwork` and `forceNetwork` to allow zooming. 149 | Thanks to @timelyportfolio. 150 | 151 | - Updated `treeNetwork` URL. 152 | 153 | ## Version 0.1.2.2 154 | 155 | - Update link in `forceNetwork` example. 156 | 157 | ## Version 0.1.2.1 158 | 159 | Enhanced flexibility when using data frames manipulated with dplyr/data.table. 160 | Thanks to Kevin Kuo. 161 | 162 | ## Version 0.1.2 163 | 164 | - Minor internal correction to d3.js version number. 165 | 166 | ## Version 0.1.1 167 | 168 | - `treeNetwork` added allowing the user to create tree networks. Thanks to 169 | B.W. Lewis. 170 | 171 | - Upgrade to d3.js version 3.5.2. 172 | 173 | ## Version 0.1 174 | 175 | - `sankeyNetwork` added. 176 | 177 | - `colourScale` argument added to `forceNetwork` and `sankeyNetwork` to allow 178 | the user to change the node colour scale. 179 | -------------------------------------------------------------------------------- /inst/htmlwidgets/simpleNetwork.js: -------------------------------------------------------------------------------- 1 | HTMLWidgets.widget({ 2 | 3 | name: "simpleNetwork", 4 | 5 | type: "output", 6 | 7 | initialize: function(el, width, height) { 8 | 9 | d3.select(el).append("svg") 10 | .attr("width", width) 11 | .attr("height", height); 12 | 13 | return d3.layout.force(); 14 | }, 15 | 16 | resize: function(el, width, height, force) { 17 | 18 | d3.select(el).select("svg") 19 | .attr("width", width) 20 | .attr("height", height); 21 | 22 | force.size([width, height]).resume(); 23 | }, 24 | 25 | renderValue: function(el, x, force) { 26 | 27 | // convert links data frame to d3 friendly format 28 | var links = HTMLWidgets.dataframeToD3(x.links); 29 | 30 | // compute the nodes from the links 31 | var nodes = {}; 32 | links.forEach(function(link) { 33 | link.source = nodes[link.source] || (nodes[link.source] = {name: link.source}); 34 | link.target = nodes[link.target] || (nodes[link.target] = {name: link.target}); 35 | link.value = +link.value; 36 | }); 37 | 38 | // get the width and height 39 | var width = el.offsetWidth; 40 | var height = el.offsetHeight; 41 | 42 | // set this up even if zoom = F 43 | var zoom = d3.behavior.zoom(); 44 | 45 | // create d3 force layout 46 | force 47 | .nodes(d3.values(nodes)) 48 | .links(links) 49 | .size([width, height]) 50 | .linkDistance(x.options.linkDistance) 51 | .charge(x.options.charge) 52 | .on("tick", tick) 53 | .start(); 54 | 55 | // thanks http://plnkr.co/edit/cxLlvIlmo1Y6vJyPs6N9?p=preview 56 | // http://stackoverflow.com/questions/22924253/adding-pan-zoom-to-d3js-force-directed 57 | var drag = force.drag() 58 | .on("dragstart", dragstart) 59 | // allow force drag to work with pan/zoom drag 60 | function dragstart(d) { 61 | d3.event.sourceEvent.preventDefault(); 62 | d3.event.sourceEvent.stopPropagation(); 63 | } 64 | 65 | // select the svg element and remove existing children 66 | var svg = d3.select(el).select("svg"); 67 | svg.selectAll("*").remove(); 68 | // add two g layers; the first will be zoom target if zoom = T 69 | // fine to have two g layers even if zoom = F 70 | svg = svg 71 | .append("g").attr("class","zoom-layer") 72 | .append("g") 73 | 74 | // add zooming if requested 75 | if (x.options.zoom) { 76 | zoom.on("zoom", redraw) 77 | function redraw() { 78 | d3.select(el).select(".zoom-layer").attr("transform", 79 | "translate(" + d3.event.translate + ")"+ 80 | " scale(" + d3.event.scale + ")"); 81 | } 82 | 83 | d3.select(el).select("svg") 84 | .attr("pointer-events", "all") 85 | .call(zoom); 86 | 87 | } else { 88 | zoom.on("zoom", null); 89 | } 90 | 91 | // draw links 92 | var link = svg.selectAll(".link") 93 | .data(force.links()) 94 | .enter().append("line") 95 | .style("stroke", x.options.linkColour) 96 | .style("opacity", x.options.opacity) 97 | .style("stroke-width", "1.5px"); 98 | 99 | // draw nodes 100 | var node = svg.selectAll(".node") 101 | .data(force.nodes()) 102 | .enter().append("g") 103 | .on("mouseover", mouseover) 104 | .on("mouseout", mouseout) 105 | .on("click", click) 106 | .on("dblclick", dblclick) 107 | .call(force.drag); 108 | 109 | // node circles 110 | node.append("circle") 111 | .attr("r", 8) 112 | .style("fill", x.options.nodeColour) 113 | .style("stroke", "#fff") 114 | .style("opacity", x.options.opacity) 115 | .style("stroke-width", "1.5px"); 116 | 117 | // node text 118 | node.append("text") 119 | .attr("x", 12) 120 | .attr("dy", ".35em") 121 | .style("font", x.options.fontSize + "px " + x.options.fontFamily) 122 | .style("fill", x.options.textColour) 123 | .style("opacity", x.options.opacity) 124 | .style("pointer-events", "none") 125 | .text(function(d) { return d.name; }); 126 | 127 | // tick event handler 128 | function tick() { 129 | link 130 | .attr("x1", function(d) { return d.source.x; }) 131 | .attr("y1", function(d) { return d.source.y; }) 132 | .attr("x2", function(d) { return d.target.x; }) 133 | .attr("y2", function(d) { return d.target.y; }); 134 | node.attr("transform", function(d) { 135 | return "translate(" + d.x + "," + d.y + ")"; 136 | }); 137 | } 138 | 139 | // mouseover event handler 140 | function mouseover() { 141 | d3.select(this).select("circle").transition() 142 | .duration(750) 143 | .attr("r", 16); 144 | } 145 | 146 | // mouseout event handler 147 | function mouseout() { 148 | d3.select(this).select("circle").transition() 149 | .duration(750) 150 | .attr("r", 8); 151 | } 152 | 153 | // click event handler 154 | function click() { 155 | d3.select(this).select("text").transition() 156 | .duration(750) 157 | .attr("x", 22) 158 | .style("stroke-width", ".5px") 159 | .style("opacity", 1) 160 | .style("fill", x.options.nodeClickColour) 161 | .style("font", x.options.clickTextSize + "px " + x.options.fontFamily); 162 | d3.select(this).select("circle").transition() 163 | .duration(750) 164 | .style("fill", x.options.nodeClickColour) 165 | .attr("r", 16) 166 | } 167 | 168 | // double-click event handler 169 | function dblclick() { 170 | d3.select(this).select("circle").transition() 171 | .duration(750) 172 | .attr("r", 6) 173 | .style("fill", x.options.nodeClickColour); 174 | d3.select(this).select("text").transition() 175 | .duration(750) 176 | .attr("x", 12) 177 | .style("stroke", "none") 178 | .style("fill", x.options.nodeClickColour) 179 | .style("stroke", "none") 180 | .style("opacity", x.options.opacity) 181 | .style("font", x.options.fontSize + "px " + x.options.fontFamily); 182 | } 183 | }, 184 | }); 185 | -------------------------------------------------------------------------------- /R/chordNetwork.R: -------------------------------------------------------------------------------- 1 | #' Create Reingold-Tilford Tree network diagrams. 2 | #' 3 | #' @param Data A square matrix or data frame whose (n, m) entry represents 4 | #' the strength of the link from group n to group m 5 | #' @param height height for the network graph's frame area in pixels (if 6 | #' \code{NULL} then height is automatically determined based on context) 7 | #' @param width numeric width for the network graph's frame area in pixels (if 8 | #' \code{NULL} then width is automatically determined based on context) 9 | #' @param initialOpacity specify the opacity before the user mouses over 10 | #' the link 11 | #' @param colourScale specify the hexadecimal colours in which to display 12 | #' the different categories. If there are fewer colours than categories, 13 | #' the last colour is repeated as necessary (if \code{NULL} then defaults 14 | #' to D3 colour scale) 15 | #' @param padding specify the amount of space between adjacent categories 16 | #' on the outside of the graph 17 | #' @param fontSize numeric font size in pixels for the node text labels. 18 | #' @param fontFamily font family for the node text labels. 19 | #' @param labels vector containing labels of the categories 20 | #' @param useTicks integer number of ticks on the radial axis. 21 | #' The default is `0` which means no ticks will be drawn. 22 | #' @param labelDistance integer distance in pixels (px) between 23 | #' text labels and outer radius. The default is `30`. 24 | #' 25 | #' 26 | #' @examples 27 | #' \dontrun{ 28 | #' #### Data about hair colour preferences, from 29 | #' ## https://github.com/mbostock/d3/wiki/Chord-Layout 30 | #' 31 | #' hairColourData <- matrix(c(11975, 1951, 8010, 1013, 32 | #' 5871, 10048, 16145, 990, 33 | #' 8916, 2060, 8090, 940, 34 | #' 2868, 6171, 8045, 6907), 35 | #' nrow = 4) 36 | #' 37 | #' chordNetwork(Data = hairColourData, 38 | #' width = 500, 39 | #' height = 500, 40 | #' colourScale = c("#000000", 41 | #' "#FFDD89", 42 | #' "#957244", 43 | #' "#F26223"), 44 | #' labels = c("red", "brown", "blond", "gray")) 45 | #' 46 | #' } 47 | #' 48 | #' @source 49 | #' 50 | #' Mike Bostock: \url{https://github.com/mbostock/d3/wiki/Chord-Layout}. 51 | #' 52 | #' @export 53 | #' 54 | chordNetwork <- function(Data, 55 | height = 500, 56 | width = 500, 57 | initialOpacity = 0.8, 58 | useTicks = 0, 59 | colourScale = c("#1f77b4", 60 | "#aec7e8", 61 | "#ff7f0e", 62 | "#ffbb78", 63 | "#2ca02c", 64 | "#98df8a", 65 | "#d62728", 66 | "#ff9896", 67 | "#9467bd", 68 | "#c5b0d5", 69 | "#8c564b", 70 | "#c49c94", 71 | "#e377c2", 72 | "#f7b6d2", 73 | "#7f7f7f", 74 | "#c7c7c7", 75 | "#bcbd22", 76 | "#dbdb8d", 77 | "#17becf", 78 | "#9edae5"), 79 | padding = 0.1, 80 | fontSize = 14, 81 | fontFamily = "sans-serif", 82 | labels = c(), 83 | labelDistance = 30) 84 | 85 | { 86 | options <- list( 87 | width = width, 88 | height = height, 89 | use_ticks = useTicks, 90 | initial_opacity = initialOpacity, 91 | colour_scale = colourScale, 92 | padding = padding, 93 | font_size = fontSize, 94 | font_family = fontFamily, 95 | labels = labels, 96 | label_distance = labelDistance 97 | ) 98 | 99 | if (!is.matrix(Data) && !is.data.frame(Data)) 100 | { 101 | stop("Data must be of type matrix or data frame") 102 | } 103 | 104 | if (nrow(Data) != ncol(Data)) 105 | { 106 | stop(paste("Data must have the same number of rows and columns; given ", 107 | nrow(Data), 108 | "rows and", 109 | ncol(Data), 110 | "columns", 111 | sep = " ")) 112 | } 113 | 114 | if(length(labels)!=0 && length(labels)!=ncol(Data)){ 115 | stop(paste("Length of labels vector should be the same as the number of rows")) 116 | } 117 | 118 | if (is.data.frame(Data)) 119 | { 120 | Data = data.matrix(Data) 121 | } 122 | 123 | # create widget 124 | htmlwidgets::createWidget( 125 | name = "chordNetwork", 126 | x = list(matrix = Data, options = options), 127 | width = width, 128 | height = height, 129 | htmlwidgets::sizingPolicy(viewer.suppress = TRUE, 130 | browser.fill = TRUE, 131 | browser.padding = 75, 132 | knitr.figure = FALSE, 133 | knitr.defaultWidth = 500, 134 | knitr.defaultHeight = 500), 135 | package = "networkD3") 136 | } 137 | 138 | #' @rdname networkD3-shiny 139 | #' @export 140 | chordNetworkOutput <- function(outputId, width = "100%", height = "500px") { 141 | shinyWidgetOutput(outputId, "chordNetwork", width, height, 142 | package = "networkD3") 143 | } 144 | 145 | #' @rdname networkD3-shiny 146 | #' @export 147 | renderchordNetwork <- function(expr, env = parent.frame(), quoted = FALSE) { 148 | if (!quoted) { expr <- substitute(expr) } # force quoted 149 | shinyRenderWidget(expr, chordNetworkOutput, env, quoted = TRUE) 150 | } 151 | 152 | -------------------------------------------------------------------------------- /inst/examples/examples.R: -------------------------------------------------------------------------------- 1 | 2 | library(networkD3) 3 | 4 | # simpleNetwork 5 | src <- c("A", "A", "A", "A", "B", "B", "C", "C", "D") 6 | target <- c("B", "C", "D", "J", "E", "F", "G", "H", "I") 7 | networkData <- data.frame(src, target) 8 | simpleNetwork(networkData) 9 | 10 | # with sans-serif 11 | simpleNetwork(networkData, fontFamily = "sans-serif") 12 | 13 | # with another font 14 | simpleNetwork(networkData, fontFamily = "fantasy") 15 | 16 | # forceNetwork 17 | data(MisLinks) 18 | data(MisNodes) 19 | 20 | # Create graph 21 | forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source", 22 | Target = "target", Value = "value", NodeID = "name", 23 | Group = "group", opacity = 1, zoom = F, bounded = T) 24 | 25 | # with a simple click action - make the circles bigger when clicked 26 | MyClickScript <- 27 | ' d3.select(this).select("circle").transition() 28 | .duration(750) 29 | .attr("r", 30)' 30 | 31 | forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source", 32 | Target = "target", Value = "value", NodeID = "name", 33 | Group = "group", opacity = 1, zoom = F, bounded = T, 34 | clickAction = MyClickScript) 35 | 36 | # showing how you can re-use the name of the clicked-on node (which is 'd') 37 | # You are unlikely to want to do this pop-up alert, but you might want 38 | # instead to use Shiny.onInputChange() to allocate d.XXX to an element 39 | # input$XXX for user in a Shiny app. 40 | MyClickScript <- 'alert("You clicked " + d.name + " which is in row " + (d.index + 1) + 41 | " of your original R data frame");' 42 | forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source", 43 | Target = "target", Value = "value", NodeID = "name", 44 | Group = "group", opacity = 1, zoom = F, bounded = T, 45 | clickAction = MyClickScript) 46 | 47 | forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source", 48 | Target = "target", Value = "value", NodeID = "name", 49 | Group = "group", opacity = 1, zoom = F, bounded = T, 50 | clickAction = "alert('Ouch!')") 51 | 52 | # With a different font, and dimensions chosen to illustrate bounded box 53 | forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source", 54 | Target = "target", Value = "value", NodeID = "name", 55 | Group = "group", opacity = 1, zoom = F, bounded = T, 56 | fontFamily = "cursive", 57 | width = 1500, height = 300) 58 | 59 | # With a different font, and node text faintly visible when not hovered over 60 | forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source", 61 | Target = "target", Value = "value", NodeID = "name", 62 | Group = "group", opacity = 1, zoom = F, bounded = T, 63 | fontFamily = "cursive", opacityNoHover = 0.3) 64 | 65 | # Create graph with legend and varying radius 66 | forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source", 67 | Target = "target", Value = "value", NodeID = "name", 68 | Nodesize = 'size', radiusCalculation = "d.nodesize", 69 | Group = "group", opacity = 1, legend = T, bounded = F) 70 | 71 | # Create graph with legend and varying radius and a bounded box 72 | forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source", 73 | Target = "target", Value = "value", NodeID = "name", 74 | Nodesize = 'size', radiusCalculation = " Math.sqrt(d.nodesize)+6", 75 | Group = "group", opacity = 1, legend = T, bounded = T) 76 | 77 | 78 | # sankeyNetwork 79 | URL <- "https://cdn.rawgit.com/christophergandrud/networkD3/master/JSONdata/energy.json" 80 | Energy <- jsonlite::fromJSON(URL) 81 | 82 | # Plot 83 | sankeyNetwork(Links = Energy$links, Nodes = Energy$nodes, Source = "source", 84 | Target = "target", Value = "value", NodeID = "name", 85 | fontSize = 12, nodeWidth = 30) 86 | 87 | # And with a different font 88 | sankeyNetwork(Links = Energy$links, Nodes = Energy$nodes, Source = "source", 89 | Target = "target", Value = "value", NodeID = "name", 90 | fontSize = 12, nodeWidth = 30, fontFamily = "monospace") 91 | 92 | # as of 0.2.6 sankeyNetwork supports cycles 93 | # simple network with cycle 5 -> 0 94 | net_cycles <- list( 95 | links = data.frame( 96 | source = c(0,0,0,1,1,5), 97 | target = c(1,2,3,4,5,0), 98 | value = 10 99 | ), 100 | nodes = data.frame( 101 | name = letters[1:6] 102 | ) 103 | ) 104 | 105 | # notice how few arguments we need now 106 | # some output but not the nice output I expect 107 | sankeyNetwork( 108 | net_cycles$links, 109 | net_cycles$nodes, 110 | Value = "value" 111 | ) 112 | 113 | 114 | # radialNetwork 115 | Flare <- jsonlite::fromJSON( 116 | "https://gist.githubusercontent.com/mbostock/4063550/raw/a05a94858375bd0ae023f6950a2b13fac5127637/flare.json", 117 | simplifyDataFrame = FALSE 118 | ) 119 | 120 | hc <- hclust(dist(USArrests), "ave") 121 | 122 | radialNetwork(List = Flare, fontSize = 10, opacity = 0.9, margin=0) 123 | radialNetwork(as.radialNetwork(hc)) 124 | 125 | # and with a different font 126 | radialNetwork(List = Flare, fontSize = 10, opacity = 0.9, margin=0, fontFamily = "sans-serif") 127 | 128 | diagonalNetwork(List = Flare, fontSize = 10, opacity = 0.9, margin=0) 129 | diagonalNetwork(as.radialNetwork(hc), height = 700, margin = 50) 130 | 131 | 132 | # dendroNetwork 133 | hc <- hclust(dist(USArrests), "ave") 134 | 135 | dendroNetwork(hc, height = 600) 136 | dendroNetwork(hc, treeOrientation = "vertical") 137 | 138 | dendroNetwork(hc, height = 600, linkType = "diagonal") 139 | dendroNetwork(hc, treeOrientation = "vertical", linkType = "diagonal") 140 | 141 | dendroNetwork(hc, textColour = c("red", "green", "orange")[cutree(hc, 3)], 142 | height = 600) 143 | dendroNetwork(hc, textColour = c("red", "green", "orange")[cutree(hc, 3)], 144 | treeOrientation = "vertical") 145 | 146 | # chordDiagram 147 | hairColourData <- matrix(c(11975, 1951, 8010, 1013, 148 | 5871, 10048, 16145, 990, 149 | 8916, 2060, 8090, 940, 150 | 2868, 6171, 8045, 6907), nrow = 4) 151 | 152 | chordNetwork(data = hairColourData, 153 | width = 500, 154 | height = 500, 155 | colourScale = c("#000000", "#FFDD89", "#957244", "#F26223")) 156 | -------------------------------------------------------------------------------- /inst/htmlwidgets/diagonalNetwork.js: -------------------------------------------------------------------------------- 1 | HTMLWidgets.widget({ 2 | 3 | name: "diagonalNetwork", 4 | type: "output", 5 | 6 | initialize: function(el, width, height) { 7 | 8 | d3.select(el).append("svg") 9 | .style("width", "100%") 10 | .style("height", "100%") 11 | .append("g") 12 | .attr("transform", "translate(40,0)"); 13 | return d3.layout.tree(); 14 | 15 | }, 16 | 17 | resize: function(el, width, height, tree) { 18 | // resize now handled by svg viewBox attribute 19 | /* 20 | var s = d3.select(el).selectAll("svg"); 21 | s.attr("width", width).attr("height", height); 22 | 23 | var margin = {top: 20, right: 20, bottom: 20, left: 20}; 24 | width = width - margin.right - margin.left; 25 | height = height - margin.top - margin.bottom; 26 | 27 | tree.size([height, width]); 28 | var svg = d3.select(el).selectAll("svg").select("g") 29 | .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 30 | */ 31 | 32 | }, 33 | 34 | renderValue: function(el, x, tree) { 35 | // x is a list with two elements, options and root; root must already be a 36 | // JSON array with the d3Tree root data 37 | 38 | var s = d3.select(el).selectAll("svg"); 39 | 40 | // margin handling 41 | // set our default margin to be 20 42 | // will override with x.options.margin if provided 43 | var margin = {top: 20, right: 20, bottom: 20, left: 20}; 44 | // go through each key of x.options.margin 45 | // use this value if provided from the R side 46 | Object.keys(x.options.margin).map(function(ky){ 47 | if(x.options.margin[ky] !== null) { 48 | margin[ky] = x.options.margin[ky]; 49 | } 50 | // set the margin on the svg with css style 51 | // commenting this out since not correct 52 | // s.style(["margin",ky].join("-"), margin[ky]); 53 | }); 54 | 55 | 56 | width = s.node().getBoundingClientRect().width - margin.right - margin.left; 57 | height = s.node().getBoundingClientRect().height - margin.top - margin.bottom; 58 | 59 | tree.size([height, width]) 60 | .separation(function(a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth; }); 61 | 62 | // select the svg group element and remove existing children 63 | s.attr("pointer-events", "all").selectAll("*").remove(); 64 | s.append("g") 65 | .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 66 | 67 | var svg = d3.select(el).selectAll("g"); 68 | 69 | var root = x.root; 70 | var nodes = tree.nodes(root), 71 | links = tree.links(nodes); 72 | 73 | var diagonal = d3.svg.diagonal() 74 | .projection(function(d) { return [d.y, d.x]; }); 75 | 76 | // draw links 77 | var link = svg.selectAll(".link") 78 | .data(links) 79 | .enter().append("path") 80 | .style("fill", "none") 81 | .style("stroke", "#ccc") 82 | .style("opacity", "0.55") 83 | .style("stroke-width", "1.5px") 84 | .attr("d", diagonal); 85 | 86 | // draw nodes 87 | var node = svg.selectAll(".node") 88 | .data(nodes) 89 | .enter().append("g") 90 | .attr("class", "node") 91 | .attr("transform", function(d) { 92 | return "translate(" + d.y + "," + d.x + ")"; 93 | }) 94 | .on("mouseover", mouseover) 95 | .on("mouseout", mouseout); 96 | 97 | // node circles 98 | node.append("circle") 99 | .attr("r", 4.5) 100 | .style("fill", x.options.nodeColour) 101 | .style("opacity", x.options.opacity) 102 | .style("stroke", x.options.nodeStroke) 103 | .style("stroke-width", "1.5px"); 104 | 105 | // node text 106 | node.append("text") 107 | .attr("dx", function(d) { return d.children ? -8 : 8; }) 108 | .attr("dy", ".31em") 109 | .attr("text-anchor", function(d) { 110 | return d.children || d._children ? "end" : "start"; 111 | }) 112 | .style("font", x.options.fontSize + "px " + x.options.fontFamily) 113 | .style("opacity", x.options.opacity) 114 | .style("fill", x.options.textColour) 115 | .text(function(d) { return d.name; }); 116 | 117 | // adjust viewBox to fit the bounds of our tree 118 | s.attr( 119 | "viewBox", 120 | [ 121 | d3.min( 122 | s.selectAll('.node text')[0].map(function(d){ 123 | return d.getBoundingClientRect().left 124 | }) 125 | ) - s.node().getBoundingClientRect().left - margin.right, 126 | d3.min( 127 | s.selectAll('.node text')[0].map(function(d){ 128 | return d.getBoundingClientRect().top 129 | }) 130 | ) - s.node().getBoundingClientRect().top - margin.top, 131 | d3.max( 132 | s.selectAll('.node text')[0].map(function(d){ 133 | return d.getBoundingClientRect().right 134 | }) 135 | ) - 136 | d3.min( 137 | s.selectAll('.node text')[0].map(function(d){ 138 | return d.getBoundingClientRect().left 139 | }) 140 | ) + margin.left + margin.right, 141 | d3.max( 142 | s.selectAll('.node text')[0].map(function(d){ 143 | return d.getBoundingClientRect().bottom 144 | }) 145 | ) - 146 | d3.min( 147 | s.selectAll('.node text')[0].map(function(d){ 148 | return d.getBoundingClientRect().top 149 | }) 150 | ) + margin.top + margin.bottom 151 | ].join(",") 152 | ); 153 | 154 | // mouseover event handler 155 | function mouseover() { 156 | d3.select(this).select("circle").transition() 157 | .duration(750) 158 | .attr("r", 9); 159 | 160 | d3.select(this).select("text").transition() 161 | .duration(750) 162 | .style("stroke-width", ".5px") 163 | .style("font", "25px " + x.options.fontFamily) 164 | .style("opacity", 1); 165 | } 166 | 167 | // mouseout event handler 168 | function mouseout() { 169 | d3.select(this).select("circle").transition() 170 | .duration(750) 171 | .attr("r", 4.5); 172 | 173 | d3.select(this).select("text").transition() 174 | .duration(750) 175 | .style("font", x.options.fontSize + "px " + x.options.fontFamily) 176 | .style("opacity", x.options.opacity); 177 | } 178 | 179 | }, 180 | }); 181 | -------------------------------------------------------------------------------- /R/dendroNetwork.R: -------------------------------------------------------------------------------- 1 | #' Create hierarchical cluster network diagrams. 2 | #' 3 | #' @param hc a hierarchical (\code{hclust}) cluster object. 4 | #' @param height height for the network graph's frame area in pixels 5 | #' @param width numeric width for the network graph's frame area in pixels 6 | #' @param fontSize numeric font size in pixels for the node text labels. 7 | #' @param linkColour character string specifying the colour you want the link 8 | #' lines to be. Multiple formats supported (e.g. hexadecimal). 9 | #' @param nodeColour character string specifying the colour you want the node 10 | #' circles to be. Multiple formats supported (e.g. hexadecimal). 11 | #' @param nodeStroke character string specifying the colour you want the node 12 | #' perimeter to be. Multiple formats supported (e.g. hexadecimal). 13 | #' @param textColour character vector or scalar specifying the colour you want 14 | #' the text to be before they are clicked. Order should match the order of 15 | #' \code{hclust$labels}. Multiple formats supported (e.g. hexadecimal). 16 | #' @param textOpacity numeric vector or scalar of the proportion opaque you 17 | #' would like the text to be before they are clicked. rder should match the 18 | #' order of \code{hclust$labels}. 19 | #' @param textRotate numeric degress to rotate text for node text. Default 20 | #' is 0 for horizontal and 65 degrees for vertical. 21 | #' @param opacity numeric value of the proportion opaque you would like the 22 | #' graph elements to be. 23 | #' @param margins numeric value or named list of plot margins 24 | #' (top, right, bottom, left). Set the margin appropriately to accomodate 25 | #' long text labels. 26 | #' @param linkType character specifying the link type between points. Options 27 | #' are 'elbow' and 'diagonal'. 28 | #' @param treeOrientation character specifying the tree orientation, Options 29 | #' are 'vertical' and 'horizontal'. 30 | #' @param zoom logical enabling plot zoom and pan 31 | #' 32 | #' 33 | #' @examples 34 | #' \dontrun{ 35 | #' hc <- hclust(dist(USArrests), "ave") 36 | #' 37 | #' dendroNetwork(hc, height = 600) 38 | #' dendroNetwork(hc, treeOrientation = "vertical") 39 | #' 40 | #' dendroNetwork(hc, height = 600, linkType = "diagonal") 41 | #' dendroNetwork(hc, treeOrientation = "vertical", linkType = "diagonal") 42 | #' 43 | #' dendroNetwork(hc, textColour = c("red", "green", "orange")[cutree(hc, 3)], 44 | #' height = 600) 45 | #' dendroNetwork(hc, textColour = c("red", "green", "orange")[cutree(hc, 3)], 46 | #' treeOrientation = "vertical") 47 | #' } 48 | #' 49 | #' @source Mike Bostock: \url{http://bl.ocks.org/mbostock/4063570}. 50 | #' 51 | #' Fabio Nelli: \url{http://www.meccanismocomplesso.org/en/dendrogramma-d3-parte1/} 52 | #' 53 | #' @importFrom stats setNames 54 | #' @importFrom utils modifyList 55 | #' @export 56 | #' 57 | dendroNetwork <- function( 58 | hc, 59 | height = 500, 60 | width = 800, 61 | fontSize = 10, 62 | linkColour = "#ccc", 63 | nodeColour = "#fff", 64 | nodeStroke = "steelblue", 65 | textColour = "#111", 66 | textOpacity = 0.9, 67 | textRotate = NULL, 68 | opacity = 0.9, 69 | margins = NULL, 70 | linkType = c("elbow", "diagonal"), 71 | treeOrientation = c("horizontal", "vertical"), 72 | zoom = FALSE) 73 | { 74 | # validate input 75 | if (length(textColour) == 1L) 76 | textColour = rep(textColour, length(hc$labels)) 77 | if (length(textOpacity) == 1L) 78 | textOpacity = rep(textOpacity, length(hc$labels)) 79 | 80 | linkType = match.arg(linkType[1], c("elbow", "diagonal")) 81 | treeOrientation = match.arg(treeOrientation[1], 82 | c("horizontal", "vertical")) 83 | 84 | root <- as.dendroNetwork(hc, textColour, textOpacity) 85 | 86 | if (treeOrientation == "vertical") 87 | margins_def = list(top = 40, right = 40, bottom = 150, left = 40) 88 | else 89 | margins_def = list(top = 40, right = 150, bottom = 40, left = 40) 90 | 91 | if (length(margins) == 1L && is.numeric(margins)) { 92 | margins = as.list(setNames(rep(margins, 4), 93 | c("top", "right", "bottom", "left"))) 94 | } else if (is.null(margins)) { 95 | margins = margins_def 96 | } else { 97 | margins = modifyList(margins_def, margins) 98 | } 99 | 100 | if (is.null(textRotate)) 101 | textRotate = ifelse(treeOrientation == "vertical", 65, 0) 102 | 103 | # create options 104 | options = list( 105 | height = height, 106 | width = width, 107 | fontSize = fontSize, 108 | linkColour = linkColour, 109 | nodeColour = nodeColour, 110 | nodeStroke = nodeStroke, 111 | textRotate = textRotate, 112 | margins = margins, 113 | opacity = opacity, 114 | linkType = linkType, 115 | treeOrientation = treeOrientation, 116 | zoom = zoom 117 | ) 118 | 119 | # create widget 120 | htmlwidgets::createWidget( 121 | name = "dendroNetwork", 122 | x = list(root = root, options = options), 123 | width = width, 124 | height = height, 125 | htmlwidgets::sizingPolicy(padding = 10, browser.fill = TRUE), 126 | package = "networkD3") 127 | } 128 | 129 | #' @rdname networkD3-shiny 130 | #' @export 131 | dendroNetworkOutput <- function(outputId, width = "100%", height = "800px") { 132 | shinyWidgetOutput(outputId, "dendroNetwork", width, height, 133 | package = "networkD3") 134 | } 135 | 136 | #' @rdname networkD3-shiny 137 | #' @export 138 | renderDendroNetwork <- function(expr, env = parent.frame(), quoted = FALSE) { 139 | if (!quoted) { expr <- substitute(expr) } # force quoted 140 | shinyRenderWidget(expr, dendroNetworkOutput, env, quoted = TRUE) 141 | } 142 | 143 | as.dendroNetwork <- function(hc, textColour, textOpacity) 144 | { 145 | if (!("hclust" %in% class(hc))) 146 | stop("hc must be a object of class hclust") 147 | 148 | if (length(textColour) != length(hc$labels)) 149 | stop("textColour length must match label length") 150 | if (length(textOpacity) != length(hc$labels)) 151 | stop("textOpacity length must match label length") 152 | 153 | ul <- function(lev) 154 | { 155 | child = lapply(1:2, function(i) { 156 | val <- abs(hc$merge[lev, ][i]) 157 | if (hc$merge[lev, ][i] < 0) 158 | list(name = hc$labels[val], y = 0, textColour = textColour[val], 159 | textOpacity = textOpacity[val]) 160 | else 161 | ul(val) 162 | }) 163 | list(name = "", y = hc$height[lev], children = child) 164 | } 165 | ul(nrow(hc$merge)) 166 | } 167 | -------------------------------------------------------------------------------- /R/diagonalNetwork.R: -------------------------------------------------------------------------------- 1 | #' Create Reingold-Tilford Tree network diagrams. 2 | #' 3 | #' @param List a hierarchical list object with a root node and children. 4 | #' @param height height for the network graph's frame area in pixels (if 5 | #' \code{NULL} then height is automatically determined based on context) 6 | #' @param width numeric width for the network graph's frame area in pixels (if 7 | #' \code{NULL} then width is automatically determined based on context) 8 | #' @param fontSize numeric font size in pixels for the node text labels. 9 | #' @param fontFamily font family for the node text labels. 10 | #' @param linkColour character string specifying the colour you want the link 11 | #' lines to be. Multiple formats supported (e.g. hexadecimal). 12 | #' @param nodeColour character string specifying the colour you want the node 13 | #' circles to be. Multiple formats supported (e.g. hexadecimal). 14 | #' @param nodeStroke character string specifying the colour you want the node 15 | #' perimeter to be. Multiple formats supported (e.g. hexadecimal). 16 | #' @param textColour character string specifying the colour you want the text to 17 | #' be before they are clicked. Multiple formats supported (e.g. hexadecimal). 18 | #' @param opacity numeric value of the proportion opaque you would like the 19 | #' graph elements to be. 20 | #' @param margin an integer or a named \code{list}/\code{vector} of integers 21 | #' for the plot margins. If using a named \code{list}/\code{vector}, 22 | #' the positions \code{top}, \code{right}, \code{bottom}, \code{left} 23 | #' are valid. If a single integer is provided, then the value will be 24 | #' assigned to the right margin. Set the margin appropriately 25 | #' to accomodate long text labels. 26 | #' 27 | #' 28 | #' @examples 29 | #' \dontrun{ 30 | #' #### Create tree from JSON formatted data 31 | #' ## Download JSON data 32 | #' # Create URL. paste0 used purely to keep within line width. 33 | #' URL <- paste0("https://cdn.rawgit.com/christophergandrud/networkD3/", 34 | #' "master/JSONdata//flare.json") 35 | #' 36 | #' ## Convert to list format 37 | #' Flare <- jsonlite::fromJSON(URL, simplifyDataFrame = FALSE) 38 | #' 39 | #' ## Recreate Bostock example from http://bl.ocks.org/mbostock/4063550 40 | #' diagonalNetwork(List = Flare, fontSize = 10, opacity = 0.9) 41 | #' 42 | #' #### Create a tree dendrogram from an R hclust object 43 | #' hc <- hclust(dist(USArrests), "ave") 44 | #' diagonalNetwork(as.radialNetwork(hc)) 45 | #' diagonalNetwork(as.radialNetwork(hc), fontFamily = "cursive") 46 | #' 47 | #' #### Create tree from a hierarchical R list 48 | #' For an alternative structure see: http://stackoverflow.com/a/30747323/1705044 49 | #' CanadaPC <- list(name = "Canada", children = list(list(name = "Newfoundland", 50 | #' children = list(list(name = "St. John's"))), 51 | #' list(name = "PEI", 52 | #' children = list(list(name = "Charlottetown"))), 53 | #' list(name = "Nova Scotia", 54 | #' children = list(list(name = "Halifax"))), 55 | #' list(name = "New Brunswick", 56 | #' children = list(list(name = "Fredericton"))), 57 | #' list(name = "Quebec", 58 | #' children = list(list(name = "Montreal"), 59 | #' list(name = "Quebec City"))), 60 | #' list(name = "Ontario", 61 | #' children = list(list(name = "Toronto"), 62 | #' list(name = "Ottawa"))), 63 | #' list(name = "Manitoba", 64 | #' children = list(list(name = "Winnipeg"))), 65 | #' list(name = "Saskatchewan", 66 | #' children = list(list(name = "Regina"))), 67 | #' list(name = "Nunavuet", 68 | #' children = list(list(name = "Iqaluit"))), 69 | #' list(name = "NWT", 70 | #' children = list(list(name = "Yellowknife"))), 71 | #' list(name = "Alberta", 72 | #' children = list(list(name = "Edmonton"))), 73 | #' list(name = "British Columbia", 74 | #' children = list(list(name = "Victoria"), 75 | #' list(name = "Vancouver"))), 76 | #' list(name = "Yukon", 77 | #' children = list(list(name = "Whitehorse"))) 78 | #' )) 79 | #' 80 | #' diagonalNetwork(List = CanadaPC, fontSize = 10) 81 | #' } 82 | #' 83 | #' @source Reingold. E. M., and Tilford, J. S. (1981). Tidier Drawings of Trees. 84 | #' IEEE Transactions on Software Engineering, SE-7(2), 223-228. 85 | #' 86 | #' Mike Bostock: \url{http://bl.ocks.org/mbostock/4339083}. 87 | #' 88 | #' @export 89 | #' 90 | diagonalNetwork <- function( 91 | List, 92 | height = NULL, 93 | width = NULL, 94 | fontSize = 10, 95 | fontFamily = "serif", 96 | linkColour = "#ccc", 97 | nodeColour = "#fff", 98 | nodeStroke = "steelblue", 99 | textColour = "#111", 100 | opacity = 0.9, 101 | margin = NULL) 102 | { 103 | # validate input 104 | if (!is.list(List)) 105 | stop("List must be a list object.") 106 | root <- List 107 | 108 | margin <- margin_handler(margin) 109 | 110 | # create options 111 | options = list( 112 | height = height, 113 | width = width, 114 | fontSize = fontSize, 115 | fontFamily = fontFamily, 116 | linkColour = linkColour, 117 | nodeColour = nodeColour, 118 | nodeStroke = nodeStroke, 119 | textColour = textColour, 120 | margin = margin, 121 | opacity = opacity 122 | ) 123 | 124 | # create widget 125 | htmlwidgets::createWidget( 126 | name = "diagonalNetwork", 127 | x = list(root = root, options = options), 128 | width = width, 129 | height = height, 130 | htmlwidgets::sizingPolicy(padding = 10, browser.fill = TRUE), 131 | package = "networkD3") 132 | } 133 | 134 | #' @rdname networkD3-shiny 135 | #' @export 136 | diagonalNetworkOutput <- function(outputId, width = "100%", height = "800px") { 137 | shinyWidgetOutput(outputId, "diagonalNetwork", width, height, 138 | package = "networkD3") 139 | } 140 | 141 | #' @rdname networkD3-shiny 142 | #' @export 143 | renderDiagonalNetwork <- function(expr, env = parent.frame(), quoted = FALSE) { 144 | if (!quoted) { expr <- substitute(expr) } # force quoted 145 | shinyRenderWidget(expr, diagonalNetworkOutput, env, quoted = TRUE) 146 | } 147 | -------------------------------------------------------------------------------- /R/sankeyNetwork.R: -------------------------------------------------------------------------------- 1 | #' Create a D3 JavaScript Sankey diagram 2 | #' 3 | #' @param Links a data frame object with the links between the nodes. It should 4 | #' have include the \code{Source} and \code{Target} for each link. An optional 5 | #' \code{Value} variable can be included to specify how close the nodes are to 6 | #' one another. 7 | #' @param Nodes a data frame containing the node id and properties of the nodes. 8 | #' If no ID is specified then the nodes must be in the same order as the 9 | #' \code{Source} variable column in the \code{Links} data frame. Currently only 10 | #' grouping variable is allowed. 11 | #' @param Source character string naming the network source variable in the 12 | #' \code{Links} data frame. 13 | #' @param Target character string naming the network target variable in the 14 | #' \code{Links} data frame. 15 | #' @param Value character string naming the variable in the \code{Links} data 16 | #' frame for how far away the nodes are from one another. 17 | #' @param NodeID character string specifying the node IDs in the \code{Nodes}. 18 | #' data frame. Must be 0-indexed. 19 | #' @param NodeGroup character string specifying the node groups in the 20 | #' \code{Nodes}. Used to color the nodes in the network. 21 | #' @param LinkGroup character string specifying the groups in the 22 | #' \code{Links}. Used to color the links in the network. 23 | #' @param units character string describing physical units (if any) for Value 24 | #' @param colourScale character string specifying the categorical colour 25 | #' scale for the nodes. See 26 | #' \url{https://github.com/mbostock/d3/wiki/Ordinal-Scales}. 27 | #' @param fontSize numeric font size in pixels for the node text labels. 28 | #' @param fontFamily font family for the node text labels. 29 | #' @param nodeWidth numeric width of each node. 30 | #' @param nodePadding numeric essentially influences the width height. 31 | #' @param margin an integer or a named \code{list}/\code{vector} of integers 32 | #' for the plot margins. If using a named \code{list}/\code{vector}, 33 | #' the positions \code{top}, \code{right}, \code{bottom}, \code{left} 34 | #' are valid. If a single integer is provided, then the value will be 35 | #' assigned to the right margin. Set the margin appropriately 36 | #' to accomodate long text labels. 37 | #' @param height numeric height for the network graph's frame area in pixels. 38 | #' @param width numeric width for the network graph's frame area in pixels. 39 | #' @param iterations number iterations to perform in the node placement 40 | #' optimization. 41 | #' 42 | #' @examples 43 | #' \dontrun{ 44 | #' # Recreate Bostock Sankey diagram: http://bost.ocks.org/mike/sankey/ 45 | #' # Load energy projection data 46 | #' URL <- paste0('https://cdn.rawgit.com/christophergandrud/networkD3/', 47 | #' 'master/JSONdata/energy.json') 48 | #' energy <- jsonlite::fromJSON(URL) 49 | #' # Plot 50 | #' sankeyNetwork(Links = energy$links, Nodes = energy$nodes, Source = 'source', 51 | #' Target = 'target', Value = 'value', NodeID = 'name', 52 | #' units = 'TWh', fontSize = 12, nodeWidth = 30) 53 | #' 54 | #' # Colour links 55 | #' energy$links$energy_type <- sub(' .*', '', 56 | #' energy$nodes[energy$links$source + 1, 'name']) 57 | #' 58 | #' sankeyNetwork(Links = energy$links, Nodes = energy$nodes, Source = 'source', 59 | #' Target = 'target', Value = 'value', NodeID = 'name', 60 | #' LinkGroup = 'energy_type', NodeGroup = NULL) 61 | #' 62 | #' } 63 | #' @source 64 | #' D3.js was created by Michael Bostock. See \url{http://d3js.org/} and, more 65 | #' specifically for Sankey diagrams \url{http://bost.ocks.org/mike/sankey/}. 66 | #' 67 | #' @seealso \code{\link{JS}} 68 | #' 69 | #' @export 70 | 71 | sankeyNetwork <- function(Links, Nodes, Source, Target, Value, 72 | NodeID, NodeGroup = NodeID, LinkGroup = NULL, Label = NULL, 73 | TopLabel = NULL, units = "", colourScale = JS("d3.scale.category20()"), 74 | fontSize = 7, fontFamily = NULL, nodeWidth = 15, nodePadding = 10, 75 | margin = NULL, height = NULL, width = NULL, iterations = 32) { 76 | # Hack for UI consistency. Think of improving. 77 | colourScale <- as.character(colourScale) 78 | 79 | # Subset data frames for network graph 80 | if (!is.data.frame(Links)) { 81 | stop("Links must be a data frame class object.") 82 | } 83 | if (!is.data.frame(Nodes)) { 84 | stop("Nodes must be a data frame class object.") 85 | } 86 | # if Source or Target are missing assume Source is the first 87 | # column Target is the second column 88 | if (missing(Source)) 89 | Source = 1 90 | if (missing(Target)) 91 | Target = 2 92 | 93 | if (missing(Value)) { 94 | LinksDF <- data.frame(Links[, Source], Links[, Target]) 95 | names(LinksDF) <- c("source", "target") 96 | } else if (!missing(Value)) { 97 | LinksDF <- data.frame(Links[, Source], Links[, Target], 98 | Links[, Value]) 99 | names(LinksDF) <- c("source", "target", "value") 100 | } 101 | 102 | # if NodeID is missing assume NodeID is the first column 103 | if (missing(NodeID)) 104 | NodeID = 1 105 | NodesDF <- data.frame(Nodes[, NodeID]) 106 | names(NodesDF) <- c("name") 107 | 108 | # add node group if specified 109 | if (is.character(NodeGroup)) { 110 | NodesDF$group <- Nodes[, NodeGroup] 111 | } 112 | 113 | if (is.character(Label)) { 114 | NodesDF$label <- Nodes[, Label] 115 | } 116 | 117 | if (is.character(TopLabel)) { 118 | NodesDF$toplabel <- Nodes[, TopLabel] 119 | } 120 | 121 | if (is.character(LinkGroup)) { 122 | LinksDF$group <- Links[, LinkGroup] 123 | } 124 | 125 | margin <- margin_handler(margin) 126 | 127 | # create options 128 | options = list(NodeID = NodeID, NodeGroup = NodeGroup, LinkGroup = LinkGroup, 129 | colourScale = colourScale, fontSize = fontSize, fontFamily = fontFamily, 130 | nodeWidth = nodeWidth, nodePadding = nodePadding, units = units, 131 | margin = margin, iterations = iterations) 132 | 133 | # create widget 134 | htmlwidgets::createWidget(name = "sankeyNetwork", x = list(links = LinksDF, 135 | nodes = NodesDF, options = options), width = width, height = height, 136 | htmlwidgets::sizingPolicy(padding = 10, browser.fill = TRUE), 137 | package = "networkD3") 138 | } 139 | 140 | #' @rdname networkD3-shiny 141 | #' @export 142 | sankeyNetworkOutput <- function(outputId, width = "100%", height = "500px") { 143 | shinyWidgetOutput(outputId, "sankeyNetwork", width, height, 144 | package = "networkD3") 145 | } 146 | 147 | #' @rdname networkD3-shiny 148 | #' @export 149 | renderSankeyNetwork <- function(expr, env = parent.frame(), quoted = FALSE) { 150 | if (!quoted) 151 | { 152 | expr <- substitute(expr) 153 | } # force quoted 154 | shinyRenderWidget(expr, sankeyNetworkOutput, env, quoted = TRUE) 155 | } 156 | -------------------------------------------------------------------------------- /inst/htmlwidgets/radialNetwork.js: -------------------------------------------------------------------------------- 1 | HTMLWidgets.widget({ 2 | 3 | name: "radialNetwork", 4 | type: "output", 5 | 6 | initialize: function(el, width, height) { 7 | 8 | var diameter = Math.min( 9 | el.getBoundingClientRect().width, 10 | el.getBoundingClientRect().height 11 | ); 12 | 13 | d3.select(el).append("svg") 14 | .style("width", "100%") 15 | .style("height", "100%") 16 | .append("g") 17 | .attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")" 18 | + " scale("+diameter/800+","+diameter/800+")"); 19 | return d3.layout.tree(); 20 | 21 | }, 22 | 23 | resize: function(el, width, height, tree) { 24 | // resize now handled by svg viewBox attribute 25 | /* 26 | var diameter = Math.min(parseInt(width),parseInt(height)); 27 | var s = d3.select(el).selectAll("svg"); 28 | s.attr("width", width).attr("height", height); 29 | tree.size([360, diameter/2 - parseInt(s.attr("margin"))]); 30 | var svg = d3.select(el).selectAll("svg").select("g"); 31 | svg.attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")" 32 | + " scale("+diameter/800+","+diameter/800+")"); 33 | */ 34 | 35 | }, 36 | 37 | renderValue: function(el, x, tree) { 38 | // x is a list with two elements, options and root; root must already be a 39 | // JSON array with the d3Tree root data 40 | 41 | var s = d3.select(el).selectAll("svg"); 42 | 43 | // margin handling 44 | // set our default margin to be 20 45 | // will override with x.options.margin if provided 46 | var margin = {top: 20, right: 20, bottom: 20, left: 20}; 47 | // go through each key of x.options.margin 48 | // use this value if provided from the R side 49 | Object.keys(x.options.margin).map(function(ky){ 50 | if(x.options.margin[ky] !== null) { 51 | margin[ky] = x.options.margin[ky]; 52 | } 53 | // set the margin on the svg with css style 54 | // commenting this out since not correct 55 | //s.style(["margin",ky].join("-"), margin[ky]); 56 | }); 57 | 58 | var diameter = Math.min( 59 | s.node().getBoundingClientRect().width - margin.right - margin.left, 60 | s.node().getBoundingClientRect().height - margin.top - margin.bottom 61 | ); 62 | 63 | tree.size([360, diameter/2]) 64 | .separation(function(a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth; }); 65 | 66 | // select the svg group element and remove existing children 67 | s.attr("pointer-events", "all").selectAll("*").remove(); 68 | s.append("g") 69 | .attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")" 70 | + " scale("+1+","+1+")"); 71 | 72 | var svg = d3.select(el).selectAll("g"); 73 | 74 | var root = x.root; 75 | var nodes = tree.nodes(root), 76 | links = tree.links(nodes); 77 | 78 | var diagonal = d3.svg.diagonal.radial() 79 | .projection(function(d) { return [d.y, d.x / 180 * Math.PI]; }); 80 | 81 | // draw links 82 | var link = svg.selectAll(".link") 83 | .data(links) 84 | .enter().append("path") 85 | .style("fill", "none") 86 | .style("stroke", "#ccc") 87 | .style("opacity", "0.55") 88 | .style("stroke-width", "1.5px") 89 | .attr("d", diagonal); 90 | 91 | // draw nodes 92 | var node = svg.selectAll(".node") 93 | .data(nodes) 94 | .enter().append("g") 95 | .attr("class", "node") 96 | .attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")"; }) 97 | .on("mouseover", mouseover) 98 | .on("mouseout", mouseout); 99 | 100 | // node circles 101 | node.append("circle") 102 | .attr("r", 4.5) 103 | .style("fill", x.options.nodeColour) 104 | .style("opacity", x.options.opacity) 105 | .style("stroke", x.options.nodeStroke) 106 | .style("stroke-width", "1.5px"); 107 | 108 | // node text 109 | node.append("text") 110 | .attr("dy", ".31em") 111 | .attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; }) 112 | .attr("transform", function(d) { return d.x < 180 ? "translate(8)" : "rotate(180)translate(-8)"; }) 113 | .style("font", x.options.fontSize + "px " + x.options.fontFamily) 114 | .style("opacity", x.options.opacity) 115 | .style("fill", x.options.textColour) 116 | .text(function(d) { return d.name; }); 117 | 118 | // adjust viewBox to fit the bounds of our tree 119 | s.attr( 120 | "viewBox", 121 | [ 122 | d3.min( 123 | s.selectAll('.node text')[0].map(function(d){ 124 | return d.getBoundingClientRect().left 125 | }) 126 | ) - s.node().getBoundingClientRect().left - margin.right, 127 | d3.min( 128 | s.selectAll('.node text')[0].map(function(d){ 129 | return d.getBoundingClientRect().top 130 | }) 131 | ) - s.node().getBoundingClientRect().top - margin.top, 132 | d3.max( 133 | s.selectAll('.node text')[0].map(function(d){ 134 | return d.getBoundingClientRect().right 135 | }) 136 | ) - 137 | d3.min( 138 | s.selectAll('.node text')[0].map(function(d){ 139 | return d.getBoundingClientRect().left 140 | }) 141 | ) + margin.left + margin.right, 142 | d3.max( 143 | s.selectAll('.node text')[0].map(function(d){ 144 | return d.getBoundingClientRect().bottom 145 | }) 146 | ) - 147 | d3.min( 148 | s.selectAll('.node text')[0].map(function(d){ 149 | return d.getBoundingClientRect().top 150 | }) 151 | ) + margin.top + margin.bottom 152 | ].join(",") 153 | ); 154 | 155 | 156 | // mouseover event handler 157 | function mouseover() { 158 | d3.select(this).select("circle").transition() 159 | .duration(750) 160 | .attr("r", 9) 161 | d3.select(this).select("text").transition() 162 | .duration(750) 163 | .attr("dy", ".31em") 164 | .attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; }) 165 | .attr("transform", function(d) { return d.x < 180 ? "translate(8)" : "rotate(180)translate(-8)"; }) 166 | .style("stroke-width", ".5px") 167 | .style("font", "25px " + x.options.fontFamily) 168 | .style("opacity", 1); 169 | } 170 | 171 | // mouseout event handler 172 | function mouseout() { 173 | d3.select(this).select("circle").transition() 174 | .duration(750) 175 | .attr("r", 4.5) 176 | d3.select(this).select("text").transition() 177 | .duration(750) 178 | .attr("dy", ".31em") 179 | .attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; }) 180 | .attr("transform", function(d) { return d.x < 180 ? "translate(8)" : "rotate(180)translate(-8)"; }) 181 | .style("font", x.options.fontSize + "px " + x.options.fontFamily) 182 | .style("opacity", x.options.opacity); 183 | } 184 | 185 | 186 | }, 187 | }); 188 | -------------------------------------------------------------------------------- /inst/htmlwidgets/forceNetwork.js: -------------------------------------------------------------------------------- 1 | HTMLWidgets.widget({ 2 | 3 | name: "forceNetwork", 4 | 5 | type: "output", 6 | 7 | initialize: function(el, width, height) { 8 | 9 | d3.select(el).append("svg") 10 | .attr("width", width) 11 | .attr("height", height); 12 | 13 | return d3.layout.force(); 14 | }, 15 | 16 | resize: function(el, width, height, force) { 17 | 18 | d3.select(el).select("svg") 19 | .attr("width", width) 20 | .attr("height", height); 21 | 22 | force.size([width, height]).resume(); 23 | }, 24 | 25 | renderValue: function(el, x, force) { 26 | 27 | // Compute the node radius using the javascript math expression specified 28 | function nodeSize(d) { 29 | if(options.nodesize){ 30 | return eval(options.radiusCalculation); 31 | 32 | }else{ 33 | return 6} 34 | 35 | } 36 | 37 | 38 | // alias options 39 | var options = x.options; 40 | 41 | // convert links and nodes data frames to d3 friendly format 42 | var links = HTMLWidgets.dataframeToD3(x.links); 43 | var nodes = HTMLWidgets.dataframeToD3(x.nodes); 44 | 45 | // get the width and height 46 | var width = el.offsetWidth; 47 | var height = el.offsetHeight; 48 | 49 | var color = eval(options.colourScale); 50 | 51 | // set this up even if zoom = F 52 | var zoom = d3.behavior.zoom(); 53 | 54 | // create d3 force layout 55 | force 56 | .nodes(d3.values(nodes)) 57 | .links(links) 58 | .size([width, height]) 59 | .linkDistance(options.linkDistance) 60 | .charge(options.charge) 61 | .on("tick", tick) 62 | .start(); 63 | 64 | // thanks http://plnkr.co/edit/cxLlvIlmo1Y6vJyPs6N9?p=preview 65 | // http://stackoverflow.com/questions/22924253/adding-pan-zoom-to-d3js-force-directed 66 | var drag = force.drag() 67 | .on("dragstart", dragstart) 68 | // allow force drag to work with pan/zoom drag 69 | function dragstart(d) { 70 | d3.event.sourceEvent.preventDefault(); 71 | d3.event.sourceEvent.stopPropagation(); 72 | } 73 | 74 | // select the svg element and remove existing children 75 | var svg = d3.select(el).select("svg"); 76 | svg.selectAll("*").remove(); 77 | // add two g layers; the first will be zoom target if zoom = T 78 | // fine to have two g layers even if zoom = F 79 | svg = svg 80 | .append("g").attr("class","zoom-layer") 81 | .append("g") 82 | 83 | // add zooming if requested 84 | if (options.zoom) { 85 | function redraw() { 86 | d3.select(el).select(".zoom-layer").attr("transform", 87 | "translate(" + d3.event.translate + ")"+ 88 | " scale(" + d3.event.scale + ")"); 89 | } 90 | zoom.on("zoom", redraw) 91 | 92 | d3.select(el).select("svg") 93 | .attr("pointer-events", "all") 94 | .call(zoom); 95 | 96 | } else { 97 | zoom.on("zoom", null); 98 | } 99 | 100 | // draw links 101 | var link = svg.selectAll(".link") 102 | .data(force.links()) 103 | .enter().append("line") 104 | .attr("class", "link") 105 | .style("stroke", function(d) { return d.colour ; }) 106 | //.style("stroke", options.linkColour) 107 | .style("opacity", options.opacity) 108 | .style("stroke-width", eval("(" + options.linkWidth + ")")) 109 | .on("mouseover", function(d) { 110 | d3.select(this) 111 | .style("opacity", 1); 112 | }) 113 | .on("mouseout", function(d) { 114 | d3.select(this) 115 | .style("opacity", options.opacity); 116 | }); 117 | 118 | // draw nodes 119 | var node = svg.selectAll(".node") 120 | .data(force.nodes()) 121 | .enter().append("g") 122 | .attr("class", "node") 123 | .style("fill", function(d) { return color(d.group); }) 124 | .style("opacity", options.opacity) 125 | .on("mouseover", mouseover) 126 | .on("mouseout", mouseout) 127 | .on("click", click) 128 | .call(force.drag); 129 | 130 | node.append("circle") 131 | .attr("r", function(d){return nodeSize(d);}) 132 | .style("stroke", "#fff") 133 | .style("opacity", options.opacity) 134 | .style("stroke-width", "1.5px"); 135 | 136 | node.append("svg:text") 137 | .attr("class", "nodetext") 138 | .attr("dx", 12) 139 | .attr("dy", ".35em") 140 | .text(function(d) { return d.name }) 141 | .style("font", options.fontSize + "px " + options.fontFamily) 142 | .style("opacity", options.opacityNoHover) 143 | .style("pointer-events", "none"); 144 | 145 | function tick() { 146 | node.attr("transform", function(d) { 147 | if(options.bounded){ // adds bounding box 148 | d.x = Math.max(nodeSize(d), Math.min(width - nodeSize(d), d.x)); 149 | d.y = Math.max(nodeSize(d), Math.min(height - nodeSize(d), d.y)); 150 | } 151 | 152 | return "translate(" + d.x + "," + d.y + ")"}); 153 | 154 | link 155 | .attr("x1", function(d) { return d.source.x; }) 156 | .attr("y1", function(d) { return d.source.y; }) 157 | .attr("x2", function(d) { return d.target.x; }) 158 | .attr("y2", function(d) { return d.target.y; }); 159 | } 160 | 161 | function mouseover() { 162 | d3.select(this).select("circle").transition() 163 | .duration(750) 164 | .attr("r", function(d){return nodeSize(d)+5;}); 165 | d3.select(this).select("text").transition() 166 | .duration(750) 167 | .attr("x", 13) 168 | .style("stroke-width", ".5px") 169 | .style("font", options.clickTextSize + "px ") 170 | .style("opacity", 1); 171 | } 172 | 173 | function mouseout() { 174 | d3.select(this).select("circle").transition() 175 | .duration(750) 176 | .attr("r", function(d){return nodeSize(d);}); 177 | d3.select(this).select("text").transition() 178 | .duration(1250) 179 | .attr("x", 0) 180 | .style("font", options.fontSize + "px ") 181 | .style("opacity", options.opacityNoHover); 182 | } 183 | 184 | function click(d) { 185 | return eval(options.clickAction) 186 | } 187 | 188 | // add legend option 189 | if(options.legend){ 190 | var legendRectSize = 18; 191 | var legendSpacing = 4; 192 | var legend = svg.selectAll('.legend') 193 | .data(color.domain()) 194 | .enter() 195 | .append('g') 196 | .attr('class', 'legend') 197 | .attr('transform', function(d, i) { 198 | var height = legendRectSize + legendSpacing; 199 | var offset = height * color.domain().length / 2; 200 | var horz = legendRectSize; 201 | var vert = i * height+4; 202 | return 'translate(' + horz + ',' + vert + ')'; 203 | }); 204 | 205 | legend.append('rect') 206 | .attr('width', legendRectSize) 207 | .attr('height', legendRectSize) 208 | .style('fill', color) 209 | .style('stroke', color); 210 | 211 | legend.append('text') 212 | .attr('x', legendRectSize + legendSpacing) 213 | .attr('y', legendRectSize - legendSpacing) 214 | .text(function(d) { return d; }); 215 | } 216 | 217 | // make font-family consistent across all elements 218 | d3.select(el).selectAll('text').style('font-family', options.fontFamily); 219 | }, 220 | }); 221 | -------------------------------------------------------------------------------- /inst/htmlwidgets/dendroNetwork.js: -------------------------------------------------------------------------------- 1 | HTMLWidgets.widget({ 2 | 3 | name: "dendroNetwork", 4 | type: "output", 5 | 6 | initialize: function(el, width, height) { 7 | 8 | d3.select(el).append("svg") 9 | .attr("width", width) 10 | .attr("height", height) 11 | .append("g"); 12 | 13 | return d3.layout.cluster(); 14 | 15 | }, 16 | 17 | resize: function(el, width, height, tree) { 18 | 19 | var s = d3.select(el).selectAll("svg") 20 | .attr("width", width) 21 | .attr("height", height); 22 | 23 | var margins = s.attr("margins"); 24 | 25 | var top = parseInt(margins.top), 26 | right = parseInt(margins.right), 27 | bottom = parseInt(margins.bottom), 28 | left = parseInt(margins.left); 29 | 30 | height = height - top - bottom; 31 | width = width - right - left; 32 | 33 | if (s.attr("treeOrientation") == "horizontal") { 34 | tree.size([height, width]); 35 | } else { 36 | tree.size([width, height]); 37 | } 38 | 39 | var svg = d3.select(el).selectAll("svg").select("g") 40 | .attr("transform", "translate(" + left + "," + top + ")"); 41 | 42 | }, 43 | 44 | renderValue: function(el, x, tree) { 45 | 46 | var s = d3.select(el).selectAll("svg") 47 | .attr("margins", x.options.margins) 48 | .attr("treeOrientation", x.options.treeOrientation); 49 | 50 | var top = parseInt(x.options.margins.top), 51 | right = parseInt(x.options.margins.right), 52 | bottom = parseInt(x.options.margins.bottom), 53 | left = parseInt(x.options.margins.left); 54 | 55 | var height = parseInt(s.attr("height")) - top - bottom, 56 | width = parseInt(s.attr("width")) - right - left; 57 | 58 | if (s.attr("treeOrientation") == "horizontal") { 59 | tree.size([height, width]); 60 | } else { 61 | tree.size([width, height]); 62 | } 63 | 64 | var zoom = d3.behavior.zoom(); 65 | 66 | var svg = d3.select(el).select("svg"); 67 | svg.selectAll("*").remove(); 68 | 69 | svg = svg 70 | .append("g").attr("class","zoom-layer") 71 | .append("g") 72 | .attr("transform", "translate(" + left + "," + top + ")"); 73 | 74 | if (x.options.zoom) { 75 | zoom.on("zoom", function() { 76 | d3.select(el).select(".zoom-layer").attr("transform", 77 | "translate(" + d3.event.translate + ")"+ 78 | " scale(" + d3.event.scale + ")"); 79 | }); 80 | 81 | d3.select(el).select("svg") 82 | .attr("pointer-events", "all") 83 | .call(zoom); 84 | 85 | } else { 86 | zoom.on("zoom", null); 87 | } 88 | 89 | var root = x.root; 90 | 91 | var xs = []; 92 | var ys = []; 93 | function getXYfromJSONTree(node){ 94 | xs.push(node.x); 95 | ys.push(node.y); 96 | if(typeof node.children != 'undefined') { 97 | for (var j in node.children) { 98 | getXYfromJSONTree(node.children[j]); 99 | } 100 | } 101 | } 102 | var ymax = Number.MIN_VALUE; 103 | var ymin = Number.MAX_VALUE; 104 | 105 | getXYfromJSONTree(root); 106 | var nodes = tree.nodes(root); 107 | var links = tree.links(nodes); 108 | nodes.forEach( function(d,i){ 109 | if(typeof xs[i] != 'undefined') { 110 | d.x = xs[i]; 111 | } 112 | if(typeof ys[i] != 'undefined') { 113 | d.y = ys[i]; 114 | } 115 | }); 116 | nodes.forEach( function(d) { 117 | if(d.y > ymax) 118 | ymax = d.y; 119 | if(d.y < ymin) 120 | ymin = d.y; 121 | }); 122 | 123 | if (s.attr("treeOrientation") == "horizontal") { 124 | fxinv = d3.scale.linear().domain([ymin, ymax]).range([0, width]); 125 | fx = d3.scale.linear().domain([ymax, ymin]).range([0, width]); 126 | } else { 127 | fxinv = d3.scale.linear().domain([ymin, ymax]).range([0, height]); 128 | fx = d3.scale.linear().domain([ymax, ymin]).range([0, height]); 129 | } 130 | 131 | // draw links 132 | var link = svg.selectAll(".link") 133 | .data(links) 134 | .enter().append("path") 135 | .style("fill", "none") 136 | .style("stroke", "#ccc") 137 | .style("opacity", "0.55") 138 | .style("stroke-width", "1.5px"); 139 | 140 | if (x.options.linkType == "elbow") { 141 | if (s.attr("treeOrientation") == "horizontal") { 142 | link.attr("d", function(d, i) { 143 | return "M" + fx(d.source.y) + "," + d.source.x 144 | + "V" + d.target.x + "H" + fx(d.target.y); 145 | }); 146 | } else { 147 | link.attr("d", function(d, i) { 148 | return "M" + d.source.x + "," + fx(d.source.y) 149 | + "H" + d.target.x + "V" + fx(d.target.y); 150 | }); 151 | } 152 | } else { 153 | if (s.attr("treeOrientation") == "horizontal") { 154 | link.attr("d", d3.svg.diagonal() 155 | .projection(function(d) { return [fx(d.y), d.x]; })); 156 | } else { 157 | link.attr("d", d3.svg.diagonal() 158 | .projection(function(d) { return [d.x, fx(d.y)]; })); 159 | } 160 | } 161 | 162 | // draw nodes 163 | var node = svg.selectAll(".node") 164 | .data(nodes) 165 | .enter().append("g") 166 | .attr("class", "node") 167 | .on("mouseover", mouseover) 168 | .on("mouseout", mouseout); 169 | 170 | if (s.attr("treeOrientation") == "horizontal") { 171 | node.attr("transform", function(d) { return "translate(" + fx(d.y) + "," + d.x + ")"; }); 172 | } else { 173 | node.attr("transform", function(d) { return "translate(" + d.x + "," + fx(d.y) + ")"; }); 174 | } 175 | 176 | // node circles 177 | node.append("circle") 178 | .attr("r", 4.5) 179 | .style("fill", x.options.nodeColour) 180 | .style("opacity", x.options.opacity) 181 | .style("stroke", x.options.nodeStroke) 182 | .style("stroke-width", "1.5px"); 183 | 184 | // node text 185 | node.append("text") 186 | .attr("transform", "rotate(" + x.options.textRotate + ")") 187 | .style("font", x.options.fontSize + "px serif") 188 | .style("opacity", function(d) { return d.textOpacity; }) 189 | .style("fill", function(d) { return d.textColour; }) 190 | .text(function(d) { return d.name; }); 191 | 192 | if (s.attr("treeOrientation") == "horizontal") { 193 | node.select("text") 194 | .attr("dx", function(d) { return d.children ? -8 : 8; }) 195 | .attr("dy", ".31em") 196 | .attr("text-anchor", function(d) { return d.children ? "end" : "start"; }); 197 | } else { 198 | node.select("text") 199 | .attr("x", function(d) { return d.children ? -8 : 8; }) 200 | .attr("dy", ".31em") 201 | .attr("text-anchor", "start"); 202 | } 203 | 204 | // mouseover event handler 205 | function mouseover() { 206 | d3.select(this).select("circle").transition() 207 | .duration(750) 208 | .attr("r", 9); 209 | 210 | d3.select(this).select("text").transition() 211 | .duration(750) 212 | .style("stroke-width", ".5px") 213 | .style("font", "25px serif") 214 | .style("opacity", 1); 215 | } 216 | 217 | // mouseout event handler 218 | function mouseout() { 219 | d3.select(this).select("circle").transition() 220 | .duration(750) 221 | .attr("r", 4.5); 222 | 223 | d3.select(this).select("text").transition() 224 | .duration(750) 225 | .style("font", x.options.fontSize + "px serif") 226 | .style("opacity", x.options.opacity); 227 | } 228 | 229 | }, 230 | }); 231 | -------------------------------------------------------------------------------- /man/forceNetwork.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/forceNetwork.R 3 | \name{forceNetwork} 4 | \alias{forceNetwork} 5 | \title{Create a D3 JavaScript force directed network graph.} 6 | \source{ 7 | D3.js was created by Michael Bostock. See \url{http://d3js.org/} and, more 8 | specifically for force directed networks 9 | \url{https://github.com/mbostock/d3/wiki/Force-Layout}. 10 | } 11 | \usage{ 12 | forceNetwork(Links, Nodes, Source, Target, Value, NodeID, Nodesize, Group, 13 | height = NULL, width = NULL, colourScale = JS("d3.scale.category20()"), 14 | fontSize = 7, fontFamily = "serif", linkDistance = 50, 15 | linkWidth = JS("function(d) { return Math.sqrt(d.value); }"), 16 | radiusCalculation = JS(" Math.sqrt(d.nodesize)+6"), charge = -120, 17 | linkColour = "#666", opacity = 0.6, zoom = FALSE, legend = FALSE, 18 | bounded = FALSE, opacityNoHover = 0, clickAction = NULL) 19 | } 20 | \arguments{ 21 | \item{Links}{a data frame object with the links between the nodes. It should 22 | include the \code{Source} and \code{Target} for each link. These should be 23 | numbered starting from 0. An optional \code{Value} variable can be included 24 | to specify how close the nodes are to one another.} 25 | 26 | \item{Nodes}{a data frame containing the node id and properties of the nodes. 27 | If no ID is specified then the nodes must be in the same order as the Source 28 | variable column in the \code{Links} data frame. Currently only a grouping 29 | variable is allowed.} 30 | 31 | \item{Source}{character string naming the network source variable in the 32 | \code{Links} data frame.} 33 | 34 | \item{Target}{character string naming the network target variable in the 35 | \code{Links} data frame.} 36 | 37 | \item{Value}{character string naming the variable in the \code{Links} data 38 | frame for how wide the links are.} 39 | 40 | \item{NodeID}{character string specifying the node IDs in the \code{Nodes} 41 | data frame.} 42 | 43 | \item{Nodesize}{character string specifying the a column in the \code{Nodes} 44 | data frame with some value to vary the node radius's with. See also 45 | \code{radiusCalculation}.} 46 | 47 | \item{Group}{character string specifying the group of each node in the 48 | \code{Nodes} data frame.} 49 | 50 | \item{height}{numeric height for the network graph's frame area in pixels.} 51 | 52 | \item{width}{numeric width for the network graph's frame area in pixels.} 53 | 54 | \item{colourScale}{character string specifying the categorical colour 55 | scale for the nodes. See 56 | \url{https://github.com/mbostock/d3/wiki/Ordinal-Scales}.} 57 | 58 | \item{fontSize}{numeric font size in pixels for the node text labels.} 59 | 60 | \item{fontFamily}{font family for the node text labels.} 61 | 62 | \item{linkDistance}{numeric or character string. Either numberic fixed 63 | distance between the links in pixels (actually arbitrary relative to the 64 | diagram's size). Or a JavaScript function, possibly to weight by 65 | \code{Value}. For example: 66 | \code{linkDistance = JS("function(d){return d.value * 10}")}.} 67 | 68 | \item{linkWidth}{numeric or character string. Can be a numeric fixed width in 69 | pixels (arbitrary relative to the diagram's size). Or a JavaScript function, 70 | possibly to weight by \code{Value}. The default is 71 | \code{linkWidth = JS("function(d) { return Math.sqrt(d.value); }")}.} 72 | 73 | \item{radiusCalculation}{character string. A javascript mathematical 74 | expression, to weight the radius by \code{Nodesize}. The default value is 75 | \code{radiusCalculation = JS("Math.sqrt(d.nodesize)+6")}.} 76 | 77 | \item{charge}{numeric value indicating either the strength of the node 78 | repulsion (negative value) or attraction (positive value).} 79 | 80 | \item{linkColour}{character vector specifying the colour(s) you want the link 81 | lines to be. Multiple formats supported (e.g. hexadecimal).} 82 | 83 | \item{opacity}{numeric value of the proportion opaque you would like the 84 | graph elements to be.} 85 | 86 | \item{zoom}{logical value to enable (\code{TRUE}) or disable (\code{FALSE}) 87 | zooming.} 88 | 89 | \item{legend}{logical value to enable node colour legends.} 90 | 91 | \item{bounded}{logical value to enable (\code{TRUE}) or disable 92 | (\code{FALSE}) the bounding box limiting the graph's extent. See 93 | \url{http://bl.ocks.org/mbostock/1129492}.} 94 | 95 | \item{opacityNoHover}{numeric value of the opacity proportion for node labels 96 | text when the mouse is not hovering over them.} 97 | 98 | \item{clickAction}{character string with a JavaScript expression to evaluate 99 | when a node is clicked.} 100 | } 101 | \description{ 102 | Create a D3 JavaScript force directed network graph. 103 | } 104 | \examples{ 105 | # Load data 106 | data(MisLinks) 107 | data(MisNodes) 108 | # Create graph 109 | forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source", 110 | Target = "target", Value = "value", NodeID = "name", 111 | Group = "group", opacity = 0.4, zoom = TRUE) 112 | 113 | # Create graph with legend and varying node radius 114 | forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source", 115 | Target = "target", Value = "value", NodeID = "name", 116 | Nodesize = "size", 117 | radiusCalculation = "Math.sqrt(d.nodesize)+6", 118 | Group = "group", opacity = 0.4, legend = TRUE) 119 | 120 | \dontrun{ 121 | #### JSON Data Example 122 | # Load data JSON formated data into two R data frames 123 | # Create URL. paste0 used purely to keep within line width. 124 | URL <- paste0("https://cdn.rawgit.com/christophergandrud/networkD3/", 125 | "master/JSONdata/miserables.json") 126 | 127 | MisJson <- jsonlite::fromJSON(URL) 128 | 129 | # Create graph 130 | forceNetwork(Links = MisJson$links, Nodes = MisJson$nodes, Source = "source", 131 | Target = "target", Value = "value", NodeID = "name", 132 | Group = "group", opacity = 0.4) 133 | 134 | # Create graph with zooming 135 | forceNetwork(Links = MisJson$links, Nodes = MisJson$nodes, Source = "source", 136 | Target = "target", Value = "value", NodeID = "name", 137 | Group = "group", opacity = 0.4, zoom = TRUE) 138 | 139 | 140 | # Create a bounded graph 141 | forceNetwork(Links = MisJson$links, Nodes = MisJson$nodes, Source = "source", 142 | Target = "target", Value = "value", NodeID = "name", 143 | Group = "group", opacity = 0.4, bounded = TRUE) 144 | 145 | # Create graph with node text faintly visible when no hovering 146 | forceNetwork(Links = MisJson$links, Nodes = MisJson$nodes, Source = "source", 147 | Target = "target", Value = "value", NodeID = "name", 148 | Group = "group", opacity = 0.4, bounded = TRUE, 149 | opacityNoHover = TRUE) 150 | 151 | ## Specify colours for specific edges 152 | # Find links to Valjean (11) 153 | which(MisNodes == "Valjean", arr = TRUE)[1] - 1 154 | ValjeanInds = which(MisLinks == 11, arr = TRUE)[, 1] 155 | 156 | # Create a colour vector 157 | ValjeanCols = ifelse(1:nrow(MisLinks) \%in\% ValjeanInds, "#bf3eff", "#666") 158 | 159 | forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source", 160 | Target = "target", Value = "value", NodeID = "name", 161 | Group = "group", opacity = 0.8, linkColour = ValjeanCols) 162 | 163 | 164 | ## Create graph with alert pop-up when a node is clicked. You're 165 | # unlikely to want to do exactly this, but you might use 166 | # Shiny.onInputChange() to allocate d.XXX to an element of input 167 | # for use in a Shiny app. 168 | 169 | MyClickScript <- 'alert("You clicked " + d.name + " which is in row " + 170 | (d.index + 1) + " of your original R data frame");' 171 | 172 | forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source", 173 | Target = "target", Value = "value", NodeID = "name", 174 | Group = "group", opacity = 1, zoom = FALSE, 175 | bounded = TRUE, clickAction = MyClickScript) 176 | } 177 | 178 | } 179 | \seealso{ 180 | \code{\link{JS}}. 181 | } 182 | 183 | -------------------------------------------------------------------------------- /R/radialNetwork.R: -------------------------------------------------------------------------------- 1 | #' Create Reingold-Tilford Tree network diagrams. 2 | #' 3 | #' @param List a hierarchical list object with a root node and children. 4 | #' @param height height for the network graph's frame area in pixels (if 5 | #' \code{NULL} then height is automatically determined based on context) 6 | #' @param width numeric width for the network graph's frame area in pixels (if 7 | #' \code{NULL} then width is automatically determined based on context) 8 | #' @param fontSize numeric font size in pixels for the node text labels. 9 | #' @param fontFamily font family for the node text labels. 10 | #' @param linkColour character string specifying the colour you want the link 11 | #' lines to be. Multiple formats supported (e.g. hexadecimal). 12 | #' @param nodeColour character string specifying the colour you want the node 13 | #' circles to be. Multiple formats supported (e.g. hexadecimal). 14 | #' @param nodeStroke character string specifying the colour you want the node 15 | #' perimeter to be. Multiple formats supported (e.g. hexadecimal). 16 | #' @param textColour character string specifying the colour you want the text to 17 | #' be before they are clicked. Multiple formats supported (e.g. hexadecimal). 18 | #' @param opacity numeric value of the proportion opaque you would like the 19 | #' graph elements to be. 20 | #' @param margin an integer or a named \code{list}/\code{vector} of integers 21 | #' for the plot margins. If using a named \code{list}/\code{vector}, 22 | #' the positions \code{top}, \code{right}, \code{bottom}, \code{left} 23 | #' are valid. If a single integer is provided, then the value will be 24 | #' assigned to the right margin. Set the margin appropriately 25 | #' to accomodate long text labels. 26 | #' 27 | #' 28 | #' @examples 29 | #' \dontrun{ 30 | #' #### Create tree from JSON formatted data 31 | #' ## Download JSON data 32 | #' # Create URL. paste0 used purely to keep within line width. 33 | #' URL <- paste0("https://cdn.rawgit.com/christophergandrud/networkD3/", 34 | #' "master/JSONdata//flare.json") 35 | #' 36 | #' ## Convert to list format 37 | #' Flare <- jsonlite::fromJSON(URL, simplifyDataFrame = FALSE) 38 | #' 39 | #' ## Recreate Bostock example from http://bl.ocks.org/mbostock/4063550 40 | #' radialNetwork(List = Flare, fontSize = 10, opacity = 0.9) 41 | #' 42 | #' #### Create a tree dendrogram from an R hclust object 43 | #' hc <- hclust(dist(USArrests), "ave") 44 | #' radialNetwork(as.radialNetwork(hc)) 45 | #' radialNetwork(as.radialNetwork(hc), fontFamily = "cursive") 46 | #' 47 | #' #### Create tree from a hierarchical R list 48 | #' For an alternative structure see: http://stackoverflow.com/a/30747323/1705044 49 | #' CanadaPC <- list(name = "Canada", children = list(list(name = "Newfoundland", 50 | #' children = list(list(name = "St. John's"))), 51 | #' list(name = "PEI", 52 | #' children = list(list(name = "Charlottetown"))), 53 | #' list(name = "Nova Scotia", 54 | #' children = list(list(name = "Halifax"))), 55 | #' list(name = "New Brunswick", 56 | #' children = list(list(name = "Fredericton"))), 57 | #' list(name = "Quebec", 58 | #' children = list(list(name = "Montreal"), 59 | #' list(name = "Quebec City"))), 60 | #' list(name = "Ontario", 61 | #' children = list(list(name = "Toronto"), 62 | #' list(name = "Ottawa"))), 63 | #' list(name = "Manitoba", 64 | #' children = list(list(name = "Winnipeg"))), 65 | #' list(name = "Saskatchewan", 66 | #' children = list(list(name = "Regina"))), 67 | #' list(name = "Nunavuet", 68 | #' children = list(list(name = "Iqaluit"))), 69 | #' list(name = "NWT", 70 | #' children = list(list(name = "Yellowknife"))), 71 | #' list(name = "Alberta", 72 | #' children = list(list(name = "Edmonton"))), 73 | #' list(name = "British Columbia", 74 | #' children = list(list(name = "Victoria"), 75 | #' list(name = "Vancouver"))), 76 | #' list(name = "Yukon", 77 | #' children = list(list(name = "Whitehorse"))) 78 | #' )) 79 | #' 80 | #' radialNetwork(List = CanadaPC, fontSize = 10) 81 | #' } 82 | #' 83 | #' @source Reingold. E. M., and Tilford, J. S. (1981). Tidier Drawings of Trees. 84 | #' IEEE Transactions on Software Engineering, SE-7(2), 223-228. 85 | #' 86 | #' Mike Bostock: \url{http://bl.ocks.org/mbostock/4063550}. 87 | #' 88 | #' @export 89 | #' 90 | radialNetwork <- function( 91 | List, 92 | height = NULL, 93 | width = NULL, 94 | fontSize = 10, 95 | fontFamily = "serif", 96 | linkColour = "#ccc", 97 | nodeColour = "#fff", 98 | nodeStroke = "steelblue", 99 | textColour = "#111", 100 | opacity = 0.9, 101 | margin = NULL) 102 | { 103 | # validate input 104 | if (!is.list(List)) 105 | stop("List must be a list object.") 106 | root <- List 107 | 108 | margin <- margin_handler(margin) 109 | 110 | # create options 111 | options = list( 112 | height = height, 113 | width = width, 114 | fontSize = fontSize, 115 | fontFamily = fontFamily, 116 | linkColour = linkColour, 117 | nodeColour = nodeColour, 118 | nodeStroke = nodeStroke, 119 | textColour = textColour, 120 | margin = margin, 121 | opacity = opacity 122 | ) 123 | 124 | # create widget 125 | htmlwidgets::createWidget( 126 | name = "radialNetwork", 127 | x = list(root = root, options = options), 128 | width = width, 129 | height = height, 130 | htmlwidgets::sizingPolicy(padding = 10, browser.fill = TRUE), 131 | package = "networkD3") 132 | } 133 | 134 | #' @rdname networkD3-shiny 135 | #' @export 136 | radialNetworkOutput <- function(outputId, width = "100%", height = "800px") { 137 | shinyWidgetOutput(outputId, "radialNetwork", width, height, 138 | package = "networkD3") 139 | } 140 | 141 | #' @rdname networkD3-shiny 142 | #' @export 143 | renderRadialNetwork <- function(expr, env = parent.frame(), quoted = FALSE) { 144 | if (!quoted) { expr <- substitute(expr) } # force quoted 145 | shinyRenderWidget(expr, radialNetworkOutput, env, quoted = TRUE) 146 | } 147 | 148 | #' Convert an R hclust or dendrogram object into a radialNetwork list. 149 | #' 150 | #' \code{as.radialNetwork} converts an R hclust or dendrogram object into a list 151 | #' suitable for use by the \code{radialNetwork} function. 152 | #' 153 | #' @param d An object of R class \code{hclust} or \code{dendrogram}. 154 | #' @param root An optional name for the root node. If missing, use the first 155 | #' argument variable name. 156 | #' 157 | #' @details \code{as.radialNetwork} coverts R objects of class \code{hclust} or 158 | #' \code{dendrogram} into a list suitable for use with the \code{radialNetwork} 159 | #' function. 160 | #' @examples 161 | #' # Create a hierarchical cluster object and display with radialNetwork 162 | #' ## dontrun 163 | #' hc <- hclust(dist(USArrests), "ave") 164 | #' radialNetwork(as.radialNetwork(hc)) 165 | #' 166 | #' @importFrom stats as.dendrogram 167 | #' 168 | #' @export 169 | 170 | as.radialNetwork <- function(d, root) 171 | { 172 | if(missing(root)) root <- as.character(match.call()[[2]]) 173 | if("hclust" %in% class(d)) d <- as.dendrogram(d) 174 | if(!("dendrogram" %in% class(d))) 175 | stop("d must be a object of class hclust or dendrogram") 176 | ul <- function(x, level = 1) { 177 | if(is.list(x)) { 178 | return(lapply(x, function(y) 179 | { 180 | name <- "" 181 | if(!is.list(y)) name <- attr(y, "label") 182 | list(name=name, children=ul(y, level + 1)) 183 | })) 184 | } 185 | list(name = attr(x,"label")) 186 | } 187 | list(name = root, children = ul(d)) 188 | } 189 | -------------------------------------------------------------------------------- /inst/htmlwidgets/sankeyNetwork.js: -------------------------------------------------------------------------------- 1 | HTMLWidgets.widget({ 2 | 3 | name: "sankeyNetwork", 4 | 5 | type: "output", 6 | 7 | initialize: function(el, width, height) { 8 | 9 | d3.select(el).append("svg") 10 | .style("width", "100%") 11 | .style("height", "100%"); 12 | 13 | return { 14 | sankey: d3.sankey(), 15 | x: null 16 | }; 17 | }, 18 | 19 | resize: function(el, width, height, instance) { 20 | /* handle resizing now through the viewBox 21 | d3.select(el).select("svg") 22 | .attr("width", width) 23 | .attr("height", height + height * 0.05); 24 | 25 | this.renderValue(el, instance.x, instance); 26 | */ 27 | }, 28 | 29 | renderValue: function(el, x, instance) { 30 | 31 | // save the x in our instance (for calling back from resize) 32 | instance.x = x; 33 | 34 | // alias sankey and options 35 | var sankey = instance.sankey; 36 | var options = x.options; 37 | 38 | // convert links and nodes data frames to d3 friendly format 39 | var links = HTMLWidgets.dataframeToD3(x.links); 40 | var nodes = HTMLWidgets.dataframeToD3(x.nodes); 41 | 42 | 43 | // margin handling 44 | // set our default margin to be 20 45 | // will override with x.options.margin if provided 46 | var margin = {top: 20, right: 20, bottom: 20, left: 20}; 47 | // go through each key of x.options.margin 48 | // use this value if provided from the R side 49 | Object.keys(x.options.margin).map(function(ky){ 50 | if(x.options.margin[ky] !== null) { 51 | margin[ky] = x.options.margin[ky]; 52 | } 53 | // set the margin on the svg with css style 54 | // commenting this out since not correct 55 | // s.style(["margin",ky].join("-"), margin[ky]); 56 | }); 57 | 58 | // get the width and height 59 | var height = el.getBoundingClientRect().width - margin.right - margin.left; 60 | var width = el.getBoundingClientRect().height - margin.top - margin.bottom; 61 | 62 | var color = eval(options.colourScale); 63 | 64 | var color_node = function color_node(d){ 65 | if (d.group){ 66 | return color(d.group.replace(/ .*/, "")); 67 | } else { 68 | return "#cccccc"; 69 | } 70 | } 71 | 72 | var color_link = function color_link(d){ 73 | if (d.group){ 74 | return color(d.group.replace(/ .*/, "")); 75 | } else { 76 | return "#000000"; 77 | } 78 | } 79 | 80 | var opacity_link = function opacity_link(d){ 81 | if (d.group){ 82 | return 0.7; 83 | } else { 84 | return 0.2; 85 | } 86 | } 87 | 88 | 89 | var formatNumber = d3.format(",.0f"), 90 | format = function(d) { return formatNumber(d); } 91 | 92 | // create d3 sankey layout 93 | sankey 94 | .nodes(d3.values(nodes)) 95 | .links(links) 96 | .size([width, height]) 97 | .nodeWidth(options.nodeWidth) 98 | .nodePadding(options.nodePadding) 99 | .layout(options.iterations); 100 | 101 | // select the svg element and remove existing children 102 | d3.select(el).select("svg").selectAll("*").remove(); 103 | // append g for our container to transform by margin 104 | var svg = d3.select(el).select("svg").append("g") 105 | .attr("transform", 106 | "translate(" + margin.left + "," + margin.top + ")" + 107 | "rotate(90," + width/2 + "," + height/2 + ")"); 108 | 109 | // draw path 110 | var path = sankey.link(); 111 | 112 | // draw links 113 | var link = svg.selectAll(".link") 114 | .data(sankey.links()) 115 | 116 | link.enter().append("path") 117 | .attr("class", "link") 118 | 119 | link 120 | .attr("d", path) 121 | .style("stroke-width", function(d) { return d.dy; }) 122 | .style("fill", "none") 123 | .style("stroke", color_link) 124 | .style("stroke-opacity", opacity_link) 125 | .sort(function(a, b) { return b.dy - a.dy; }) 126 | .on("mouseover", function(d) { 127 | d3.select(this) 128 | .style("stroke-opacity", function(d){return opacity_link(d) + 0.3}); 129 | }) 130 | .on("mouseout", function(d) { 131 | d3.select(this) 132 | .style("stroke-opacity", opacity_link); 133 | }); 134 | 135 | // add backwards class to cycles 136 | link.classed('backwards', function (d) { return d.target.x < d.source.x; }); 137 | 138 | svg.selectAll(".link.backwards") 139 | .style("stroke-dasharray","9,1") 140 | .style("stroke","#402") 141 | 142 | // draw nodes 143 | var node = svg.selectAll(".node") 144 | .data(sankey.nodes()) 145 | 146 | node.enter().append("g") 147 | .attr("class", "node") 148 | .attr("transform", function(d) { return "translate(" + 149 | d.x + "," + d.y + ")"; }) 150 | .call(d3.behavior.drag() 151 | .origin(function(d) { return d; }) 152 | .on("dragstart", function() { this.parentNode.appendChild(this); }) 153 | .on("drag", dragmove)); 154 | 155 | // note: u2192 is right-arrow 156 | link.append("title") 157 | .text(function(d) { return (d.source.label || d.source.name) + 158 | " \u2192 " + (d.target.label || d.target.name) + 159 | "\n" + format(d.value) + " " + options.units; }); 160 | 161 | node.append("rect") 162 | .attr("height", function(d) { return d.dy; }) 163 | .attr("width", sankey.nodeWidth()) 164 | .style("fill", function(d) { 165 | return d.color = color_node(d); }) 166 | .style("stroke", function(d) { return d3.rgb(d.color).darker(2); }) 167 | .style("opacity", 0.9) 168 | .style("cursor", "move") 169 | .append("title") 170 | .text(function(d) { return d.name + "\n" + format(d.value) + 171 | " " + options.units; }); 172 | 173 | node.append("text") 174 | .attr("x", 0) 175 | .attr("y", 0) 176 | .attr("text-anchor", "middle") 177 | .attr("transform", function(d) { return "rotate(-90) translate(" + -d.dy/2 + ",20)" }) 178 | .text(function(d) { return d.label || d.name; }) 179 | .style("font-size", options.fontSize + "px") 180 | .style("font-family", options.fontFamily ? options.fontFamily : "inherit"); 181 | 182 | node.append("text") 183 | .attr("x", 0) 184 | .attr("y", 0) 185 | .attr("text-anchor", "start") 186 | .attr("transform", function(d) { return "rotate(-90) translate(" + -d.dy + ",-10)" }) 187 | .text(function(d) { return d.toplabel }) 188 | .style("font-size", options.fontSize + "px") 189 | .style("font-family", options.fontFamily ? options.fontFamily : "inherit"); 190 | 191 | // adjust viewBox to fit the bounds of our tree 192 | var s = d3.select(svg[0][0].parentNode); 193 | s.attr("viewBox", null); 194 | s.attr( 195 | "viewBox", 196 | [ 197 | d3.min( 198 | s.selectAll('g')[0].map(function(d){ 199 | return d.getBoundingClientRect().left 200 | }) 201 | ) - s.node().getBoundingClientRect().left - margin.right, 202 | d3.min( 203 | s.selectAll('g')[0].map(function(d){ 204 | return d.getBoundingClientRect().top 205 | }) 206 | ) - s.node().getBoundingClientRect().top - margin.top, 207 | d3.max( 208 | s.selectAll('g')[0].map(function(d){ 209 | return d.getBoundingClientRect().right 210 | }) 211 | ) - 212 | d3.min( 213 | s.selectAll('g')[0].map(function(d){ 214 | return d.getBoundingClientRect().left 215 | }) 216 | ) + margin.left + margin.right, 217 | d3.max( 218 | s.selectAll('g')[0].map(function(d){ 219 | return d.getBoundingClientRect().bottom 220 | }) 221 | ) - 222 | d3.min( 223 | s.selectAll('g')[0].map(function(d){ 224 | return d.getBoundingClientRect().top 225 | }) 226 | ) + margin.top + margin.bottom 227 | ].join(",") 228 | ); 229 | 230 | function dragmove(d) { 231 | d3.select(this).attr("transform", "translate(" + d.x + "," + 232 | (d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))) + ")"); 233 | sankey.relayout(); 234 | link.attr("d", path); 235 | } 236 | }, 237 | }); 238 | -------------------------------------------------------------------------------- /R/forceNetwork.R: -------------------------------------------------------------------------------- 1 | #' Create a D3 JavaScript force directed network graph. 2 | #' 3 | #' @param Links a data frame object with the links between the nodes. It should 4 | #' include the \code{Source} and \code{Target} for each link. These should be 5 | #' numbered starting from 0. An optional \code{Value} variable can be included 6 | #' to specify how close the nodes are to one another. 7 | #' @param Nodes a data frame containing the node id and properties of the nodes. 8 | #' If no ID is specified then the nodes must be in the same order as the Source 9 | #' variable column in the \code{Links} data frame. Currently only a grouping 10 | #' variable is allowed. 11 | #' @param Source character string naming the network source variable in the 12 | #' \code{Links} data frame. 13 | #' @param Target character string naming the network target variable in the 14 | #' \code{Links} data frame. 15 | #' @param Value character string naming the variable in the \code{Links} data 16 | #' frame for how wide the links are. 17 | #' @param NodeID character string specifying the node IDs in the \code{Nodes} 18 | #' data frame. 19 | #' @param Nodesize character string specifying the a column in the \code{Nodes} 20 | #' data frame with some value to vary the node radius's with. See also 21 | #' \code{radiusCalculation}. 22 | #' @param Group character string specifying the group of each node in the 23 | #' \code{Nodes} data frame. 24 | #' @param height numeric height for the network graph's frame area in pixels. 25 | #' @param width numeric width for the network graph's frame area in pixels. 26 | #' @param colourScale character string specifying the categorical colour 27 | #' scale for the nodes. See 28 | #' \url{https://github.com/mbostock/d3/wiki/Ordinal-Scales}. 29 | #' @param fontSize numeric font size in pixels for the node text labels. 30 | #' @param fontFamily font family for the node text labels. 31 | #' @param linkDistance numeric or character string. Either numberic fixed 32 | #' distance between the links in pixels (actually arbitrary relative to the 33 | #' diagram's size). Or a JavaScript function, possibly to weight by 34 | #' \code{Value}. For example: 35 | #' \code{linkDistance = JS("function(d){return d.value * 10}")}. 36 | #' @param linkWidth numeric or character string. Can be a numeric fixed width in 37 | #' pixels (arbitrary relative to the diagram's size). Or a JavaScript function, 38 | #' possibly to weight by \code{Value}. The default is 39 | #' \code{linkWidth = JS("function(d) { return Math.sqrt(d.value); }")}. 40 | #' @param radiusCalculation character string. A javascript mathematical 41 | #' expression, to weight the radius by \code{Nodesize}. The default value is 42 | #' \code{radiusCalculation = JS("Math.sqrt(d.nodesize)+6")}. 43 | #' @param charge numeric value indicating either the strength of the node 44 | #' repulsion (negative value) or attraction (positive value). 45 | #' @param linkColour character vector specifying the colour(s) you want the link 46 | #' lines to be. Multiple formats supported (e.g. hexadecimal). 47 | #' @param opacity numeric value of the proportion opaque you would like the 48 | #' graph elements to be. 49 | #' @param zoom logical value to enable (\code{TRUE}) or disable (\code{FALSE}) 50 | #' zooming. 51 | #' @param legend logical value to enable node colour legends. 52 | #' @param bounded logical value to enable (\code{TRUE}) or disable 53 | #' (\code{FALSE}) the bounding box limiting the graph's extent. See 54 | #' \url{http://bl.ocks.org/mbostock/1129492}. 55 | #' @param opacityNoHover numeric value of the opacity proportion for node labels 56 | #' text when the mouse is not hovering over them. 57 | #' @param clickAction character string with a JavaScript expression to evaluate 58 | #' when a node is clicked. 59 | #' 60 | #' @examples 61 | #' # Load data 62 | #' data(MisLinks) 63 | #' data(MisNodes) 64 | #' # Create graph 65 | #' forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source", 66 | #' Target = "target", Value = "value", NodeID = "name", 67 | #' Group = "group", opacity = 0.4, zoom = TRUE) 68 | #' 69 | #' # Create graph with legend and varying node radius 70 | #' forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source", 71 | #' Target = "target", Value = "value", NodeID = "name", 72 | #' Nodesize = "size", 73 | #' radiusCalculation = "Math.sqrt(d.nodesize)+6", 74 | #' Group = "group", opacity = 0.4, legend = TRUE) 75 | #' 76 | #' \dontrun{ 77 | #' #### JSON Data Example 78 | #' # Load data JSON formated data into two R data frames 79 | #' # Create URL. paste0 used purely to keep within line width. 80 | #' URL <- paste0("https://cdn.rawgit.com/christophergandrud/networkD3/", 81 | #' "master/JSONdata/miserables.json") 82 | #' 83 | #' MisJson <- jsonlite::fromJSON(URL) 84 | #' 85 | #' # Create graph 86 | #' forceNetwork(Links = MisJson$links, Nodes = MisJson$nodes, Source = "source", 87 | #' Target = "target", Value = "value", NodeID = "name", 88 | #' Group = "group", opacity = 0.4) 89 | #' 90 | #' # Create graph with zooming 91 | #' forceNetwork(Links = MisJson$links, Nodes = MisJson$nodes, Source = "source", 92 | #' Target = "target", Value = "value", NodeID = "name", 93 | #' Group = "group", opacity = 0.4, zoom = TRUE) 94 | #' 95 | #' 96 | #' # Create a bounded graph 97 | #' forceNetwork(Links = MisJson$links, Nodes = MisJson$nodes, Source = "source", 98 | #' Target = "target", Value = "value", NodeID = "name", 99 | #' Group = "group", opacity = 0.4, bounded = TRUE) 100 | #' 101 | #' # Create graph with node text faintly visible when no hovering 102 | #' forceNetwork(Links = MisJson$links, Nodes = MisJson$nodes, Source = "source", 103 | #' Target = "target", Value = "value", NodeID = "name", 104 | #' Group = "group", opacity = 0.4, bounded = TRUE, 105 | #' opacityNoHover = TRUE) 106 | #' 107 | #' ## Specify colours for specific edges 108 | #' # Find links to Valjean (11) 109 | #' which(MisNodes == "Valjean", arr = TRUE)[1] - 1 110 | #' ValjeanInds = which(MisLinks == 11, arr = TRUE)[, 1] 111 | #' 112 | #' # Create a colour vector 113 | #' ValjeanCols = ifelse(1:nrow(MisLinks) %in% ValjeanInds, "#bf3eff", "#666") 114 | #' 115 | #' forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source", 116 | #' Target = "target", Value = "value", NodeID = "name", 117 | #' Group = "group", opacity = 0.8, linkColour = ValjeanCols) 118 | #' 119 | #' 120 | #' ## Create graph with alert pop-up when a node is clicked. You're 121 | #' # unlikely to want to do exactly this, but you might use 122 | #' # Shiny.onInputChange() to allocate d.XXX to an element of input 123 | #' # for use in a Shiny app. 124 | #' 125 | #' MyClickScript <- 'alert("You clicked " + d.name + " which is in row " + 126 | #' (d.index + 1) + " of your original R data frame");' 127 | #' 128 | #' forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source", 129 | #' Target = "target", Value = "value", NodeID = "name", 130 | #' Group = "group", opacity = 1, zoom = FALSE, 131 | #' bounded = TRUE, clickAction = MyClickScript) 132 | #' } 133 | #' 134 | 135 | #' @source 136 | #' D3.js was created by Michael Bostock. See \url{http://d3js.org/} and, more 137 | #' specifically for force directed networks 138 | #' \url{https://github.com/mbostock/d3/wiki/Force-Layout}. 139 | #' @seealso \code{\link{JS}}. 140 | #' 141 | #' @export 142 | forceNetwork <- function(Links, 143 | Nodes, 144 | Source, 145 | Target, 146 | Value, 147 | NodeID, 148 | Nodesize, 149 | Group, 150 | height = NULL, 151 | width = NULL, 152 | colourScale = JS("d3.scale.category20()"), 153 | fontSize = 7, 154 | fontFamily = "serif", 155 | linkDistance = 50, 156 | linkWidth = JS("function(d) { return Math.sqrt(d.value); }"), 157 | radiusCalculation = JS(" Math.sqrt(d.nodesize)+6"), 158 | charge = -120, 159 | linkColour = "#666", 160 | opacity = 0.6, 161 | zoom = FALSE, 162 | legend = FALSE, 163 | bounded = FALSE, 164 | opacityNoHover = 0, 165 | clickAction = NULL) 166 | { 167 | # Hack for UI consistency. Think of improving. 168 | colourScale <- as.character(colourScale) 169 | linkWidth <- as.character(linkWidth) 170 | radiusCalculation <- as.character(radiusCalculation) 171 | 172 | # Subset data frames for network graph 173 | if (!is.data.frame(Links)) { 174 | stop("Links must be a data frame class object.") 175 | } 176 | if (!is.data.frame(Nodes)) { 177 | stop("Nodes must be a data frame class object.") 178 | } 179 | if (missing(Value)) { 180 | LinksDF <- data.frame(Links[, Source], Links[, Target]) 181 | names(LinksDF) <- c("source", "target") 182 | } 183 | else if (!missing(Value)) { 184 | LinksDF <- data.frame(Links[, Source], Links[, Target], Links[, Value]) 185 | names(LinksDF) <- c("source", "target", "value") 186 | } 187 | if (!missing(Nodesize)){ 188 | NodesDF <- data.frame(Nodes[, NodeID], Nodes[, Group], Nodes[, Nodesize]) 189 | names(NodesDF) <- c("name", "group", "nodesize") 190 | nodesize = TRUE 191 | }else{ 192 | NodesDF <- data.frame(Nodes[, NodeID], Nodes[, Group]) 193 | names(NodesDF) <- c("name", "group") 194 | nodesize = FALSE 195 | } 196 | LinksDF <- data.frame(LinksDF, colour=linkColour) 197 | LinksDF$colour = as.character(LinksDF$colour) 198 | 199 | # create options 200 | options = list( 201 | NodeID = NodeID, 202 | Group = Group, 203 | colourScale = colourScale, 204 | fontSize = fontSize, 205 | fontFamily = fontFamily, 206 | clickTextSize = fontSize * 2.5, 207 | linkDistance = linkDistance, 208 | linkWidth = linkWidth, 209 | charge = charge, 210 | # linkColour = linkColour, 211 | opacity = opacity, 212 | zoom = zoom, 213 | legend = legend, 214 | nodesize = nodesize, 215 | radiusCalculation = radiusCalculation, 216 | bounded = bounded, 217 | opacityNoHover = opacityNoHover, 218 | clickAction = clickAction 219 | ) 220 | 221 | # create widget 222 | htmlwidgets::createWidget( 223 | name = "forceNetwork", 224 | x = list(links = LinksDF, nodes = NodesDF, options = options), 225 | width = width, 226 | height = height, 227 | htmlwidgets::sizingPolicy(padding = 10, browser.fill = TRUE), 228 | package = "networkD3" 229 | ) 230 | } 231 | 232 | #' @rdname networkD3-shiny 233 | #' @export 234 | forceNetworkOutput <- function(outputId, width = "100%", height = "500px") { 235 | shinyWidgetOutput(outputId, "forceNetwork", width, height, 236 | package = "networkD3") 237 | } 238 | 239 | #' @rdname networkD3-shiny 240 | #' @export 241 | renderForceNetwork <- function(expr, env = parent.frame(), quoted = FALSE) { 242 | if (!quoted) { expr <- substitute(expr) } # force quoted 243 | shinyRenderWidget(expr, forceNetworkOutput, env, quoted = TRUE) 244 | } 245 | -------------------------------------------------------------------------------- /inst/htmlwidgets/lib/sankey.js: -------------------------------------------------------------------------------- 1 | d3.sankey = function() { 2 | var sankey = {}, 3 | nodeWidth = 24, 4 | nodePadding = 8, 5 | size = [1, 1], 6 | nodes = [], 7 | links = [], 8 | sinksRight = true; 9 | 10 | sankey.nodeWidth = function(_) { 11 | if (!arguments.length) return nodeWidth; 12 | nodeWidth = +_; 13 | return sankey; 14 | }; 15 | 16 | sankey.nodePadding = function(_) { 17 | if (!arguments.length) return nodePadding; 18 | nodePadding = +_; 19 | return sankey; 20 | }; 21 | 22 | sankey.nodes = function(_) { 23 | if (!arguments.length) return nodes; 24 | nodes = _; 25 | return sankey; 26 | }; 27 | 28 | sankey.links = function(_) { 29 | if (!arguments.length) return links; 30 | links = _; 31 | return sankey; 32 | }; 33 | 34 | sankey.size = function(_) { 35 | if (!arguments.length) return size; 36 | size = _; 37 | return sankey; 38 | }; 39 | 40 | sankey.sinksRight = function (_) { 41 | if (!arguments.length) return sinksRight; 42 | sinksRight = _; 43 | return sankey; 44 | }; 45 | 46 | sankey.layout = function(iterations) { 47 | computeNodeLinks(); 48 | computeNodeValues(); 49 | computeNodeBreadths(); 50 | computeNodeDepths(iterations); 51 | return sankey; 52 | }; 53 | 54 | sankey.relayout = function() { 55 | computeLinkDepths(); 56 | return sankey; 57 | }; 58 | 59 | // SVG path data generator, to be used as "d" attribute on "path" element selection. 60 | sankey.link = function() { 61 | var curvature = .5; 62 | 63 | function link(d) { 64 | var xs = d.source.x + d.source.dx, 65 | xt = d.target.x, 66 | xi = d3.interpolateNumber(xs, xt), 67 | xsc = xi(curvature), 68 | xtc = xi(1 - curvature), 69 | ys = d.source.y + d.sy + d.dy / 2, 70 | yt = d.target.y + d.ty + d.dy / 2; 71 | 72 | if (!d.cycleBreaker) { 73 | return "M" + xs + "," + ys 74 | + "C" + xsc + "," + ys 75 | + " " + xtc + "," + yt 76 | + " " + xt + "," + yt; 77 | } else { 78 | var xdelta = (1.5 * d.dy + 0.05 * Math.abs(xs - xt)); 79 | xsc = xs + xdelta; 80 | xtc = xt - xdelta; 81 | var xm = xi(0.5); 82 | var ym = d3.interpolateNumber(ys, yt)(0.5); 83 | var ydelta = (2 * d.dy + 0.1 * Math.abs(xs - xt) + 0.1 * Math.abs(ys - yt)) * (ym < (size[1] / 2) ? -1 : 1); 84 | return "M" + xs + "," + ys 85 | + "C" + xsc + "," + ys 86 | + " " + xsc + "," + (ys + ydelta) 87 | + " " + xm + "," + (ym + ydelta) 88 | + "S" + xtc + "," + yt 89 | + " " + xt + "," + yt; 90 | 91 | } 92 | } 93 | 94 | link.curvature = function(_) { 95 | if (!arguments.length) return curvature; 96 | curvature = +_; 97 | return link; 98 | }; 99 | 100 | return link; 101 | }; 102 | 103 | // Populate the sourceLinks and targetLinks for each node. 104 | // Also, if the source and target are not objects, assume they are indices. 105 | function computeNodeLinks() { 106 | nodes.forEach(function(node) { 107 | // Links that have this node as source. 108 | node.sourceLinks = []; 109 | // Links that have this node as target. 110 | node.targetLinks = []; 111 | }); 112 | links.forEach(function(link) { 113 | var source = link.source, 114 | target = link.target; 115 | if (typeof source === "number") source = link.source = nodes[link.source]; 116 | if (typeof target === "number") target = link.target = nodes[link.target]; 117 | source.sourceLinks.push(link); 118 | target.targetLinks.push(link); 119 | }); 120 | } 121 | 122 | // Compute the value (size) of each node by summing the associated links. 123 | function computeNodeValues() { 124 | nodes.forEach(function(node) { 125 | node.value = Math.max( 126 | d3.sum(node.sourceLinks, value), 127 | d3.sum(node.targetLinks, value) 128 | ); 129 | }); 130 | } 131 | 132 | // Iteratively assign the breadth (x-position) for each node. 133 | // Nodes are assigned the maximum breadth of incoming neighbors plus one; 134 | // nodes with no incoming links are assigned breadth zero, while 135 | // nodes with no outgoing links are assigned the maximum breadth. 136 | function computeNodeBreadths() { 137 | var remainingNodes = nodes, 138 | nextNodes, 139 | x = 0; 140 | 141 | // Work from left to right. 142 | // Keep updating the breath (x-position) of nodes that are target of recently updated nodes. 143 | while (remainingNodes.length && x < nodes.length) { 144 | nextNodes = []; 145 | remainingNodes.forEach(function(node) { 146 | node.x = x; 147 | node.dx = nodeWidth; 148 | node.sourceLinks.forEach(function(link) { 149 | if (nextNodes.indexOf(link.target) < 0 && !link.cycleBreaker) { 150 | nextNodes.push(link.target); 151 | } 152 | }); 153 | }); 154 | if (nextNodes.length == remainingNodes.length) { 155 | // There must be a cycle here. Let's search for a link that breaks it. 156 | findAndMarkCycleBreaker(nextNodes); 157 | // Start over. 158 | // TODO: make this optional? 159 | return computeNodeBreadths(); 160 | } 161 | else { 162 | remainingNodes = nextNodes; 163 | ++x; 164 | } 165 | } 166 | 167 | // Optionally move pure sinks always to the right. 168 | if (sinksRight) { 169 | moveSinksRight(x); 170 | } 171 | 172 | scaleNodeBreadths((size[0] - nodeWidth) / (x - 1)); 173 | } 174 | 175 | // Find a link that breaks a cycle in the graph (if any). 176 | function findAndMarkCycleBreaker(nodes) { 177 | // Go through all nodes from the given subset and traverse links searching for cycles. 178 | var link; 179 | for (var n=nodes.length - 1; n >= 0; n--) { 180 | link = depthFirstCycleSearch(nodes[n], []); 181 | if (link) { 182 | return link; 183 | } 184 | } 185 | 186 | // Depth-first search to find a link that is part of a cycle. 187 | function depthFirstCycleSearch(cursorNode, path) { 188 | var target, link; 189 | for (var n = cursorNode.sourceLinks.length - 1; n >= 0; n--) { 190 | link = cursorNode.sourceLinks[n]; 191 | if (link.cycleBreaker) { 192 | // Skip already known cycle breakers. 193 | continue; 194 | } 195 | 196 | // Check if target of link makes a cycle in current path. 197 | target = link.target; 198 | for (var l = 0; l < path.length; l++) { 199 | if (path[l].source == target) { 200 | // We found a cycle. Search for weakest link in cycle 201 | var weakest = link; 202 | for (; l < path.length; l++) { 203 | if (path[l].value < weakest.value) { 204 | weakest = path[l]; 205 | } 206 | } 207 | // Mark weakest link as (known) cycle breaker and abort search. 208 | weakest.cycleBreaker = true; 209 | return weakest; 210 | } 211 | } 212 | 213 | // Recurse deeper. 214 | path.push(link); 215 | link = depthFirstCycleSearch(target, path); 216 | path.pop(); 217 | // Stop further search if we found a cycle breaker. 218 | if (link) { 219 | return link; 220 | } 221 | } 222 | } 223 | } 224 | 225 | 226 | function moveSourcesRight() { 227 | nodes.forEach(function(node) { 228 | if (!node.targetLinks.length) { 229 | node.x = d3.min(node.sourceLinks, function(d) { return d.target.x; }) - 1; 230 | } 231 | }); 232 | } 233 | 234 | function moveSinksRight(x) { 235 | nodes.forEach(function(node) { 236 | if (!node.sourceLinks.length) { 237 | node.x = x - 1; 238 | } 239 | }); 240 | } 241 | 242 | function scaleNodeBreadths(kx) { 243 | nodes.forEach(function(node) { 244 | node.x *= kx; 245 | }); 246 | } 247 | 248 | // Compute the depth (y-position) for each node. 249 | function computeNodeDepths(iterations) { 250 | // Group nodes by breath. 251 | var nodesByBreadth = d3.nest() 252 | .key(function(d) { return d.x; }) 253 | .sortKeys(d3.ascending) 254 | .entries(nodes) 255 | .map(function(d) { return d.values; }); 256 | 257 | // 258 | initializeNodeDepth(); 259 | resolveCollisions(); 260 | computeLinkDepths(); 261 | for (var alpha = 1; iterations > 0; --iterations) { 262 | relaxRightToLeft(alpha *= .99); 263 | resolveCollisions(); 264 | computeLinkDepths(); 265 | relaxLeftToRight(alpha); 266 | resolveCollisions(); 267 | computeLinkDepths(); 268 | } 269 | 270 | function initializeNodeDepth() { 271 | // Calculate vertical scaling factor. 272 | var ky = d3.min(nodesByBreadth, function(nodes) { 273 | return (size[1] - (nodes.length - 1) * nodePadding) / d3.sum(nodes, value); 274 | }); 275 | 276 | nodesByBreadth.forEach(function(nodes) { 277 | nodes.forEach(function(node, i) { 278 | node.y = i; 279 | node.dy = node.value * ky; 280 | }); 281 | }); 282 | 283 | links.forEach(function(link) { 284 | link.dy = link.value * ky; 285 | }); 286 | } 287 | 288 | function relaxLeftToRight(alpha) { 289 | nodesByBreadth.forEach(function(nodes, breadth) { 290 | nodes.forEach(function(node) { 291 | if (node.targetLinks.length) { 292 | // Value-weighted average of the y-position of source node centers linked to this node. 293 | var y = d3.sum(node.targetLinks, weightedSource) / d3.sum(node.targetLinks, value); 294 | node.y += (y - center(node)) * alpha; 295 | } 296 | }); 297 | }); 298 | 299 | function weightedSource(link) { 300 | return (link.source.y + link.sy + link.dy / 2) * link.value; 301 | } 302 | } 303 | 304 | function relaxRightToLeft(alpha) { 305 | nodesByBreadth.slice().reverse().forEach(function(nodes) { 306 | nodes.forEach(function(node) { 307 | if (node.sourceLinks.length) { 308 | // Value-weighted average of the y-positions of target nodes linked to this node. 309 | var y = d3.sum(node.sourceLinks, weightedTarget) / d3.sum(node.sourceLinks, value); 310 | node.y += (y - center(node)) * alpha; 311 | } 312 | }); 313 | }); 314 | 315 | function weightedTarget(link) { 316 | return (link.target.y + link.ty + link.dy / 2) * link.value; 317 | } 318 | } 319 | 320 | function resolveCollisions() { 321 | nodesByBreadth.forEach(function(nodes) { 322 | var node, 323 | dy, 324 | y0 = 0, 325 | n = nodes.length, 326 | i; 327 | 328 | // Push any overlapping nodes down. 329 | nodes.sort(ascendingDepth); 330 | for (i = 0; i < n; ++i) { 331 | node = nodes[i]; 332 | dy = y0 - node.y; 333 | if (dy > 0) node.y += dy; 334 | y0 = node.y + node.dy + nodePadding; 335 | } 336 | 337 | // If the bottommost node goes outside the bounds, push it back up. 338 | dy = y0 - nodePadding - size[1]; 339 | if (dy > 0) { 340 | y0 = node.y -= dy; 341 | 342 | // Push any overlapping nodes back up. 343 | for (i = n - 2; i >= 0; --i) { 344 | node = nodes[i]; 345 | dy = node.y + node.dy + nodePadding - y0; 346 | if (dy > 0) node.y -= dy; 347 | y0 = node.y; 348 | } 349 | } 350 | }); 351 | } 352 | 353 | function ascendingDepth(a, b) { 354 | return a.y - b.y; 355 | } 356 | } 357 | 358 | // Compute y-offset of the source endpoint (sy) and target endpoints (ty) of links, 359 | // relative to the source/target node's y-position. 360 | function computeLinkDepths() { 361 | nodes.forEach(function(node) { 362 | node.sourceLinks.sort(ascendingTargetDepth); 363 | node.targetLinks.sort(ascendingSourceDepth); 364 | }); 365 | nodes.forEach(function(node) { 366 | var sy = 0, ty = 0; 367 | node.sourceLinks.forEach(function(link) { 368 | link.sy = sy; 369 | sy += link.dy; 370 | }); 371 | node.targetLinks.forEach(function(link) { 372 | link.ty = ty; 373 | ty += link.dy; 374 | }); 375 | }); 376 | 377 | function ascendingSourceDepth(a, b) { 378 | return a.source.y - b.source.y; 379 | } 380 | 381 | function ascendingTargetDepth(a, b) { 382 | return a.target.y - b.target.y; 383 | } 384 | } 385 | 386 | // Y-position of the middle of a node. 387 | function center(node) { 388 | return node.y + node.dy / 2; 389 | } 390 | 391 | // Value property accessor. 392 | function value(x) { 393 | return x.value; 394 | } 395 | 396 | return sankey; 397 | }; -------------------------------------------------------------------------------- /JSONdata/flare.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flare", 3 | "children": [ 4 | { 5 | "name": "analytics", 6 | "children": [ 7 | { 8 | "name": "cluster", 9 | "children": [ 10 | {"name": "AgglomerativeCluster", "size": 3938}, 11 | {"name": "CommunityStructure", "size": 3812}, 12 | {"name": "HierarchicalCluster", "size": 6714}, 13 | {"name": "MergeEdge", "size": 743} 14 | ] 15 | }, 16 | { 17 | "name": "graph", 18 | "children": [ 19 | {"name": "BetweennessCentrality", "size": 3534}, 20 | {"name": "LinkDistance", "size": 5731}, 21 | {"name": "MaxFlowMinCut", "size": 7840}, 22 | {"name": "ShortestPaths", "size": 5914}, 23 | {"name": "SpanningTree", "size": 3416} 24 | ] 25 | }, 26 | { 27 | "name": "optimization", 28 | "children": [ 29 | {"name": "AspectRatioBanker", "size": 7074} 30 | ] 31 | } 32 | ] 33 | }, 34 | { 35 | "name": "animate", 36 | "children": [ 37 | {"name": "Easing", "size": 17010}, 38 | {"name": "FunctionSequence", "size": 5842}, 39 | { 40 | "name": "interpolate", 41 | "children": [ 42 | {"name": "ArrayInterpolator", "size": 1983}, 43 | {"name": "ColorInterpolator", "size": 2047}, 44 | {"name": "DateInterpolator", "size": 1375}, 45 | {"name": "Interpolator", "size": 8746}, 46 | {"name": "MatrixInterpolator", "size": 2202}, 47 | {"name": "NumberInterpolator", "size": 1382}, 48 | {"name": "ObjectInterpolator", "size": 1629}, 49 | {"name": "PointInterpolator", "size": 1675}, 50 | {"name": "RectangleInterpolator", "size": 2042} 51 | ] 52 | }, 53 | {"name": "ISchedulable", "size": 1041}, 54 | {"name": "Parallel", "size": 5176}, 55 | {"name": "Pause", "size": 449}, 56 | {"name": "Scheduler", "size": 5593}, 57 | {"name": "Sequence", "size": 5534}, 58 | {"name": "Transition", "size": 9201}, 59 | {"name": "Transitioner", "size": 19975}, 60 | {"name": "TransitionEvent", "size": 1116}, 61 | {"name": "Tween", "size": 6006} 62 | ] 63 | }, 64 | { 65 | "name": "data", 66 | "children": [ 67 | { 68 | "name": "converters", 69 | "children": [ 70 | {"name": "Converters", "size": 721}, 71 | {"name": "DelimitedTextConverter", "size": 4294}, 72 | {"name": "GraphMLConverter", "size": 9800}, 73 | {"name": "IDataConverter", "size": 1314}, 74 | {"name": "JSONConverter", "size": 2220} 75 | ] 76 | }, 77 | {"name": "DataField", "size": 1759}, 78 | {"name": "DataSchema", "size": 2165}, 79 | {"name": "DataSet", "size": 586}, 80 | {"name": "DataSource", "size": 3331}, 81 | {"name": "DataTable", "size": 772}, 82 | {"name": "DataUtil", "size": 3322} 83 | ] 84 | }, 85 | { 86 | "name": "display", 87 | "children": [ 88 | {"name": "DirtySprite", "size": 8833}, 89 | {"name": "LineSprite", "size": 1732}, 90 | {"name": "RectSprite", "size": 3623}, 91 | {"name": "TextSprite", "size": 10066} 92 | ] 93 | }, 94 | { 95 | "name": "flex", 96 | "children": [ 97 | {"name": "FlareVis", "size": 4116} 98 | ] 99 | }, 100 | { 101 | "name": "physics", 102 | "children": [ 103 | {"name": "DragForce", "size": 1082}, 104 | {"name": "GravityForce", "size": 1336}, 105 | {"name": "IForce", "size": 319}, 106 | {"name": "NBodyForce", "size": 10498}, 107 | {"name": "Particle", "size": 2822}, 108 | {"name": "Simulation", "size": 9983}, 109 | {"name": "Spring", "size": 2213}, 110 | {"name": "SpringForce", "size": 1681} 111 | ] 112 | }, 113 | { 114 | "name": "query", 115 | "children": [ 116 | {"name": "AggregateExpression", "size": 1616}, 117 | {"name": "And", "size": 1027}, 118 | {"name": "Arithmetic", "size": 3891}, 119 | {"name": "Average", "size": 891}, 120 | {"name": "BinaryExpression", "size": 2893}, 121 | {"name": "Comparison", "size": 5103}, 122 | {"name": "CompositeExpression", "size": 3677}, 123 | {"name": "Count", "size": 781}, 124 | {"name": "DateUtil", "size": 4141}, 125 | {"name": "Distinct", "size": 933}, 126 | {"name": "Expression", "size": 5130}, 127 | {"name": "ExpressionIterator", "size": 3617}, 128 | {"name": "Fn", "size": 3240}, 129 | {"name": "If", "size": 2732}, 130 | {"name": "IsA", "size": 2039}, 131 | {"name": "Literal", "size": 1214}, 132 | {"name": "Match", "size": 3748}, 133 | {"name": "Maximum", "size": 843}, 134 | { 135 | "name": "methods", 136 | "children": [ 137 | {"name": "add", "size": 593}, 138 | {"name": "and", "size": 330}, 139 | {"name": "average", "size": 287}, 140 | {"name": "count", "size": 277}, 141 | {"name": "distinct", "size": 292}, 142 | {"name": "div", "size": 595}, 143 | {"name": "eq", "size": 594}, 144 | {"name": "fn", "size": 460}, 145 | {"name": "gt", "size": 603}, 146 | {"name": "gte", "size": 625}, 147 | {"name": "iff", "size": 748}, 148 | {"name": "isa", "size": 461}, 149 | {"name": "lt", "size": 597}, 150 | {"name": "lte", "size": 619}, 151 | {"name": "max", "size": 283}, 152 | {"name": "min", "size": 283}, 153 | {"name": "mod", "size": 591}, 154 | {"name": "mul", "size": 603}, 155 | {"name": "neq", "size": 599}, 156 | {"name": "not", "size": 386}, 157 | {"name": "or", "size": 323}, 158 | {"name": "orderby", "size": 307}, 159 | {"name": "range", "size": 772}, 160 | {"name": "select", "size": 296}, 161 | {"name": "stddev", "size": 363}, 162 | {"name": "sub", "size": 600}, 163 | {"name": "sum", "size": 280}, 164 | {"name": "update", "size": 307}, 165 | {"name": "variance", "size": 335}, 166 | {"name": "where", "size": 299}, 167 | {"name": "xor", "size": 354}, 168 | {"name": "_", "size": 264} 169 | ] 170 | }, 171 | {"name": "Minimum", "size": 843}, 172 | {"name": "Not", "size": 1554}, 173 | {"name": "Or", "size": 970}, 174 | {"name": "Query", "size": 13896}, 175 | {"name": "Range", "size": 1594}, 176 | {"name": "StringUtil", "size": 4130}, 177 | {"name": "Sum", "size": 791}, 178 | {"name": "Variable", "size": 1124}, 179 | {"name": "Variance", "size": 1876}, 180 | {"name": "Xor", "size": 1101} 181 | ] 182 | }, 183 | { 184 | "name": "scale", 185 | "children": [ 186 | {"name": "IScaleMap", "size": 2105}, 187 | {"name": "LinearScale", "size": 1316}, 188 | {"name": "LogScale", "size": 3151}, 189 | {"name": "OrdinalScale", "size": 3770}, 190 | {"name": "QuantileScale", "size": 2435}, 191 | {"name": "QuantitativeScale", "size": 4839}, 192 | {"name": "RootScale", "size": 1756}, 193 | {"name": "Scale", "size": 4268}, 194 | {"name": "ScaleType", "size": 1821}, 195 | {"name": "TimeScale", "size": 5833} 196 | ] 197 | }, 198 | { 199 | "name": "util", 200 | "children": [ 201 | {"name": "Arrays", "size": 8258}, 202 | {"name": "Colors", "size": 10001}, 203 | {"name": "Dates", "size": 8217}, 204 | {"name": "Displays", "size": 12555}, 205 | {"name": "Filter", "size": 2324}, 206 | {"name": "Geometry", "size": 10993}, 207 | { 208 | "name": "heap", 209 | "children": [ 210 | {"name": "FibonacciHeap", "size": 9354}, 211 | {"name": "HeapNode", "size": 1233} 212 | ] 213 | }, 214 | {"name": "IEvaluable", "size": 335}, 215 | {"name": "IPredicate", "size": 383}, 216 | {"name": "IValueProxy", "size": 874}, 217 | { 218 | "name": "math", 219 | "children": [ 220 | {"name": "DenseMatrix", "size": 3165}, 221 | {"name": "IMatrix", "size": 2815}, 222 | {"name": "SparseMatrix", "size": 3366} 223 | ] 224 | }, 225 | {"name": "Maths", "size": 17705}, 226 | {"name": "Orientation", "size": 1486}, 227 | { 228 | "name": "palette", 229 | "children": [ 230 | {"name": "ColorPalette", "size": 6367}, 231 | {"name": "Palette", "size": 1229}, 232 | {"name": "ShapePalette", "size": 2059}, 233 | {"name": "SizePalette", "size": 2291} 234 | ] 235 | }, 236 | {"name": "Property", "size": 5559}, 237 | {"name": "Shapes", "size": 19118}, 238 | {"name": "Sort", "size": 6887}, 239 | {"name": "Stats", "size": 6557}, 240 | {"name": "Strings", "size": 22026} 241 | ] 242 | }, 243 | { 244 | "name": "vis", 245 | "children": [ 246 | { 247 | "name": "axis", 248 | "children": [ 249 | {"name": "Axes", "size": 1302}, 250 | {"name": "Axis", "size": 24593}, 251 | {"name": "AxisGridLine", "size": 652}, 252 | {"name": "AxisLabel", "size": 636}, 253 | {"name": "CartesianAxes", "size": 6703} 254 | ] 255 | }, 256 | { 257 | "name": "controls", 258 | "children": [ 259 | {"name": "AnchorControl", "size": 2138}, 260 | {"name": "ClickControl", "size": 3824}, 261 | {"name": "Control", "size": 1353}, 262 | {"name": "ControlList", "size": 4665}, 263 | {"name": "DragControl", "size": 2649}, 264 | {"name": "ExpandControl", "size": 2832}, 265 | {"name": "HoverControl", "size": 4896}, 266 | {"name": "IControl", "size": 763}, 267 | {"name": "PanZoomControl", "size": 5222}, 268 | {"name": "SelectionControl", "size": 7862}, 269 | {"name": "TooltipControl", "size": 8435} 270 | ] 271 | }, 272 | { 273 | "name": "data", 274 | "children": [ 275 | {"name": "Data", "size": 20544}, 276 | {"name": "DataList", "size": 19788}, 277 | {"name": "DataSprite", "size": 10349}, 278 | {"name": "EdgeSprite", "size": 3301}, 279 | {"name": "NodeSprite", "size": 19382}, 280 | { 281 | "name": "render", 282 | "children": [ 283 | {"name": "ArrowType", "size": 698}, 284 | {"name": "EdgeRenderer", "size": 5569}, 285 | {"name": "IRenderer", "size": 353}, 286 | {"name": "ShapeRenderer", "size": 2247} 287 | ] 288 | }, 289 | {"name": "ScaleBinding", "size": 11275}, 290 | {"name": "Tree", "size": 7147}, 291 | {"name": "TreeBuilder", "size": 9930} 292 | ] 293 | }, 294 | { 295 | "name": "events", 296 | "children": [ 297 | {"name": "DataEvent", "size": 2313}, 298 | {"name": "SelectionEvent", "size": 1880}, 299 | {"name": "TooltipEvent", "size": 1701}, 300 | {"name": "VisualizationEvent", "size": 1117} 301 | ] 302 | }, 303 | { 304 | "name": "legend", 305 | "children": [ 306 | {"name": "Legend", "size": 20859}, 307 | {"name": "LegendItem", "size": 4614}, 308 | {"name": "LegendRange", "size": 10530} 309 | ] 310 | }, 311 | { 312 | "name": "operator", 313 | "children": [ 314 | { 315 | "name": "distortion", 316 | "children": [ 317 | {"name": "BifocalDistortion", "size": 4461}, 318 | {"name": "Distortion", "size": 6314}, 319 | {"name": "FisheyeDistortion", "size": 3444} 320 | ] 321 | }, 322 | { 323 | "name": "encoder", 324 | "children": [ 325 | {"name": "ColorEncoder", "size": 3179}, 326 | {"name": "Encoder", "size": 4060}, 327 | {"name": "PropertyEncoder", "size": 4138}, 328 | {"name": "ShapeEncoder", "size": 1690}, 329 | {"name": "SizeEncoder", "size": 1830} 330 | ] 331 | }, 332 | { 333 | "name": "filter", 334 | "children": [ 335 | {"name": "FisheyeTreeFilter", "size": 5219}, 336 | {"name": "GraphDistanceFilter", "size": 3165}, 337 | {"name": "VisibilityFilter", "size": 3509} 338 | ] 339 | }, 340 | {"name": "IOperator", "size": 1286}, 341 | { 342 | "name": "label", 343 | "children": [ 344 | {"name": "Labeler", "size": 9956}, 345 | {"name": "RadialLabeler", "size": 3899}, 346 | {"name": "StackedAreaLabeler", "size": 3202} 347 | ] 348 | }, 349 | { 350 | "name": "layout", 351 | "children": [ 352 | {"name": "AxisLayout", "size": 6725}, 353 | {"name": "BundledEdgeRouter", "size": 3727}, 354 | {"name": "CircleLayout", "size": 9317}, 355 | {"name": "CirclePackingLayout", "size": 12003}, 356 | {"name": "DendrogramLayout", "size": 4853}, 357 | {"name": "ForceDirectedLayout", "size": 8411}, 358 | {"name": "IcicleTreeLayout", "size": 4864}, 359 | {"name": "IndentedTreeLayout", "size": 3174}, 360 | {"name": "Layout", "size": 7881}, 361 | {"name": "NodeLinkTreeLayout", "size": 12870}, 362 | {"name": "PieLayout", "size": 2728}, 363 | {"name": "RadialTreeLayout", "size": 12348}, 364 | {"name": "RandomLayout", "size": 870}, 365 | {"name": "StackedAreaLayout", "size": 9121}, 366 | {"name": "TreeMapLayout", "size": 9191} 367 | ] 368 | }, 369 | {"name": "Operator", "size": 2490}, 370 | {"name": "OperatorList", "size": 5248}, 371 | {"name": "OperatorSequence", "size": 4190}, 372 | {"name": "OperatorSwitch", "size": 2581}, 373 | {"name": "SortOperator", "size": 2023} 374 | ] 375 | }, 376 | {"name": "Visualization", "size": 16540} 377 | ] 378 | } 379 | ] 380 | } 381 | -------------------------------------------------------------------------------- /JSONdata/miserables.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes":[ 3 | {"name":"Myriel","group":1}, 4 | {"name":"Napoleon","group":1}, 5 | {"name":"Mlle.Baptistine","group":1}, 6 | {"name":"Mme.Magloire","group":1}, 7 | {"name":"CountessdeLo","group":1}, 8 | {"name":"Geborand","group":1}, 9 | {"name":"Champtercier","group":1}, 10 | {"name":"Cravatte","group":1}, 11 | {"name":"Count","group":1}, 12 | {"name":"OldMan","group":1}, 13 | {"name":"Labarre","group":2}, 14 | {"name":"Valjean","group":2}, 15 | {"name":"Marguerite","group":3}, 16 | {"name":"Mme.deR","group":2}, 17 | {"name":"Isabeau","group":2}, 18 | {"name":"Gervais","group":2}, 19 | {"name":"Tholomyes","group":3}, 20 | {"name":"Listolier","group":3}, 21 | {"name":"Fameuil","group":3}, 22 | {"name":"Blacheville","group":3}, 23 | {"name":"Favourite","group":3}, 24 | {"name":"Dahlia","group":3}, 25 | {"name":"Zephine","group":3}, 26 | {"name":"Fantine","group":3}, 27 | {"name":"Mme.Thenardier","group":4}, 28 | {"name":"Thenardier","group":4}, 29 | {"name":"Cosette","group":5}, 30 | {"name":"Javert","group":4}, 31 | {"name":"Fauchelevent","group":0}, 32 | {"name":"Bamatabois","group":2}, 33 | {"name":"Perpetue","group":3}, 34 | {"name":"Simplice","group":2}, 35 | {"name":"Scaufflaire","group":2}, 36 | {"name":"Woman1","group":2}, 37 | {"name":"Judge","group":2}, 38 | {"name":"Champmathieu","group":2}, 39 | {"name":"Brevet","group":2}, 40 | {"name":"Chenildieu","group":2}, 41 | {"name":"Cochepaille","group":2}, 42 | {"name":"Pontmercy","group":4}, 43 | {"name":"Boulatruelle","group":6}, 44 | {"name":"Eponine","group":4}, 45 | {"name":"Anzelma","group":4}, 46 | {"name":"Woman2","group":5}, 47 | {"name":"MotherInnocent","group":0}, 48 | {"name":"Gribier","group":0}, 49 | {"name":"Jondrette","group":7}, 50 | {"name":"Mme.Burgon","group":7}, 51 | {"name":"Gavroche","group":8}, 52 | {"name":"Gillenormand","group":5}, 53 | {"name":"Magnon","group":5}, 54 | {"name":"Mlle.Gillenormand","group":5}, 55 | {"name":"Mme.Pontmercy","group":5}, 56 | {"name":"Mlle.Vaubois","group":5}, 57 | {"name":"Lt.Gillenormand","group":5}, 58 | {"name":"Marius","group":8}, 59 | {"name":"BaronessT","group":5}, 60 | {"name":"Mabeuf","group":8}, 61 | {"name":"Enjolras","group":8}, 62 | {"name":"Combeferre","group":8}, 63 | {"name":"Prouvaire","group":8}, 64 | {"name":"Feuilly","group":8}, 65 | {"name":"Courfeyrac","group":8}, 66 | {"name":"Bahorel","group":8}, 67 | {"name":"Bossuet","group":8}, 68 | {"name":"Joly","group":8}, 69 | {"name":"Grantaire","group":8}, 70 | {"name":"MotherPlutarch","group":9}, 71 | {"name":"Gueulemer","group":4}, 72 | {"name":"Babet","group":4}, 73 | {"name":"Claquesous","group":4}, 74 | {"name":"Montparnasse","group":4}, 75 | {"name":"Toussaint","group":5}, 76 | {"name":"Child1","group":10}, 77 | {"name":"Child2","group":10}, 78 | {"name":"Brujon","group":4}, 79 | {"name":"Mme.Hucheloup","group":8} 80 | ], 81 | "links":[ 82 | {"source":1,"target":0,"value":1}, 83 | {"source":2,"target":0,"value":8}, 84 | {"source":3,"target":0,"value":10}, 85 | {"source":3,"target":2,"value":6}, 86 | {"source":4,"target":0,"value":1}, 87 | {"source":5,"target":0,"value":1}, 88 | {"source":6,"target":0,"value":1}, 89 | {"source":7,"target":0,"value":1}, 90 | {"source":8,"target":0,"value":2}, 91 | {"source":9,"target":0,"value":1}, 92 | {"source":11,"target":10,"value":1}, 93 | {"source":11,"target":3,"value":3}, 94 | {"source":11,"target":2,"value":3}, 95 | {"source":11,"target":0,"value":5}, 96 | {"source":12,"target":11,"value":1}, 97 | {"source":13,"target":11,"value":1}, 98 | {"source":14,"target":11,"value":1}, 99 | {"source":15,"target":11,"value":1}, 100 | {"source":17,"target":16,"value":4}, 101 | {"source":18,"target":16,"value":4}, 102 | {"source":18,"target":17,"value":4}, 103 | {"source":19,"target":16,"value":4}, 104 | {"source":19,"target":17,"value":4}, 105 | {"source":19,"target":18,"value":4}, 106 | {"source":20,"target":16,"value":3}, 107 | {"source":20,"target":17,"value":3}, 108 | {"source":20,"target":18,"value":3}, 109 | {"source":20,"target":19,"value":4}, 110 | {"source":21,"target":16,"value":3}, 111 | {"source":21,"target":17,"value":3}, 112 | {"source":21,"target":18,"value":3}, 113 | {"source":21,"target":19,"value":3}, 114 | {"source":21,"target":20,"value":5}, 115 | {"source":22,"target":16,"value":3}, 116 | {"source":22,"target":17,"value":3}, 117 | {"source":22,"target":18,"value":3}, 118 | {"source":22,"target":19,"value":3}, 119 | {"source":22,"target":20,"value":4}, 120 | {"source":22,"target":21,"value":4}, 121 | {"source":23,"target":16,"value":3}, 122 | {"source":23,"target":17,"value":3}, 123 | {"source":23,"target":18,"value":3}, 124 | {"source":23,"target":19,"value":3}, 125 | {"source":23,"target":20,"value":4}, 126 | {"source":23,"target":21,"value":4}, 127 | {"source":23,"target":22,"value":4}, 128 | {"source":23,"target":12,"value":2}, 129 | {"source":23,"target":11,"value":9}, 130 | {"source":24,"target":23,"value":2}, 131 | {"source":24,"target":11,"value":7}, 132 | {"source":25,"target":24,"value":13}, 133 | {"source":25,"target":23,"value":1}, 134 | {"source":25,"target":11,"value":12}, 135 | {"source":26,"target":24,"value":4}, 136 | {"source":26,"target":11,"value":31}, 137 | {"source":26,"target":16,"value":1}, 138 | {"source":26,"target":25,"value":1}, 139 | {"source":27,"target":11,"value":17}, 140 | {"source":27,"target":23,"value":5}, 141 | {"source":27,"target":25,"value":5}, 142 | {"source":27,"target":24,"value":1}, 143 | {"source":27,"target":26,"value":1}, 144 | {"source":28,"target":11,"value":8}, 145 | {"source":28,"target":27,"value":1}, 146 | {"source":29,"target":23,"value":1}, 147 | {"source":29,"target":27,"value":1}, 148 | {"source":29,"target":11,"value":2}, 149 | {"source":30,"target":23,"value":1}, 150 | {"source":31,"target":30,"value":2}, 151 | {"source":31,"target":11,"value":3}, 152 | {"source":31,"target":23,"value":2}, 153 | {"source":31,"target":27,"value":1}, 154 | {"source":32,"target":11,"value":1}, 155 | {"source":33,"target":11,"value":2}, 156 | {"source":33,"target":27,"value":1}, 157 | {"source":34,"target":11,"value":3}, 158 | {"source":34,"target":29,"value":2}, 159 | {"source":35,"target":11,"value":3}, 160 | {"source":35,"target":34,"value":3}, 161 | {"source":35,"target":29,"value":2}, 162 | {"source":36,"target":34,"value":2}, 163 | {"source":36,"target":35,"value":2}, 164 | {"source":36,"target":11,"value":2}, 165 | {"source":36,"target":29,"value":1}, 166 | {"source":37,"target":34,"value":2}, 167 | {"source":37,"target":35,"value":2}, 168 | {"source":37,"target":36,"value":2}, 169 | {"source":37,"target":11,"value":2}, 170 | {"source":37,"target":29,"value":1}, 171 | {"source":38,"target":34,"value":2}, 172 | {"source":38,"target":35,"value":2}, 173 | {"source":38,"target":36,"value":2}, 174 | {"source":38,"target":37,"value":2}, 175 | {"source":38,"target":11,"value":2}, 176 | {"source":38,"target":29,"value":1}, 177 | {"source":39,"target":25,"value":1}, 178 | {"source":40,"target":25,"value":1}, 179 | {"source":41,"target":24,"value":2}, 180 | {"source":41,"target":25,"value":3}, 181 | {"source":42,"target":41,"value":2}, 182 | {"source":42,"target":25,"value":2}, 183 | {"source":42,"target":24,"value":1}, 184 | {"source":43,"target":11,"value":3}, 185 | {"source":43,"target":26,"value":1}, 186 | {"source":43,"target":27,"value":1}, 187 | {"source":44,"target":28,"value":3}, 188 | {"source":44,"target":11,"value":1}, 189 | {"source":45,"target":28,"value":2}, 190 | {"source":47,"target":46,"value":1}, 191 | {"source":48,"target":47,"value":2}, 192 | {"source":48,"target":25,"value":1}, 193 | {"source":48,"target":27,"value":1}, 194 | {"source":48,"target":11,"value":1}, 195 | {"source":49,"target":26,"value":3}, 196 | {"source":49,"target":11,"value":2}, 197 | {"source":50,"target":49,"value":1}, 198 | {"source":50,"target":24,"value":1}, 199 | {"source":51,"target":49,"value":9}, 200 | {"source":51,"target":26,"value":2}, 201 | {"source":51,"target":11,"value":2}, 202 | {"source":52,"target":51,"value":1}, 203 | {"source":52,"target":39,"value":1}, 204 | {"source":53,"target":51,"value":1}, 205 | {"source":54,"target":51,"value":2}, 206 | {"source":54,"target":49,"value":1}, 207 | {"source":54,"target":26,"value":1}, 208 | {"source":55,"target":51,"value":6}, 209 | {"source":55,"target":49,"value":12}, 210 | {"source":55,"target":39,"value":1}, 211 | {"source":55,"target":54,"value":1}, 212 | {"source":55,"target":26,"value":21}, 213 | {"source":55,"target":11,"value":19}, 214 | {"source":55,"target":16,"value":1}, 215 | {"source":55,"target":25,"value":2}, 216 | {"source":55,"target":41,"value":5}, 217 | {"source":55,"target":48,"value":4}, 218 | {"source":56,"target":49,"value":1}, 219 | {"source":56,"target":55,"value":1}, 220 | {"source":57,"target":55,"value":1}, 221 | {"source":57,"target":41,"value":1}, 222 | {"source":57,"target":48,"value":1}, 223 | {"source":58,"target":55,"value":7}, 224 | {"source":58,"target":48,"value":7}, 225 | {"source":58,"target":27,"value":6}, 226 | {"source":58,"target":57,"value":1}, 227 | {"source":58,"target":11,"value":4}, 228 | {"source":59,"target":58,"value":15}, 229 | {"source":59,"target":55,"value":5}, 230 | {"source":59,"target":48,"value":6}, 231 | {"source":59,"target":57,"value":2}, 232 | {"source":60,"target":48,"value":1}, 233 | {"source":60,"target":58,"value":4}, 234 | {"source":60,"target":59,"value":2}, 235 | {"source":61,"target":48,"value":2}, 236 | {"source":61,"target":58,"value":6}, 237 | {"source":61,"target":60,"value":2}, 238 | {"source":61,"target":59,"value":5}, 239 | {"source":61,"target":57,"value":1}, 240 | {"source":61,"target":55,"value":1}, 241 | {"source":62,"target":55,"value":9}, 242 | {"source":62,"target":58,"value":17}, 243 | {"source":62,"target":59,"value":13}, 244 | {"source":62,"target":48,"value":7}, 245 | {"source":62,"target":57,"value":2}, 246 | {"source":62,"target":41,"value":1}, 247 | {"source":62,"target":61,"value":6}, 248 | {"source":62,"target":60,"value":3}, 249 | {"source":63,"target":59,"value":5}, 250 | {"source":63,"target":48,"value":5}, 251 | {"source":63,"target":62,"value":6}, 252 | {"source":63,"target":57,"value":2}, 253 | {"source":63,"target":58,"value":4}, 254 | {"source":63,"target":61,"value":3}, 255 | {"source":63,"target":60,"value":2}, 256 | {"source":63,"target":55,"value":1}, 257 | {"source":64,"target":55,"value":5}, 258 | {"source":64,"target":62,"value":12}, 259 | {"source":64,"target":48,"value":5}, 260 | {"source":64,"target":63,"value":4}, 261 | {"source":64,"target":58,"value":10}, 262 | {"source":64,"target":61,"value":6}, 263 | {"source":64,"target":60,"value":2}, 264 | {"source":64,"target":59,"value":9}, 265 | {"source":64,"target":57,"value":1}, 266 | {"source":64,"target":11,"value":1}, 267 | {"source":65,"target":63,"value":5}, 268 | {"source":65,"target":64,"value":7}, 269 | {"source":65,"target":48,"value":3}, 270 | {"source":65,"target":62,"value":5}, 271 | {"source":65,"target":58,"value":5}, 272 | {"source":65,"target":61,"value":5}, 273 | {"source":65,"target":60,"value":2}, 274 | {"source":65,"target":59,"value":5}, 275 | {"source":65,"target":57,"value":1}, 276 | {"source":65,"target":55,"value":2}, 277 | {"source":66,"target":64,"value":3}, 278 | {"source":66,"target":58,"value":3}, 279 | {"source":66,"target":59,"value":1}, 280 | {"source":66,"target":62,"value":2}, 281 | {"source":66,"target":65,"value":2}, 282 | {"source":66,"target":48,"value":1}, 283 | {"source":66,"target":63,"value":1}, 284 | {"source":66,"target":61,"value":1}, 285 | {"source":66,"target":60,"value":1}, 286 | {"source":67,"target":57,"value":3}, 287 | {"source":68,"target":25,"value":5}, 288 | {"source":68,"target":11,"value":1}, 289 | {"source":68,"target":24,"value":1}, 290 | {"source":68,"target":27,"value":1}, 291 | {"source":68,"target":48,"value":1}, 292 | {"source":68,"target":41,"value":1}, 293 | {"source":69,"target":25,"value":6}, 294 | {"source":69,"target":68,"value":6}, 295 | {"source":69,"target":11,"value":1}, 296 | {"source":69,"target":24,"value":1}, 297 | {"source":69,"target":27,"value":2}, 298 | {"source":69,"target":48,"value":1}, 299 | {"source":69,"target":41,"value":1}, 300 | {"source":70,"target":25,"value":4}, 301 | {"source":70,"target":69,"value":4}, 302 | {"source":70,"target":68,"value":4}, 303 | {"source":70,"target":11,"value":1}, 304 | {"source":70,"target":24,"value":1}, 305 | {"source":70,"target":27,"value":1}, 306 | {"source":70,"target":41,"value":1}, 307 | {"source":70,"target":58,"value":1}, 308 | {"source":71,"target":27,"value":1}, 309 | {"source":71,"target":69,"value":2}, 310 | {"source":71,"target":68,"value":2}, 311 | {"source":71,"target":70,"value":2}, 312 | {"source":71,"target":11,"value":1}, 313 | {"source":71,"target":48,"value":1}, 314 | {"source":71,"target":41,"value":1}, 315 | {"source":71,"target":25,"value":1}, 316 | {"source":72,"target":26,"value":2}, 317 | {"source":72,"target":27,"value":1}, 318 | {"source":72,"target":11,"value":1}, 319 | {"source":73,"target":48,"value":2}, 320 | {"source":74,"target":48,"value":2}, 321 | {"source":74,"target":73,"value":3}, 322 | {"source":75,"target":69,"value":3}, 323 | {"source":75,"target":68,"value":3}, 324 | {"source":75,"target":25,"value":3}, 325 | {"source":75,"target":48,"value":1}, 326 | {"source":75,"target":41,"value":1}, 327 | {"source":75,"target":70,"value":1}, 328 | {"source":75,"target":71,"value":1}, 329 | {"source":76,"target":64,"value":1}, 330 | {"source":76,"target":65,"value":1}, 331 | {"source":76,"target":66,"value":1}, 332 | {"source":76,"target":63,"value":1}, 333 | {"source":76,"target":62,"value":1}, 334 | {"source":76,"target":48,"value":1}, 335 | {"source":76,"target":58,"value":1} 336 | ] 337 | } 338 | --------------------------------------------------------------------------------