d3.js如何从数组中绘制堆叠的水平条?

时间:2015-02-13 03:06:04

标签: javascript d3.js

我试图让这个堆叠水平条的特定示例在独立浏览器中工作:http://tributary.io/inlet/4966973。我知道之前有一个关于此代码的问题,但它从未被用于一个有效的例子。当我在浏览器中运行下面的代码时(即在支流站点之外),我收到此错误: '错误:属性转换的值无效="翻译(0,NaN)"'。为什么不在浏览器中运行代码?是否缺少某些元素?

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

1 个答案:

答案 0 :(得分:3)

你发现自己是一个相当有趣的问题,它可能与我们在另一个类似问题上发现的问题有关。请参阅this answer

为了测试理论,这里是您的代码副本并粘贴到Stack Overflow片段中。第一个是使用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)]};  
            }
            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

/*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轴的方式(特别是连接到轴的域)之间存在差异。

这是一个在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;
     })),
    
  2. 更改y比例

    的域名
    var y = d3.scale.ordinal()
                    //.domain(d3.range(m))
                   .domain(labels)
                   .rangeRoundBands([2, height], .08);
    
  3. 从y轴移除.tickValues来电。它将使用比例的domain

    var yAxis = d3.svg.axis()
                      .scale(y)
                      .tickSize(1)
                      .tickPadding(6)
                      //.tickValues(labels)
                      .orient("left");
    
  4. 您可以看到此新版本在d3 3.4.11中正常运行。

    这是使用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中正常工作,所以它应该可以解决你的问题。

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

    注意:把这一点搞得很有趣,但是在26029141的答案中没有附加评论,那么诊断几乎是不可能的。