Datacrawler
Datacrawler

Reputation: 2876

D3 Chart values are not grouped (from JSON load)

I have a JSON query and I am presenting the data using a D3 chart. It seems that the grouping (Countries) does not work. We can some more than one line for each country (e.g. Canada). Part of the code:

data.forEach(function (a) {
    groups[a.value] = groups[a.value] || [];
    groups[a.value].push(a);    
});

result = Object.keys(groups).reduce(function (r, k) {
    return r.concat(groups[k]);
}, []);

Ideally, I want to make this work by changing the script. A second solution is to change the format of the JSON (manipulation). I am trying to focus on the first solution... The snippet is here:

      var json_data= {"headers":["Dimension 1","Metric 1","Metric 2"],"rows":[["Australia",174,23],["Canada",502,17],["France",242,37],["Germany",102,42],["United Kingdom",126,44],["United States",1246,47],["Australia",680,80],["Canada",1241,66],["Canada",1241,66],["France",150,30],["Germany",244,22],["United Kingdom",501,9],["United States",4960,41],["Australia",9,8],["Canada",3655,70],["France",1654,95],["Germany",1190,36],["United Kingdom",1222,38],["United States",7941,53],["Australia",6829,56],["Canada",1664,75],["France",2995,88],["Germany",1487,100],["United Kingdom",9245,29],["United States",9008,66],["Australia",9376,7],["Canada",1531,31],["France",5421,22],["Germany",6975,41],["United Kingdom",4320,100],["United States",3200,41],["Australia",6688,41],["Canada",699,42],["France",5403,70],["Germany",6377,49],["United Kingdom",2471,14],["United States",6650,4],["Australia",865,70],["Canada",511,20],["France",981,36],["Germany",57,10],["United Kingdom",675,38],["United States",40,72],["Australia",400,63],["Canada",971,90],["France",357,93],["Germany",820,40],["United Kingdom",520,32],["United States",448,24],["Australia",513,40],["Canada",977,8],["France",118,84],["Germany",161,29],["United Kingdom",239,89],["United States",327,79]]};
      var headers = json_data.headers;
      var platform_data = json_data.rows;
      var data = [];
      var metric = 0;
      for (var i in platform_data)
        {
          var dimension = platform_data[i][0];
          metric = (platform_data[i][1]).toFixed(0);
          object = { label: dimension, value: metric};
          data.push(object);
        }

//Sorting
var data = data,
    groups = Object.create(null),
    result = [];

data.forEach(function (a) {
    groups[a.value] = groups[a.value] || [];
    groups[a.value].push(a);    
});

result = Object.keys(groups).reduce(function (r, k) {
    return r.concat(groups[k]);
}, []);


//Descending - Reverse JSON order
var objAssetSelection = result.reverse();
data = objAssetSelection;

//D3 Code

        var div = d3.select("body").append("div").attr("class", "toolTip");
    
        var axisMargin = 20,
                margin = 40,
                valueMargin = 4,
                width = parseInt(d3.select('body').style('width'), 10),
                height = parseInt(d3.select('body').style('height'), 10),
                barHeight = (height-axisMargin-margin*2)* 0.4/data.length,
                barPadding = (height-axisMargin-margin*2)*0.6/data.length,
                data, bar, svg, scale, xAxis, labelWidth = 0;
    
        max = d3.max(data, function(d) { return d.value; });
    
        svg = d3.select('body')
                .append("svg")
                .attr("width", width)
                .attr("height", height);
    
    
        bar = svg.selectAll("g")
                .data(data)
                .enter()
                .append("g");
    
        bar.attr("class", "bar")
                .attr("cx",0)
                .attr("transform", function(d, i) {
                    return "translate(" + margin + "," + (i * (barHeight + barPadding) + barPadding) + ")";
                });
    
        bar.append("text")
                .attr("class", "label")
                .attr("y", barHeight / 2)
                .attr("dy", ".35em") //vertical align middle
                .text(function(d){
                    return d.label;
                }).each(function() {
            labelWidth = Math.ceil(Math.max(labelWidth, this.getBBox().width));
        });
    
        scale = d3.scale.linear()
                .domain([0, max])
                .range([0, width - margin*2 - labelWidth]);
    
        xAxis = d3.svg.axis()
                .scale(scale)
                .tickSize(-height + 2*margin + axisMargin)
                .orient("bottom");
    
        bar.append("rect")
                .attr("transform", "translate("+labelWidth+", 0)")
                .attr("height", barHeight)
                .attr("width", function(d){
                    return scale(d.value);
                });
    
        bar.append("text")
                .attr("class", "value")
                .attr("y", barHeight / 2)
                .attr("dx", -valueMargin + labelWidth) //margin right
                .attr("dy", ".35em") //vertical align middle
                .attr("text-anchor", "end")
                .text(function(d){
                    return (d.value);
                })
                .attr("x", function(d){
                    var width = this.getBBox().width;
                    return Math.max(width + valueMargin, scale(d.value));
                });
    
        bar
                .on("mousemove", function(d){
                    div.style("left", d3.event.pageX+10+"px");
                    div.style("top", d3.event.pageY-25+"px");
                    div.style("display", "inline-block");
                    div.html((d.label)+"<br>"+(d.value));
                });
        bar
                .on("mouseout", function(d){
                    div.style("display", "none");
                });
    
        svg.insert("g",":first-child")
                .attr("class", "axisHorizontal")
                .attr("transform", "translate(" + (margin + labelWidth) + ","+ (height - axisMargin - margin)+")")
                .call(xAxis);
