d3.js如何从数组绘制堆叠的水平条? [英] d3.js How to draw stacked horziontal bars from array?

查看:77
本文介绍了d3.js如何从数组绘制堆叠的水平条?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试获取此堆叠的水平条的特定示例,使其在浏览器中独立运行: http://tributary .io/inlet/4966973 .我知道以前曾有人问过有关此代码的问题,但从未将其作为工作示例.当我在浏览器中(即朝贡站点外部)运行以下代码时,出现此错误: 错误:属性transform ="translate(0,NaN)"的值无效".为什么代码无法在浏览器中运行?是否缺少某些元素?

I am trying to get this particular example of stacked horizontal bars to work in a browser standalone: http://tributary.io/inlet/4966973 . I know a question has been asked about this code before, but it was never taken to a working example. When I run the code below in a browser (ie outside the tributary site), I get this error: 'Error: Invalid value for attribute transform="translate(0,NaN)"'. Why won't the code run in a browser? Is there some element missing?

<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<div id="container">

<section id="display" style="width: 1038px;">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:xlink="http://www.w3.org/1999/xlink" class="tributary_svg" width="677" height="533"></svg></section>
</div>
<script>
/*modified from Mike Bostock at http://bl.ocks.org/3943967 */

var data = [
{"key":"FL", "pop1":3000, "pop2":4000, "pop3":5000},
{"key":"CA", "pop1":3000, "pop2":3000, "pop3":3000},
{"key":"NY", "pop1":12000, "pop2":5000, "pop3":13000},
{"key":"NC", "pop1":8000, "pop2":21000, "pop3":11000},
{"key":"SC", "pop1":30000, "pop2":12000, "pop3":8000},
{"key":"AZ", "pop1":26614, "pop2":6944, "pop3":30778},
{"key":"TX", "pop1":8000, "pop2":12088, "pop3":20000}
];

var n = 3, // number of layers
m = data.length, // number of samples per layer
stack = d3.layout.stack(),
labels = data.map(function(d) {return d.key;}),

//go through each layer (pop1, pop2 etc, that's the range(n) part)
//then go through each object in data and pull out that objects's population data
//and put it into an array where x is the index and y is the number
layers = stack(d3.range(n).map(function(d) { 
            var a = [];
            for (var i = 0; i < m; ++i) {
                a[i] = {x: i, y: data[i]['pop' + (d+1)]};  
            }
            return a;
         })),

//the largest single layer
yGroupMax = d3.max(layers, function(layer) { return d3.max(layer,      function(d) { return d.y; }); }),
//the largest stack
yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });

var margin = {top: 40, right: 10, bottom: 20, left: 50},
width = 677 - margin.left - margin.right,
height = 533 - margin.top - margin.bottom;

var y = d3.scale.ordinal()
.domain(d3.range(m))
.rangeRoundBands([2, height], .08);

var x = d3.scale.linear()
.domain([0, yStackMax])
.range([0, width]);

var color = d3.scale.linear()
.domain([0, n - 1])
.range(["#aad", "#556"]);

var xx = margin.top;

var svg = d3.select("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 layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) { return color(i); });

 layer.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("y", function(d) { return y(d.x); })
.attr("x", function(d) { return x(d.y0); })
.attr("height", y.rangeBand())
.attr("width", function(d) { return x(d.y); });

var yAxis = d3.svg.axis()
.scale(y)
.tickSize(1)
.tickPadding(6)
.tickValues(labels)
.orient("left");

svg.append("g")
.attr("class", "y axis")
.call(yAxis);
</script

    </body>

推荐答案

您发现自己是一个相当有趣的问题,它可能与我们发现的另一个类似问题有关.参见此答案.

You've found yourself a rather interesting issue, and it may be related to what we found with another similar problem. See this answer.

为检验该理论,这是您的代码副本,并粘贴到Stack Overflow片段中.第一个正在使用d3 3.2.8,并且似乎可以正常工作.

To test the theory, here is your code copy and pasted into Stack Overflow snippets. The first one is using d3 3.2.8, and seems to work properly.

/*modified from Mike Bostock at http://bl.ocks.org/3943967 */

var data = [
{"key":"FL", "pop1":3000, "pop2":4000, "pop3":5000},
{"key":"CA", "pop1":3000, "pop2":3000, "pop3":3000},
{"key":"NY", "pop1":12000, "pop2":5000, "pop3":13000},
{"key":"NC", "pop1":8000, "pop2":21000, "pop3":11000},
{"key":"SC", "pop1":30000, "pop2":12000, "pop3":8000},
{"key":"AZ", "pop1":26614, "pop2":6944, "pop3":30778},
{"key":"TX", "pop1":8000, "pop2":12088, "pop3":20000}
];

var n = 3, // number of layers
m = data.length, // number of samples per layer
stack = d3.layout.stack(),
labels = data.map(function(d) {return d.key;}),

//go through each layer (pop1, pop2 etc, that's the range(n) part)
//then go through each object in data and pull out that objects's population data
//and put it into an array where x is the index and y is the number
layers = stack(d3.range(n).map(function(d) { 
            var a = [];
            for (var i = 0; i < m; ++i) {
                a[i] = {x: i, y: data[i]['pop' + (d+1)]};  
            }
            return a;
         })),

//the largest single layer
yGroupMax = d3.max(layers, function(layer) { return d3.max(layer,      function(d) { return d.y; }); }),
//the largest stack
yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });

var margin = {top: 40, right: 10, bottom: 20, left: 50},
width = 677 - margin.left - margin.right,
height = 533 - margin.top - margin.bottom;

var y = d3.scale.ordinal()
.domain(d3.range(m))
.rangeRoundBands([2, height], .08);

var x = d3.scale.linear()
.domain([0, yStackMax])
.range([0, width]);

var color = d3.scale.linear()
.domain([0, n - 1])
.range(["#aad", "#556"]);

var xx = margin.top;

var svg = d3.select("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 layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) { return color(i); });

 layer.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("y", function(d) { return y(d.x); })
.attr("x", function(d) { return x(d.y0); })
.attr("height", y.rangeBand())
.attr("width", function(d) { return x(d.y); });

var yAxis = d3.svg.axis()
.scale(y)
.tickSize(1)
.tickPadding(6)
.tickValues(labels)
.orient("left");

svg.append("g")
.attr("class", "y axis")
.call(yAxis);

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.2.8/d3.min.js"></script>
<div id="container">

<section id="display" style="width: 1038px;">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:xlink="http://www.w3.org/1999/xlink" class="tributary_svg" width="677" height="533"></svg></section>
</div>

再次出现,这次使用d3 3.4.11

Here it is again, this time using d3 3.4.11

/*modified from Mike Bostock at http://bl.ocks.org/3943967 */

var data = [
{"key":"FL", "pop1":3000, "pop2":4000, "pop3":5000},
{"key":"CA", "pop1":3000, "pop2":3000, "pop3":3000},
{"key":"NY", "pop1":12000, "pop2":5000, "pop3":13000},
{"key":"NC", "pop1":8000, "pop2":21000, "pop3":11000},
{"key":"SC", "pop1":30000, "pop2":12000, "pop3":8000},
{"key":"AZ", "pop1":26614, "pop2":6944, "pop3":30778},
{"key":"TX", "pop1":8000, "pop2":12088, "pop3":20000}
];

var n = 3, // number of layers
m = data.length, // number of samples per layer
stack = d3.layout.stack(),
labels = data.map(function(d) {return d.key;}),

//go through each layer (pop1, pop2 etc, that's the range(n) part)
//then go through each object in data and pull out that objects's population data
//and put it into an array where x is the index and y is the number
layers = stack(d3.range(n).map(function(d) { 
            var a = [];
            for (var i = 0; i < m; ++i) {
                a[i] = {x: i, y: data[i]['pop' + (d+1)]};  
            }
            return a;
         })),

//the largest single layer
yGroupMax = d3.max(layers, function(layer) { return d3.max(layer,      function(d) { return d.y; }); }),
//the largest stack
yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });

var margin = {top: 40, right: 10, bottom: 20, left: 50},
width = 677 - margin.left - margin.right,
height = 533 - margin.top - margin.bottom;

var y = d3.scale.ordinal()
.domain(d3.range(m))
.rangeRoundBands([2, height], .08);

var x = d3.scale.linear()
.domain([0, yStackMax])
.range([0, width]);

var color = d3.scale.linear()
.domain([0, n - 1])
.range(["#aad", "#556"]);

var xx = margin.top;

