christophergandrud / networkD3

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

sankey plot for correctly set node height ? #268

Closed LuffyLuffy closed 4 years ago

LuffyLuffy commented 4 years ago

Hi, I found there is some code in sanekyNetwork.js to add a parameter so that we can predefined the height of a node. Previously, the node height was by accumulating in the links part. But the value is not scale according to the canvas height. don't know how to attached the files. image

links file:

N1 N2 value
T_006 T_007 226
T_004 T_007 28
T_003 T_007 26
T_002 T_007 3
T_001 T_007 4
T_006_new T_007 21
T_004_new T_007 21
T_003_new T_007 7
T_002_new T_007 9
T_004 T_006 193
T_003 T_006 34
T_002 T_006 5
T_001 T_006 23
T_004_new T_006 46
T_003_new T_006 14
T_002_new T_006 11
T_003 T_004 146
T_002 T_004 23
T_001 T_004 37
T_003_new T_004 20
T_002_new T_004 24
T_002 T_003 151
T_001 T_003 128
T_002_new T_003 30
T_001 T_002 191

nodes:file

Id height
T_007 345
T_006 326
T_004 250
T_003 309
T_002 191
T_001 557
T_007_new 52
T_006_new 128
T_004_new 128
T_003_new 115
T_002_new 112

R code: library(networkD3) library(htmlwidgets) library(tidyverse) options(stringsAsFactors = F) links1<-read.csv('edges_test.csv') nodes1<-read.csv('node.csv') nodeID<-rownames(nodes1) names(nodeID)<-nodes1$Id

names(links) = c("source", "target", "value")

links1$source<-as.numeric(unname(nodeID[links1$N1]))-1 links1$target<-as.numeric(unname(nodeID[links1$N2]))-1 nodes1$name<-as.numeric(unname(nodeID[nodes1$Id]))-1 nodes1$ngroup<-gsub("_new","",nodes1$Id) color_scale1<-as.data.frame(nodes1) color_scale1$range<-brewer.pal(n=nrow(color_scale1),name='Paired')[as.numeric(factor(color_scale1$ngroup))] color_scale1$domain=color_scale1$Id

sankeyNetwork(Links = links1, Nodes = nodes1,#nodePadding=100, Source = "source", Target = "target", Value = "value", LinkGroup = 'N1',NodeGroup = 'ngroup', NodeID = "Id",#height = 200, fontSize= 12, nodeWidth = 10,sinksRight = T,iterations = 0)

node.append("rect")
        **.attr("height", function(d) { return d.value; })**
        .attr("width", sankey.nodeWidth())
        .style("fill", function(d) {
            return d.color = color_node(d); })
        .style("stroke", function(d) { return d3.rgb(d.color).darker(2); })
        .style("opacity", 0.9)
        .style("cursor", "move")
        .append("title")
        .append("foreignObject")
        .append("xhtml:body")
        .html(function(d) { return "<pre>" + d.name + "<br>" + format(d.value) + 
            " " + options.units + "</pre>"; });
LuffyLuffy commented 4 years ago

take T_001 for example, the node has 557, it can flow to others, but the sum of its contribution to other is 383. Thus, it still keep 174. Then the node height should be 557. However, presently, sankeyNetwork only give a length of 383, and the 174 was ignored. Thus, I wonder if it is possible to give a nodeHeight setting option, so that the I can present my data correctly. Any hint on achieving my purpose would be highly appreciated!

cjyetman commented 4 years ago

Please make a fully reproducible example. I should be able to copy-paste your code and click run and see the same result.

LuffyLuffy commented 4 years ago

Please make a fully reproducible example. I should be able to copy-paste your code and click run and see the same result.

Thanks for your prompt reply! Below is a reproducible example. I hope the node height can be set.

rm(list=ls()) library(stringi) library(networkD3) library(htmlwidgets) library(tidyverse) library(RColorBrewer)

install.packages('networkD3')

rm(list=ls()) options(stringsAsFactors = F) links1<-as.data.frame(matrix(c( "T_006","T_007",226, "T_004","T_007",28, "T_003","T_007",26, "T_002","T_007",3, "T_001","T_007",4, "T_006_new","T_007",21, "T_004_new","T_007",21, "T_003_new","T_007",7, "T_002_new","T_007",9, "T_004","T_006",193, "T_003","T_006",34, "T_002","T_006",5, "T_001","T_006",23, "T_004_new","T_006",46, "T_003_new","T_006",14, "T_002_new","T_006",11, "T_003","T_004",146, "T_002","T_004",23, "T_001","T_004",37, "T_003_new","T_004",20, "T_002_new","T_004",24, "T_002","T_003",151, "T_001","T_003",128, "T_002_new","T_003",30, "T_001","T_002",191),nrow = 25,byrow = T, )) colnames(links1)<-c('N1','N2','value') nodes1<-as.data.frame(matrix( c("T_007",345, "T_006",326, "T_004",250, "T_003",309, "T_002",191, "T_001",557, "T_007_new",52, "T_006_new",128, "T_004_new",128, "T_003_new",115, "T_002_new",112),nrow = 11,byrow = T )) colnames(nodes1)<-c("Id","height") nodeID<-rownames(nodes1) names(nodeID)<-nodes1$Id

names(links) = c("source", "target", "value")

links1$source<-as.numeric(unname(nodeID[links1$N1]))-1 links1$target<-as.numeric(unname(nodeID[links1$N2]))-1 nodes1$name<-as.numeric(unname(nodeID[nodes1$Id]))-1 nodes1$ngroup<-gsub("_new","",nodes1$Id) color_scale1<-as.data.frame(nodes1) color_scale1$range<-brewer.pal(n=nrow(color_scale1),name='Paired')[as.numeric(factor(color_scale1$ngroup))] color_scale1$domain=color_scale1$Id

sankeyNetwork(Links = links1, Nodes = nodes1,#nodePadding=100, Source = "source", Target = "target", Value = "value", LinkGroup = 'N1',NodeGroup = 'ngroup', NodeID = "Id",#height = 200, fontSize= 12, nodeWidth = 10,sinksRight = T,iterations = 0)

cjyetman commented 4 years ago

No, sorry. Sankey plots are intended to show flows between nodes, so the node size always reflects the amount of flow out of it. You could of course write a custom D3 visualization in JavaScript to do what you want, but that's out of scope for this package/function.

LuffyLuffy commented 4 years ago

Yes, but it's also have some meaning to show the total source. Thanks, I'll try to learn some D3 javascript.

cjyetman commented 4 years ago

You could sort of force a source node to have a specific size by adding a source node before it that flows in the value that you want it to start with...

library(tidyverse)
library(networkD3)

links <- tibble::tribble(
  ~source, ~target, ~value,
  "a",     "b",    200,
  "b",     "c",     25,
  "b",     "d",     25,
  "b",     "e",     25,
  "b",     "f",     25
)

nodes <- data.frame(
  name = unique(c(as.character(links$source), as.character(links$target))))

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

sankeyNetwork(Links = links, Nodes = nodes,
              Source = "IDsource", Target = "IDtarget",
              Value = "value", NodeID = "name", 
              sinksRight=FALSE)
Screenshot 2019-12-15 at 12 15 37
LuffyLuffy commented 4 years ago

Exactly, I only need to calculate the left part and make a self link, and set the link color as white or just keep it ! Thanks!