Mermoz Mermoz - 8 months ago 72
JSON Question

Sankey diagram transition

I would like to know if there is an easy way to modify the Sankey diagram example so that there is smooth transition to new data. For example, imagine I have different datafiles (energy1.json, energy2.json ...) how could d3 plot a Sankey diagram for the first dataset, then waits and later on rearanges the boxes disposition to represent the second dataset?


This is possible. Here's one approach using a csv file. Working sankey here:

  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:, values: [] };
            for (var j=0; j < 101; j++) {
            graph.nodes.push({ "name": d.source });
            graph.nodes.push({ "name": });
                source: portfolioValues[i].source,
                target: portfolioValues[i].target,
                value: portfolioValues[i].values[startingAllocation]
    //this handy little function returns only the distinct / unique nodes
    graph.nodes = d3.keys(
            .key(function (d) { return; })
    // it appears d3 with force layout wants a numeric source and target
    // so loop through each link replacing the text with its index from node
    graph.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 strings
    graph.nodes.forEach(function (d, i) {
        graph.nodes[i] = { "name": d };
    // construct sankey
  3. Listen for a change and pass user input to your update function.

    $(".sankey-slider").bind("slider:changed", function (event, data) {
    slideValue = data.value;
  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) {
              source: p.source,
              value: p.values[allocation]
        graph.links = newLinks;
        .size([width, height])
  5. Select each element that needs to be changed and pass the new data values.

      .attr("d", path)
      .attr("id", function(d,i){ = 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 + ")"; });
    .attr("height", function(d) { return d.dy; })

Working sankey here: