1

I would like to produce a Sankey-diagram using the networkD3 package in R that has the functionality as described in this question, and the "highlight_node_links" function in the answer:

d3 Sankey - Highlight all connected paths from start to end

I'm a beginner with both R and Javascript and my problem is that I cannot make the above javascript function work with my R code. I have checked Highlight all connected paths from start to end in Sankey graph using R question too, but unfortunately I couldn't solve it based on the answer there either. I do understand I need to use htmlwidgets::onRender, but cannot figure out how.

A small sample data and the network generated from it:

links = data.frame(source = c("me",  "you", "she", "p1",  "p1",  "p2",  "p2"), target = c("p1",  "p2",  "p1",  "p2",  "b1",  "b1",  "b2"), weight = c(20, 10, 30, 40, 60, 50, 50)) 
nodes <- data.frame(name = c(links$source, links$target) %>% unique()) 
links$IDsource = match(links$source, nodes$name)-1 
links$IDtarget = match(links$target, nodes$name)-1

sn <- sankeyNetwork(Links = links, 
                      Nodes = nodes,
                      Source = "IDsource", 
                      Target = "IDtarget",
                      Value = "weight",  
                      NodeID = "name",  
                      sinksRight = TRUE)

And the way I tried to include the highlight_node_links function (that I defined earlier unchanged from the above link):

    htmlwidgets::onRender(
  sn,
  '
        function(el, x) {
          var link = d3.selectAll(".link");
         var node = d3.selectAll(".node");
        node.on("mousedown.drag", null);
        node.on("click",highlight_node_links);

  function highlight_node_links(node,i){

  var remainingNodes=[],
  nextNodes=[];

  var stroke_opacity = 0;
  if( d3.select(this).attr("data-clicked") == "1" ){
  d3.select(this).attr("data-clicked","0");
  stroke_opacity = 0.2;
  }else{
  d3.select(this).attr("data-clicked","1");
  stroke_opacity = 0.5;
  }

  var traverse = [{
  linkType : "sourceLinks",
  nodeType : "target"
  },{
  linkType : "targetLinks",
  nodeType : "source"
  }];

  traverse.forEach(function(step){
  node[step.linkType].forEach(function(link) {
  remainingNodes.push(link[step.nodeType]);
  highlight_link(link.id, stroke_opacity);
  });

  while (remainingNodes.length) {
  nextNodes = [];
  remainingNodes.forEach(function(node) {
  node[step.linkType].forEach(function(link) {
  nextNodes.push(link[step.nodeType]);
  highlight_link(link.id, stroke_opacity);
  });
  });
  remainingNodes = nextNodes;
  }
  });
  }

  function highlight_link(id,opacity){
  d3.select("#link-"+id).style("stroke-opacity", opacity)
         }}'
)
CJ Yetman
  • 8,373
  • 2
  • 24
  • 56
gombi
  • 23
  • 4

1 Answers1

1

When I run your code, I get the warning message...

Warning message:
It looks like Source/Target is not zero-indexed. This is required in JavaScript
 and so your plot may not render.

which tells you what the root of the problem is... your links and nodes data frames are not properly created.

You can easily resolve this by adding stringsAsFactors = FALSE as a parameter to the data.frame command that you use to create the links data frame.

Working example...

library(networkD3)
library(htmlwidgets)
library(dplyr)

links = data.frame(
  source = c("me",  "you", "she", "p1",  "p1",  "p2",  "p2"),
  target = c("p1",  "p2",  "p1",  "p2",  "b1",  "b1",  "b2"),
  weight = c(20, 10, 30, 40, 60, 50, 50),
  stringsAsFactors = FALSE
)
nodes <-
  data.frame(name = c(links$source, links$target) %>% unique())
links$IDsource = match(links$source, nodes$name) - 1
links$IDtarget = match(links$target, nodes$name) - 1

sn <- sankeyNetwork(
  Links = links,
  Nodes = nodes,
  Source = "IDsource",
  Target = "IDtarget",
  Value = "weight",
  NodeID = "name",
  sinksRight = TRUE
)

htmlwidgets::onRender(
  sn,
  '
        function(el, x) {
          var link = d3.selectAll(".link");
         var node = d3.selectAll(".node");
        node.on("mousedown.drag", null);
        node.on("click",highlight_node_links)}'
)

enter image description here

CJ Yetman
  • 8,373
  • 2
  • 24
  • 56
  • Thanks for your answer. I'm sorry, I created the sample data wrong, my original data does not have this problem. I copy-pasted your code, and it still does not work for me. It plots the Sankey-diagram, but does not add the new functionality. I guess this means the problem is elsewhere, and the only thing I can think of is my definition of the 'hightlight_node_links' function. I do that the following way: `highlight_node_links <- function() {'function highlight_node_links(node,i){` and then the rest of the function. – gombi Dec 19 '18 at 13:09
  • I would... 1. close/quit RStudio completely and restart it... 2. make sure all of the relevant packages are up to date. My example should work if you copy-paste it and do not make any modifications to it. You may also want to try opening it in a browser window to see if the problem is related to viewing it in RStudio's built-in browser. – CJ Yetman Dec 19 '18 at 13:16
  • The way you specify `highlight_node_links` in your above comment is very strange. I can't see how that would possibly work. You are creating a string object that has a function that returns a string representation of another function with the same name (presumably in JavaScript), which would not exist or be called in the JavaScript environment created by the htmlwidget. That most certainly will not work. – CJ Yetman Dec 19 '18 at 13:21
  • I tried all your suggestions, restarted rstudio, packages are up-to date, and it also does not work for me in a browser. I also put the `highlight_node_links` definition within function(el, x), right after `node.on("click",highlight_node_links)` . I'll edit my original post to show you where. Should it work? If not, what else did I do wrong? I still don't have any warning or error messages. – gombi Dec 19 '18 at 15:12
  • Something must be wrong with your installation/environment. Maybe try it on a different computer. – CJ Yetman Dec 19 '18 at 15:17
  • I understand better now what you're trying to do now that I see the whole function. In short, it's not going to work... not without additional modifications of the underlying JavaScript code. That function was written specifically for a different implementation of a sankey diagram, which has critical pieces that are not present in the sankey code that networkD3 uses. You cannot simply drop-in that function and have it work as expected. – CJ Yetman Dec 19 '18 at 19:07
  • OK, thanks for having another look. Can you please tell me then what I am supposed to change or do different for it to work the way it works for you? – gombi Dec 27 '18 at 09:37
  • The “highlighting all links” never worked for me. Originally you hadn’t posted the full function, so I assumed it would be available and be working correctly. One could likely replicate the behavior by rewriting the function in the context of networkD3’s sankey code, if they had sufficient knowledge and experience with JavaScript and D3. – CJ Yetman Dec 27 '18 at 11:45