使用D3.js Sankey布局解析数据时出现问题

时间:2014-04-21 22:21:32

标签: d3.js sankey-diagram

我正在寻找一些关于我正在创建的Sankey图表出错的提示。我正在描绘食物消费随时间的变化,并使用Sankey布局来想象这些值在四十年内如何变化。

bl.ock and small dataset are here。相关代码:

var margin = {top: 1, right: 1, bottom: 6, left: 1},
    width = 1260 - margin.left - margin.right,
    height = 1000 - margin.top - margin.bottom;

var formatNumber = d3.format(",.0f"),
    format = function(d) { return formatNumber(d) + " TWh"; },
    color = d3.scale.category20();

var svg = d3.select("#chart").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var sankey = d3.sankey()
    .nodeWidth(15)
    .nodePadding(10)
    .size([width, height]);

var path = sankey.link();


// ========================== Prepare data ==========================

queue()
    .defer(d3.csv, "grains.csv")
    .await(ready);


// ========================== Start viz ==========================

function ready(error, csv_data) {

  nodes = [];
  edges = [];
  nodesArray = [];

  // Scales
  yearScale = d3.scale.linear().domain([1640,1688]).range([20,width -20]);
  radiusScale = d3.scale.linear().domain([0,300]).range([2,12]).clamp(true);
  chargeScale = d3.scale.linear().domain([0,100]).range([0,-100]).clamp(true);
  uniqueValues = d3.set(nodesArray.map(function(d) {return d.name})).values();
  colorScale = d3.scale.category20b(uniqueValues);
  sortScale = d3.scale.ordinal().domain(uniqueValues).rangePoints([-0.001,.001]);

  // Create a JSON link array
  // This creates unique nodes for each item and its corresponding date.
  // For example, nodes are rendered as "peas-1640," "peas-1641," etc.
  csv_data.forEach(function(link) {
      key = link.translation + '-' + link.date;
      link.source = nodes[key] || (nodes[key] = {name: link.translation, date: link.date, origX: yearScale(parseInt(link.date)), value: link.value || 0});
  });

// Build the edgesArray array
// This creates the edgesArray to correspond with unique nodes. We're telling
// items and dates to remain together. So, the code below tells the graph
// layout that `1641` is preceded by `1640` and followed by `1642`, etc.
var y = "→";
  for (x in nodes) {
    nodesArray.push(nodes[x])
    if(nodes[y]) {
      nodes[y].date = parseInt(nodes[y].date);
      if (nodes[y].name == nodes[x].name) {
        var newLink = {source:nodes[y], target:nodes[x]}
        edges.push(newLink);
      }
    }
    y = x;
  }

  sankey
      .nodeWidth(10)
      .nodePadding(10)
      .size([1200, 1200])
      .nodes(nodesArray.filter(function(d,i) {return d.date < 1650}))
      .links(edges.filter(function(d,i) { return i < 50 && d.source.date < 1650 &&  d.target.date < 1650} )) // filtering to test a smaller data set
      .layout(32);

  var link = svg.append("g").selectAll(".link")
    .data(edges.filter(function(d,i) { return i < 50 && d.source.date < 1650 &&  d.target.date < 1650} )) // filtering to test a smaller data set
    .enter().append("path")
      .attr("class", "link")
      .attr("d", path)
      .style("stroke-width", function(d) { return Math.max(1, d.dy); })
      .sort(function(a, b) { return b.dy - a.dy; });

  link.append("title")
      .text(function(d) { return d.source.name + " → " + d.target.name + "\n" + format(d.value); });

  var node = svg.append("g").selectAll(".node")
      .data(nodesArray.filter(function(d,i) {return d.date < 1650}))  // filtering to test a smaller data set
    .enter().append("g")
      .attr("class", "node")
      .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
    .call(d3.behavior.drag()
      .origin(function(d) { return d; })
      .on("dragstart", function() { this.parentNode.appendChild(this); })
      .on("drag", dragmove));

  node.append("rect")
      .attr("height", function(d) { return d.dy; })
      .attr("width", sankey.nodeWidth())
      .style("fill", function(d) { return d.color = color(d.name.replace(/ .*/, "")); })
      .style("stroke", function(d) { return d3.rgb(d.color).darker(2); })
    .append("title")
      .text(function(d) { return d.name + "\n" + format(d.value); });

  node.append("text")
      .attr("x", -6)
      .attr("y", function(d) { return d.dy / 2; })
      .attr("dy", ".35em")
      .attr("text-anchor", "end")
      .attr("transform", null)
      .text(function(d) { return d.name; })
    .filter(function(d) { return d.x < width / 2; })
      .attr("x", 6 + sankey.nodeWidth())
      .attr("text-anchor", "start");

  function dragmove(d) {
    d3.select(this).attr("transform", "translate(" + d.x + "," + (d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))) + ")");
    sankey.relayout();
    link.attr("d", path);
  }
};

不幸的是,我发现了一个错误see in the bl.ockBoss suggested它可能是一个循环链接,但我有点不知所措。任何提示或建议?

编辑:为了清楚起见,我是这样的:

Stream graph from Density Design
Source

据我所知,我认为我正在构建节点和边缘。如果我们在控制台中查看nodes数组和edge数组:

Nodes array

Edges array

它不像通常的Sankey或冲积图,正如我经常看到的那样,它显示了物品的折叠和扩展。在我的例子中,日期,食品和价值在整个可视化的整个过程中都是单个流,但是根据给定年份的值进行调整大小/重新定位(如上面的示例图像)。

0 个答案:

没有答案