@import url('https://fonts.googleapis.com/css?family=Roboto');

body {
        font-family: "Roboto"!important;
        width: 100%;
        height: 500px;
        position: relative;
    }

    svg {
        width: 100%;
        height: 100%;
        position: center;
    }

    .toolTip {
        position: absolute;
        display: none;
        width: auto;
        height: auto;
        background: none repeat scroll 0 0 white;
        border: 0 none;
        border-radius: 8px 8px 8px 8px;
        box-shadow: -3px 3px 15px #888888;
        color: black;
        font: 12px sans-serif;
        padding: 5px;
        text-align: center;
    }

    text {
        font: 10px sans-serif;
        color: white;
    }
    text.value {
        font-size: 100%;
        fill: white;
    }

    .axisHorizontal path{
        fill: none;
    }

    .axisHorizontal .tick line {
        stroke-width: 1;
        stroke: rgba(0, 0, 0, 0.2);
    }

    .bar {
        fill: steelblue;
        fill-opacity: .9;
    }
<script src="https://d3js.org/d3.v3.min.js"></script>

Upvotes: 1

Views: 50

Answers (1)

Shashank
Shashank

Reputation: 5670

The grouping code is incorrect. If you console log data after your grouping data, it's the same non-grouped data.

For better grouping, you can take a look at d3.nest. Here's the grouping code using d3.nest (grouping data based on label and summing up the values using d3.sum (d3.sum):

var nested_data = d3.nest()
                    .key(function(d) { return d.label; })
                    .rollup(function (d) { 
                      return d3.sum(d, function(v) { return +v.value;})
                    }).entries(data);

And to not disturb your rest of the code, I'm mapping this nested_data to the original format by this:

data = nested_data.map(function(row) {
  return {label: row.key, value: row.values};
});

EDIT: Adding sorting code

data.sort(function(a, b) { return a.value > b.value ? -1 : a.value === b.value ? 0 : 1; });

Using the above nesting/grouping, here's a code snippet:

var json_data= {"headers":["Dimension 1","Metric 1","Metric 2"],"rows":[["Australia",174,23],["Canada",502,17],["France",242,37],["Germany",102,42],["United Kingdom",126,44],["United States",1246,47],["Australia",680,80],["Canada",1241,66],["Canada",1241,66],["France",150,30],["Germany",244,22],["United Kingdom",501,9],["United States",4960,41],["Australia",9,8],["Canada",3655,70],["France",1654,95],["Germany",1190,36],["United Kingdom",1222,38],["United States",7941,53],["Australia",6829,56],["Canada",1664,75],["France",2995,88],["Germany",1487,100],["United Kingdom",9245,29],["United States",9008,66],["Australia",9376,7],["Canada",1531,31],["France",5421,22],["Germany",6975,41],["United Kingdom",4320,100],["United States",3200,41],["Australia",6688,41],["Canada",699,42],["France",5403,70],["Germany",6377,49],["United Kingdom",2471,14],["United States",6650,4],["Australia",865,70],["Canada",511,20],["France",981,36],["Germany",57,10],["United Kingdom",675,38],["United States",40,72],["Australia",400,63],["Canada",971,90],["France",357,93],["Germany",820,40],["United Kingdom",520,32],["United States",448,24],["Australia",513,40],["Canada",977,8],["France",118,84],["Germany",161,29],["United Kingdom",239,89],["United States",327,79]]};
      var headers = json_data.headers;
      var platform_data = json_data.rows;
      var data = [];
      var metric = 0;
      for (var i in platform_data)
        {
          var dimension = platform_data[i][0];
          metric = (platform_data[i][1]).toFixed(0);
          object = { label: dimension, value: metric};
          data.push(object);
        }

//Sorting
var data = data,
    groups = Object.create(null),
    result = [];

