最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Large data set breaks d3 sankey diagram - Stack Overflow

programmeradmin2浏览0评论

I'm making a d3 Sankey diagram from the example in . This example works fine with a smaller data set. When I switched to using a larger data set, the visualization breaks. It looks like the problem is that the dy values bee negative.

In the console, the error is:

Error: <rect> attribute height: A negative value is not valid. ("-9.02557856272838")

The code it points to is:

node.append("rect")
  .attr("height", function(d) { return d.dy; })

This is perhaps because the plots are going off screen? I looked at using d3 scales, but I'm not sure how to implement them. Maybe something like this:

d3.scale.ordinal()
.domain(data.map(function(d) { return d.name; }))
.rangeRoundBands([0, height], .2);

Or maybe there's a way to shrink the visualization as the data set gets larger so that everything will fit in the container.

Here is my code:

I'm making a d3 Sankey diagram from the example in https://bl.ocks/d3noob/5028304. This example works fine with a smaller data set. When I switched to using a larger data set, the visualization breaks. It looks like the problem is that the dy values bee negative.

In the console, the error is:

Error: <rect> attribute height: A negative value is not valid. ("-9.02557856272838")

The code it points to is:

node.append("rect")
  .attr("height", function(d) { return d.dy; })

This is perhaps because the plots are going off screen? I looked at using d3 scales, but I'm not sure how to implement them. Maybe something like this:

d3.scale.ordinal()
.domain(data.map(function(d) { return d.name; }))
.rangeRoundBands([0, height], .2);

Or maybe there's a way to shrink the visualization as the data set gets larger so that everything will fit in the container.

Here is my code: https://plnkr.co/edit/hOjEBHhS7vfajD2wb8t9?p=preview

Share Improve this question asked Nov 13, 2016 at 21:43 kimlikimli 2791 gold badge6 silver badges20 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 6

With 945 nodes and 2463 links, there is no way this is going to fit in an 740-pixel-height container. Not only that, you have to ask yourself "how is this dataviz going to be useful to the reader with that huge amount of information?". But since that's none of my business, you can do a couple of things:

The first one, of course, is filtering your data. If that's not an option, you can increase the container height:

height = 3000 - margin.top - margin.bottom;

And reduce the padding of the nodes:

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

The result is in this plunker: https://plnkr.co/edit/Idb6ZROhq1kuZatbGtqg?p=preview

But if even that is not an option, you can change sankey.js code or, in a lazy solution, avoid negative numbers with this:

.attr("height", function(d) { return d.dy < 0 ? 0 : d.y; })

This being the result: https://plnkr.co/edit/tpaMpK5yXwYh9MEo8hgn?p=preview

I've encountered the same problem. I have graphs with 50 nodes and 50 links, but also graphs with 1200 nodes and 1200 links. The first thing I tried was to increase the canvas width and height. So I've increased the width based on the graph depth and the height based on the level where I had the most nodes.

However, I ended up in some edge cases graphs with either a too bigger width or too bigger height. So I ended up with a regeneration of the graph, until each node has a min height and until the space between the linked nodes is reasonable.

So here is what I did:

class SankeyChart {
   sankeyGenerator;
   graph;
   width = 1000;
   height = 1000;

   constructor(private nodes, private links) {
      this.sankeyGenerator = sankey()
        .extent([[10, 10], [this.width - 10, this.height - 10]])
      this.draw();
   }

 
  private draw() {
    this.graph = this.getGraph();
    this.drawLinks(this.graph.links); // method that draw the links
    this.drawNodes(this.graph.nodes); // method that draw the nodes
  }


  private getGraph() {
    const graph = this.sankeyGenerator({nodes: this.nodes, links: this.links});

    const increaseWidth = graph.links.some(l => Math.abs(l.source.y1 - l.target.y0) > 4 * Math.abs(l.source.x1 - l.target.x0));
    const increaseHeight = graph.nodes.some(n => n.y1 - n.y0 < 1);
    if (increaseHeight) {
      this.height += 1000;
    }
    if (increaseWidth) {
      this.width += 1000;
    }
    if (increaseHeight || increaseWidth) {
      this.sankeyGenerator.extent([[10, 10], [this.width - 10, this.height - 10]]);
      return this.getGraph();
    }
    return graph;
  }

}

Note that you might want to perform an extra filter on graph.nodes.some(n => n.y1 - n.y0 < 1);. I had a few cases where all the nodes had y1 - y0 > 1000, but there were 2 or 3 nodes where y1 - y0 < 0.00000001.

Also note that you might add a parameter to the class to increase the size of the container more than 1000px each time when getGraph() is called for big charts.

发布评论

评论列表(0)

  1. 暂无评论