Sankey diagram transition Sankey diagram transition json json

Sankey diagram transition


This is possible. Here's one approach using a csv file. Working sankey here: https://www.betterment.com/resources/investment-strategy/portfolio-management/portfolio-diversification/

  1. Define a global array outside of your d3.csv call.

    var portfolioValues = [];
  2. When parsing the csv to create the node/link structure, push values to your global array.

    d3.csv("etf-geo.csv", function(error, data) {    graph = {"nodes" : [], "links" : []};    data.forEach(function (d, i) {        var item = { source: d.source, target: d.target, values: [] };        for (var j=0; j < 101; j++) {            item.values.push(d['value'+j.toString()]);        }        portfolioValues.push(item);        graph.nodes.push({ "name": d.source });        graph.nodes.push({ "name": d.target });        graph.links.push({            source: portfolioValues[i].source,            target: portfolioValues[i].target,            value: portfolioValues[i].values[startingAllocation]        });    });//this handy little function returns only the distinct / unique nodesgraph.nodes = d3.keys(    d3.nest()        .key(function (d) { return d.name; })        .map(graph.nodes));// it appears d3 with force layout wants a numeric source and target// so loop through each link replacing the text with its index from nodegraph.links.forEach(function (d, i) {    graph.links[i].source = graph.nodes.indexOf(graph.links[i].source);    graph.links[i].target = graph.nodes.indexOf(graph.links[i].target);    portfolioValues[i].source = graph.links[i].source;    portfolioValues[i].target = graph.links[i].target;});// now loop through each nodes to make nodes an array of objects// rather than an array of stringsgraph.nodes.forEach(function (d, i) {    graph.nodes[i] = { "name": d };});// construct sankeysankey    .nodes(graph.nodes)    .links(graph.links)    .layout();
  3. Listen for a change and pass user input to your update function.

    $(".sankey-slider").bind("slider:changed", function (event, data) {slideValue = data.value;updateData(parseInt(slideValue)); });
  4. Create a temporary array and retrieve the correct values from the global array. Call the sankey functions to recalculate the layout.

        var newLinks = [];    portfolioValues.forEach(function(p, i) {        newLinks.push({          source: p.source,          target: p.target,          value: p.values[allocation]        });    });    graph.links = newLinks;    sankey    .nodes(graph.nodes)    .links(graph.links)    .size([width, height])    .layout();
  5. Select each element that needs to be changed and pass the new data values.

    d3.selectAll(".link")  .data(graph.links)  .attr("d", path)  .attr("id", function(d,i){    d.id = i;    return "link-"+i;  })  .style("stroke-width", function(d) { return Math.max(1, d.dy); })  .sort(function(a, b) { return b.dy - a.dy; });d3.selectAll(".node").attr("transform", function(d) {  return "translate(" + d.x + "," + d.y + ")"; });d3.selectAll("rect").attr("height", function(d) { return d.dy; }).on("mouseover",highlight_node_links).on("mouseout",onNodeMouseout);

Working sankey here:https://www.betterment.com/resources/investment-strategy/portfolio-management/portfolio-diversification/


Since the automatic positioning of nodes includes a part which tries to minimize link distance in a connected graph which is an np optimization problem, any kind of optimizer can potentially jump from one minimum to another leading to a jump in layout. So a guaranteed smooth transition wont be possible.

The closest possible solution would probably be to linearly interpolate between the two input data sets and thereby generate a series of graphs which (depending on the data) more or less smoothly transition from one two the other.

Hope this helps.