桑基图转换

问题描述 投票:0回答:2

我想知道是否有一种简单的方法来修改 Sankey 图示例,以便顺利过渡到新数据。例如,假设我有不同的数据文件(energy1.json、energy2.json ...),d3 如何为第一个数据集绘制桑基图,然后等待并稍后重新排列框配置以表示第二个数据集?

json d3.js transition sankey-diagram
2个回答
15
投票

这是可能的。这是使用 csv 文件的一种方法。

  1. 在 d3.csv 调用之外定义一个全局数组。

     var portfolioValues = [];
    
  2. 解析 csv 以创建节点/链接结构时,将值推送到全局数组。

     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 nodes
     graph.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 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
     sankey
         .nodes(graph.nodes)
         .links(graph.links)
         .layout();
    
  3. 监听更改并将用户输入传递到更新函数。

     $(".sankey-slider").bind("slider:changed", function (event, data) {
    
     slideValue = data.value;
    
     updateData(parseInt(slideValue));
    
      });
    
  4. 创建一个临时数组并从全局数组中检索正确的值。调用 sankey 函数重新计算布局。

         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. 选择需要更改的每个元素并传递新的数据值。

     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);
    

2
投票

由于节点的自动定位包括尝试最小化连通图中的链接距离的部分,这是一个 np 优化问题,因此任何类型的优化器都可能从一个最小值跳转到另一个最小值,从而导致布局跳转。因此不可能保证平稳过渡。

最接近的可能解决方案可能是在两个输入数据集之间进行线性插值,从而生成一系列图表,这些图表(取决于数据)或多或少平滑地从两个输入数据集过渡。

希望这有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.