Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

d3 how to make single stacked column chart

I'm trying to get d3 to draw a single stacked bar chart, like so:

<svg width = '500' height= '100'>
<rect x='0' y='0' width='224' height='30' fill='green'/>
<rect x='224' y='0' width='84' height='30' fill='blue'/>
<rect x='308' y='0' width='29' height='30' fill='darkgray'/>
<rect x='337' y='0' width='3' height='30' fill='#606060'/>
</svg>

As you can see, the x position starts at zero, then each subsequent x position is equal to the sum of the preceding widths.

So I'm trying to get d3 to draw something like that from an array called datavars:

var datavars = [224, 84, 29, 3];

... and to ensure that d3 assigns the correct width-value to the correct x-value, I created these variables:

var prev_width0 = 0;
var prev_width1 = datavars[0];
var prev_width2 =  datavars[0] +  datavars[1];
var prev_width3 =  datavars[0] +  datavars[1] +  datavars[2];

... and define the x value like so:

//create SVG element
var svg = d3.select('body')
.append('svg')
.attr('width', w)
.attr('height', h);

svg.selectAll('rect')
.data(datavars)
.enter()
.append('rect')
.attr('width', function(d){
    return d;})
               .attr('x',function(d, i){
               return 'prev_width'+i; })
.attr('y',0)
.attr('height', 30);

As you might have guessed, the strongly-indented function (based on the sum of the preceding widths, and defined in the prev_width variables) returns a string for each iteration (prev_width0, prev_width1, etc.) and not the values I thought I defined when I created the prev_width variables.

I'm obviously defining the variables incorrectly. Any idea how I can do this properly?

like image 228
prokaryote Avatar asked Sep 26 '22 20:09

prokaryote


1 Answers

JavaScript does not interpret the string "datavars1" as the variable datavars1. To interpret a string as a JavaScript variable you can use eval(). So change: return 'prev_width'+i; }) to return eval('prev_width'+i); }). Although a better idea might be to use an array instead such as:

var prev_width = [0,
                  datavars[0],
                  datavars[0] +  datavars[1],
                  datavars[0] +  datavars[1] +  datavars[2]
                 ];

...
   .attr('x',function(d, i){
   return prev_width[i]; })

Fiddle Example

You can do a similar thing with the colors:

var colors = ['green','blue','darkgray','#606060'];

...
.attr('fill', function(d, i){ return colors[i]; })

Bar chart with color

You can also create a single array of objects to store the color, width, etc.


A more scalable idea would be to get rid of the array prev_width and have a function that can take the sum up to a point. Such as:

function sum(array, start, end) {
    var total = 0;
    for(var i=start; i<end; i++) total += array[i];
    return total;
}

Then you can do:

...
   .attr('x',function(d, i){
   return sum(datavars, 0, i); })

Example using summation

like image 157
Spencer Wieczorek Avatar answered Oct 17 '22 08:10

Spencer Wieczorek