christophergandrud / networkD3

D3 JavaScript Network Graphs from R
http://christophergandrud.github.io/networkD3
649 stars 269 forks source link

A legend for Sankey networks #240

Open wlandau opened 6 years ago

wlandau commented 6 years ago

Any plans for sankeyNetwork(NodeGroup = "column_with_color", legend = TRUE)?

cjyetman commented 6 years ago

I don't believe anyone is working on that, but suppose we'd be open to PRs for it.

fbiga commented 2 years ago

Hello, I faced the same issue. Here's the way to add a legend.

You first need to create a d3.js code and store in the working directory. This d3.js code will create a legend, I started from the description in https://www.d3-graph-gallery.com/graph/custom_legend.html (section: categorical legend, use a loop). I adapted to let it use data for the sankey as input

the d3.js could be called in R using r2d3::r2d3() function.

Then you need the function manipulateWidget::combineWidgets() to pair the sankey from sankeyNetwork and the legend.

I hope this could be useful for implementing a legend = T argument within the sankeyNetwork function.

Here's an example: the d3.js code `// create a list of keys var keys = data

// Usually you have a color scale in your chart already var color = d3.scaleOrdinal() .domain(keys) .range(d3.schemeCategory10)

// Add one dot in the legend for each name. svg.selectAll("mydots") .data(keys) .enter() .append("circle") .attr("cx", 50) .attr("cy", function(d,i){ return 100 + i*25}) // 100 is where the first dot appears. 25 is the distance between dots .attr("r", 7) .style("fill", function(d){ return color(d)})

// Add one dot in the legend for each name. svg.selectAll("mylabels") .data(keys) .enter() .append("text") .attr("x", 70) .attr("y", function(d,i){ return 100 + i*25}) // 100 is where the first dot appears. 25 is the distance between dots // .style("fill", function(d){ return color(d)}) .text(function(d){ return d}) .attr("text-anchor", "left") .style("alignment-baseline", "middle")`

and here's the R code: library(networkD3) library(dplyr) library(manipulateWidget) library(r2d3)

links <- data.frame( source=c("group_A","group_A", "group_B", "group_C", "group_C", "group_E"), target=c("group_C","group_D", "group_E", "group_F", "group_G", "group_H"), value=c(2, 3, 2, 3, 1, 3) ) # Make a connection data frame

nodes <- data.frame( name=c(as.character(links$source), as.character(links$target)) %>% unique() ) # Make a nodes data frame

links$IDsource <- match(links$source, nodes$name)-1 links$IDtarget <- match(links$target, nodes$name)-1

links$group <- as.factor(c("type_a","type_a","type_a","type_b","type_b","type_b")) # Add a 'group' column to each connection: nodes$group <- as.factor(c("my_first_group","my_first_group","my_first_group","my_first_group","my_first_group","my_second_group","my_first_group","my_second_group"))

my_color <- 'd3.scaleOrdinal() .domain(["type_a", "type_b", "my_first_group", "my_second_group"]) .range(d3.schemeCategory10)'

sankey <- sankeyNetwork(Links = links, Nodes = nodes, Source = "IDsource", Target = "IDtarget", Value = "value", NodeID = "name", colourScale=my_color, LinkGroup="group", NodeGroup="group")

legend <- r2d3(data = unique(c(links$group, nodes$group)), script = "legend_test.js") # legend

combineWidgets(sankey, legend, ncol = 2, colsize = c(5,1)) # Combine plots