var svg = d3.select("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 layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) { return color(i); });

 layer.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("y", function(d) { return y(d.x); })
.attr("x", function(d) { return x(d.y0); })
.attr("height", y.rangeBand())
.attr("width", function(d) { return x(d.y); });

var yAxis = d3.svg.axis()
.scale(y)
.tickSize(1)
.tickPadding(6)
.tickValues(labels)
.orient("left");

svg.append("g")
.attr("class", "y axis")
.call(yAxis);

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="container">

<section id="display" style="width: 1038px;">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:xlink="http://www.w3.org/1999/xlink" class="tributary_svg" width="677" height="533"></svg></section>
</div>

两个代码段之间的javascript是相同的,您可以清楚地看到行为是不同的.因此,简而言之,d3版本处理y轴(尤其是附加到该轴的域)的方式有所不同.

The javascript is identical between the two snippets, and you can plainly see that the behavior is different. So, in short, there is a difference between the way that d3 versions handle the y axis (and in particular the domain attached to the axis).

这是在d3 3.4.11中修复的版本

Here's a version that fixes things up in d3 3.4.11

/*modified from Mike Bostock at http://bl.ocks.org/3943967 */

var data = [
{"key":"FL", "pop1":3000, "pop2":4000, "pop3":5000},
{"key":"CA", "pop1":3000, "pop2":3000, "pop3":3000},
{"key":"NY", "pop1":12000, "pop2":5000, "pop3":13000},
{"key":"NC", "pop1":8000, "pop2":21000, "pop3":11000},
{"key":"SC", "pop1":30000, "pop2":12000, "pop3":8000},
{"key":"AZ", "pop1":26614, "pop2":6944, "pop3":30778},
{"key":"TX", "pop1":8000, "pop2":12088, "pop3":20000}
];

var n = 3, // number of layers
m = data.length, // number of samples per layer
stack = d3.layout.stack(),
labels = data.map(function(d) {return d.key;}),

//go through each layer (pop1, pop2 etc, that's the range(n) part)
//then go through each object in data and pull out that objects's population data
//and put it into an array where x is the index and y is the number
layers = stack(d3.range(n).map(function(d) { 
            var a = [];
            for (var i = 0; i < m; ++i) {
                //a[i] = {x: i, y: data[i]['pop' + (d+1)]};  
                a[i] = {x: data[i].key, y: data[i]['pop' + (d+1)]};  
            }
            return a;
         })),

//the largest single layer
yGroupMax = d3.max(layers, function(layer) { return d3.max(layer,      function(d) { return d.y; }); }),
//the largest stack
yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });

var margin = {top: 40, right: 10, bottom: 20, left: 50},
width = 677 - margin.left - margin.right,
height = 533 - margin.top - margin.bottom;

var y = d3.scale.ordinal()
//.domain(d3.range(m))
.domain(labels)
.rangeRoundBands([2, height], .08);

var x = d3.scale.linear()
.domain([0, yStackMax])
.range([0, width]);

var color = d3.scale.linear()
.domain([0, n - 1])
.range(["#aad", "#556"]);

var xx = margin.top;