/* data.forEach(function (a) {
    groups[a.value] = groups[a.value] || [];
    groups[a.value].push(a);    
});

result = Object.keys(groups).reduce(function (r, k) {
    return r.concat(groups[k]);
}, []); */


var nested_data = d3.nest()
		    .key(function(d) { return d.label; })
                    .rollup(function (d) { 
                      return d3.sum(d, function(v) { return +v.value;})
                    }).entries(data);
                      
data = nested_data.map(function(row) {
	return {label: row.key, value: row.values};
}).sort(function(a, b) { return a.value > b.value ? -1 : a.value === b.value ? 0 : 1; });
//Descending - Reverse JSON order
/* var objAssetSelection = result.reverse();
data = objAssetSelection;
 */
//D3 Code

        var div = d3.select("body").append("div").attr("class", "toolTip");
    
        var axisMargin = 20,
                margin = 40,
                valueMargin = 4,
                width = parseInt(d3.select('body').style('width'), 10),
                height = parseInt(d3.select('body').style('height'), 10),
                barHeight = (height-axisMargin-margin*2)* 0.4/data.length,
                barPadding = (height-axisMargin-margin*2)*0.6/data.length,
                data, bar, svg, scale, xAxis, labelWidth = 0;
    
        max = d3.max(data, function(d) { return d.value; });
    
        svg = d3.select('body')
                .append("svg")
                .attr("width", width)
                .attr("height", height);
    
    
        bar = svg.selectAll("g")
                .data(data)
                .enter()
                .append("g");
    
        bar.attr("class", "bar")
                .attr("cx",0)
                .attr("transform", function(d, i) {
                    return "translate(" + margin + "," + (i * (barHeight + barPadding) + barPadding) + ")";
                });
    
        bar.append("text")
                .attr("class", "label")
                .attr("y", barHeight / 2)
                .attr("dy", ".35em") //vertical align middle
                .text(function(d){
                    return d.label;
                }).each(function() {
            labelWidth = Math.ceil(Math.max(labelWidth, this.getBBox().width));
        });
    
        scale = d3.scale.linear()
                .domain([0, max])
                .range([0, width - margin*2 - labelWidth]);
    
        xAxis = d3.svg.axis()
                .scale(scale)
                .tickSize(-height + 2*margin + axisMargin)
                .orient("bottom");
    
        bar.append("rect")
                .attr("transform", "translate("+labelWidth+", 0)")
                .attr("height", barHeight)
                .attr("width", function(d){
                    return scale(d.value);
                });
    
        bar.append("text")
                .attr("class", "value")
                .attr("y", barHeight / 2)
                .attr("dx", -valueMargin + labelWidth) //margin right
                .attr("dy", ".35em") //vertical align middle
                .attr("text-anchor", "end")
                .text(function(d){
                    return (d.value);
                })
                .attr("x", function(d){
                    var width = this.getBBox().width;
                    return Math.max(width + valueMargin, scale(d.value));
                });
    
        bar
                .on("mousemove", function(d){
                    div.style("left", d3.event.pageX+10+"px");
                    div.style("top", d3.event.pageY-25+"px");
                    div.style("display", "inline-block");
                    div.html((d.label)+"<br>"+(d.value));
                });
        bar
                .on("mouseout", function(d){
                    div.style("display", "none");
                });
    
        svg.insert("g",":first-child")
                .attr("class", "axisHorizontal")
                .attr("transform", "translate(" + (margin + labelWidth) + ","+ (height - axisMargin - margin)+")")
                .call(xAxis);
body {
        font-family: "Roboto"!important;
        width: 100%;
        height: 500px;
        position: relative;
    }

    svg {
        width: 100%;
        height: 100%;
        position: center;
    }

    .toolTip {
        position: absolute;
        display: none;
        width: auto;
        height: auto;
        background: none repeat scroll 0 0 white;
        border: 0 none;
        border-radius: 8px 8px 8px 8px;
        box-shadow: -3px 3px 15px #888888;
        color: black;
        font: 12px sans-serif;
        padding: 5px;
        text-align: center;
    }

    text {
        font: 10px sans-serif;
        color: white;
    }
    text.value {
        font-size: 100%;
        fill: white;
    }

    .axisHorizontal path{
        fill: none;
    }

    .axisHorizontal .tick line {
        stroke-width: 1;
        stroke: rgba(0, 0, 0, 0.2);
    }

    .bar {
        fill: steelblue;
        fill-opacity: .9;
    }
<script src="https://d3js.org/d3.v3.min.js"></script>

To learn more about d3 nest, here's a good examples list: http://bl.ocks.org/phoebebright/raw/3176159/

Hope this helps.

Upvotes: 1

Related Questions