44465

d3.js How to draw stacked horziontal bars from array?

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>

Answer1:

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

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.

<div class="snippet" data-lang="js" data-hide="true" data-console="false" data-babel="false"> <div class="snippet-code snippet-currently-hidden">

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


Here it is again, this time using d3 3.4.11

<div class="snippet" data-lang="js" data-hide="true" data-console="false" data-babel="false"> <div class="snippet-code snippet-currently-hidden">

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


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

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

<div class="snippet" data-lang="js" data-hide="true" data-console="false" data-babel="false"> <div class="snippet-code snippet-currently-hidden">

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


There are three differences in this version:

<ol> <li>

Update the way the x value in the stack is referenced

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; })), </li> <li>

Change the domain for the y scale

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

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"); </li> </ol>

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

Here is the fixed version using d3 3.2.8:

<div class="snippet" data-lang="js" data-hide="true" data-console="false" data-babel="false"> <div class="snippet-code snippet-currently-hidden">

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


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

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.

Recommend

  • Plotting point estimates with error bars for different groups
  • What to pass for TypeScript optional parameter when there is no exact value to specify, 'undefi
  • How make this XML format output using PL SQL
  • Amazon RDS replica
  • How to make mobile devices not load an external JS
  • error when using poplib to get email
  • running FFTW on GPU vs using CUFFT
  • Camel route to filter out the auto response emails
  • Hide option in select based on other select value
  • Incorrect output via fftw_mpi_r2c_2d and fftw_mpi_c2r_2d
  • running FFTW on GPU vs using CUFFT
  • How to install the guess_language package in Python
  • Pass session variable to new page when link is clicked
  • Does Index Using a Logical Expression work for strings? [duplicate]
  • Determine Filesize of Email Attachment Before Downloading
  • PHP - Setting inherited static property will also set it in other classes inheriting it
  • How can you retrieve a full nested document in Solr?
  • Put elements of a 1D vector into a 3D matrix using another matrix of positions
  • Wordpress Mobile - Change Part of the URL Instead of Redirecting to the Front Page?
  • Remove the signature from a MIME encoded email with PHP
  • Filter Values of Current Week with XQuery
  • other option at place of tag
  • Can I create EventListener in AS3 from one Object and remove it from another Object?
  • RS-232 communication using an Arduino Duemilanove and the Cutedigi RS-232 interface
  • Aspect not executed in Spring
  • How to create a generic Task.ContinueWith extension method
  • pure javascript dom dynamic insert, update and delete
  • SSLRead() return error -9806/15958)
  • What distributed message queues support millions of queues?
  • Why are “sc.addFile” and “spark-submit --files” not distributing a local file to all workers?
  • Python getting common name from URL using ssl.getpeercert()
  • pickle.PicklingError: args[0] from __newobj__ args has the wrong class with hadoop python
  • Flash radiobutton: how do I get the selected radiobutton?
  • Spring: No transaction manager has been configured
  • python script hangs on input method when running spark
  • does jqgrid support a multiple checkbox list for editing
  • Why value captured by reference in lambda is broken? [duplicate]
  • Why joiner is not used after Sequence generator or Update statergy
  • Recursive/Hierarchical Query Using Postgres
  • UserPrincipal.Current returns apppool on IIS