var svg = d3.select("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 layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) { return color(i); });

 layer.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("y", function(d) { return y(d.x); })
.attr("x", function(d) { return x(d.y0); })
.attr("height", y.rangeBand())
.attr("width", function(d) { return x(d.y); });

var yAxis = d3.svg.axis()
.scale(y)
.tickSize(1)
.tickPadding(6)
//.tickValues(labels)
.orient("left");

svg.append("g")
.attr("class", "y axis")
.call(yAxis);

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="container">

<section id="display" style="width: 1038px;">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:xlink="http://www.w3.org/1999/xlink" class="tributary_svg" width="677" height="533"></svg></section>
</div>

此版本有三个区别:

  1. 更新引用堆栈中x值的方式

layers = stack(d3.range(n).map(function(d) { 
    var a = [];
        for (var i = 0; i < m; ++i) {
            //a[i] = {x: i, y: data[i]['pop' + (d+1)]};  
            a[i] = {x: data[i].key, y: data[i]['pop' + (d+1)]};  
        }
        return a;
 })),

  • 更改y标度的域

  • Change the domain for the y scale

    var y = d3.scale.ordinal()
                    //.domain(d3.range(m))
                   .domain(labels)
                   .rangeRoundBands([2, height], .08);
    

  • 从y轴删除.tickValues调用.它将使用刻度的domain.

  • Remove the .tickValues call from the y axis. It will use the domain of the scale instead.

    var yAxis = d3.svg.axis()
                      .scale(y)
                      .tickSize(1)
                      .tickPadding(6)
                      //.tickValues(labels)
                      .orient("left");
    

  • 您可以看到此新版本在d3 3.4.11中正常工作.

    You can see that this new version works properly in d3 3.4.11.

    以下是使用d3 3.2.8的固定版本:

    Here is the fixed version using d3 3.2.8:

    /*modified from Mike Bostock at http://bl.ocks.org/3943967 */
    
    var data = [
    {"key":"FL", "pop1":3000, "pop2":4000, "pop3":5000},
    {"key":"CA", "pop1":3000, "pop2":3000, "pop3":3000},
    {"key":"NY", "pop1":12000, "pop2":5000, "pop3":13000},
    {"key":"NC", "pop1":8000, "pop2":21000, "pop3":11000},
    {"key":"SC", "pop1":30000, "pop2":12000, "pop3":8000},
    {"key":"AZ", "pop1":26614, "pop2":6944, "pop3":30778},
    {"key":"TX", "pop1":8000, "pop2":12088, "pop3":20000}
    ];
    
    var n = 3, // number of layers
    m = data.length, // number of samples per layer
    stack = d3.layout.stack(),
    labels = data.map(function(d) {return d.key;}),
    
    //go through each layer (pop1, pop2 etc, that's the range(n) part)
    //then go through each object in data and pull out that objects's population data
    //and put it into an array where x is the index and y is the number
    layers = stack(d3.range(n).map(function(d) { 
                var a = [];
                for (var i = 0; i < m; ++i) {
                    //a[i] = {x: i, y: data[i]['pop' + (d+1)]};  
                    a[i] = {x: data[i].key, y: data[i]['pop' + (d+1)]};  
                }
                return a;
             })),
    
    //the largest single layer
    yGroupMax = d3.max(layers, function(layer) { return d3.max(layer,      function(d) { return d.y; }); }),
    //the largest stack
    yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });
    
    var margin = {top: 40, right: 10, bottom: 20, left: 50},
    width = 677 - margin.left - margin.right,
    height = 533 - margin.top - margin.bottom;
    
    var y = d3.scale.ordinal()
    //.domain(d3.range(m))
    .domain(labels)
    .rangeRoundBands([2, height], .08);
    
    var x = d3.scale.linear()
    .domain([0, yStackMax])
    .range([0, width]);
    
    var color = d3.scale.linear()
    .domain([0, n - 1])
    .range(["#aad", "#556"]);
    
    var xx = margin.top;
    
    var svg = d3.select("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 layer = svg.selectAll(".layer")
    .data(layers)
    .enter().append("g")
    .attr("class", "layer")
    .style("fill", function(d, i) { return color(i); });
    
     layer.selectAll("rect")
    .data(function(d) { return d; })
    .enter().append("rect")
    .attr("y", function(d) { return y(d.x); })
    .attr("x", function(d) { return x(d.y0); })
    .attr("height", y.rangeBand())
    .attr("width", function(d) { return x(d.y); });
    
    var yAxis = d3.svg.axis()
    .scale(y)
    .tickSize(1)
    .tickPadding(6)
    //.tickValues(labels)
    .orient("left");
    
    svg.append("g")
    .attr("class", "y axis")
    .call(yAxis);

    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.2.8/d3.min.js"></script>
    <div id="container">
    
    <section id="display" style="width: 1038px;">
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:xlink="http://www.w3.org/1999/xlink" class="tributary_svg" width="677" height="533"></svg></section>
    </div>

    在d3 3.2.8中它似乎也可以正常工作,因此它应该可以解决您的问题.

    It also seems to work OK in d3 3.2.8, so it should solve your issues.

    这是一个有趣的诊断,我唯一能想到的是tributary.io基于较旧的d3版本,其中轴/域交互工作正常(尽管已中断),并且在您的独立版本中,您所引用的是最新版本,该版本已修复了所有问题(由于您的代码依赖于此,导致可视化效果不佳).

    It's a fun one to diagnose, and the only thing I can think of is that tributary.io is based on an older version of d3, where the axis/domain interaction was working OK (albeit broken) and in your standalone version, you were referencing the latest version, which has fixed what ever the issue was (resulting in a broken visualisation, since your code depended on it).

    注意:弄清楚这一点很有趣,但是我在

    Note: it was fun figuring this one out, but without the additional comments in my answer on 26029141 it would have been near impossible to diagnose.

    这篇关于d3.js如何从数组绘制堆叠的水平条?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