Rashedur Rahman
Rashedur Rahman

Reputation: 359

How to create vertically grouped bar chart in d3.js using json data?

In a web application I was supposed to create a vertically grouped bar chart using d3.js using json data. Previously I create a horizontal grouped bar using the following code. Can anyone help me out? Thanks in advance.

var data = {
  labels: [
    'resilience', 'maintainability', 'accessibility',
    'uptime', 'functionality', 'impact'
  ],
  series: [
    {
      label: '2012',
      values: [4, 8, 15, 16, 23, 42]
    },
    {
      label: '2013',
      values: [12, 43, 22, 11, 73, 25]
    },
    {
      label: '2014',
      values: [31, 28, 14, 8, 15, 21]
    },]
};

var chartWidth       = 300,
    barHeight        = 20,
    groupHeight      = barHeight * data.series.length,
    gapBetweenGroups = 10,
    spaceForLabels   = 150,
    spaceForLegend   = 150;

// Zip the series data together (first values, second values, etc.)
var zippedData = [];
for (var i=0; i<data.labels.length; i++) {
  for (var j=0; j<data.series.length; j++) {
    zippedData.push(data.series[j].values[i]);
  }
}

// Color scale
var color = d3.scale.category20();
var chartHeight = barHeight * zippedData.length + gapBetweenGroups * data.labels.length;

var x = d3.scale.linear()
    .domain([0, d3.max(zippedData)])
    .range([0, chartWidth]);

var y = d3.scale.linear()
    .range([chartHeight + gapBetweenGroups, 0]);

var yAxis = d3.svg.axis()
    .scale(y)
    .tickFormat('')
    .tickSize(0)
    .orient("left");

// Specify the chart area and dimensions
var chart = d3.select(".chart")
    .attr("width", spaceForLabels + chartWidth + spaceForLegend)
    .attr("height", chartHeight);

// Create bars
var bar = chart.selectAll("g")
    .data(zippedData)
    .enter().append("g")
    .attr("transform", function(d, i) {
      return "translate(" + spaceForLabels + "," + (i * barHeight + gapBetweenGroups * (0.5 + Math.floor(i/data.series.length))) + ")";
    });

// Create rectangles of the correct width
bar.append("rect")
    .attr("fill", function(d,i) { return color(i % data.series.length); })
    .attr("class", "bar")
    .attr("width", x)
    .attr("height", barHeight - 1);

// Add text label in bar
bar.append("text")
    .attr("x", function(d) { return x(d) - 3; })
    .attr("y", barHeight / 2)
    .attr("fill", "red")
    .attr("dy", ".35em")
    .text(function(d) { return d; });

// Draw labels
bar.append("text")
    .attr("class", "label")
    .attr("x", function(d) { return - 10; })
    .attr("y", groupHeight / 2)
    .attr("dy", ".35em")
    .text(function(d,i) {
      if (i % data.series.length === 0)
        return data.labels[Math.floor(i/data.series.length)];
      else
        return ""});

chart.append("g")
      .attr("class", "y axis")
      .attr("transform", "translate(" + spaceForLabels + ", " + -gapBetweenGroups/2 + ")")
      .call(yAxis);

// Draw legend
var legendRectSize = 18,
    legendSpacing  = 4;

var legend = chart.selectAll('.legend')
    .data(data.series)
    .enter()
    .append('g')
    .attr('transform', function (d, i) {
        var height = legendRectSize + legendSpacing;
        var offset = -gapBetweenGroups/2;
        var horz = spaceForLabels + chartWidth + 40 - legendRectSize;
        var vert = i * height - offset;
        return 'translate(' + horz + ',' + vert + ')';
    });

legend.append('rect')
    .attr('width', legendRectSize)
    .attr('height', legendRectSize)
    .style('fill', function (d, i) { return color(i); })
    .style('stroke', function (d, i) { return color(i); });

legend.append('text')
    .attr('class', 'legend')
    .attr('x', legendRectSize + legendSpacing)
    .attr('y', legendRectSize - legendSpacing)
    .text(function (d) { return d.label; });

Upvotes: 3

Views: 2185

Answers (1)

Rashedur Rahman
Rashedur Rahman

Reputation: 359

After continuous digging I found the correct way of doing this. Thanks to Mike Bostock for the example he provided in here. In here you can also find out the elaborate discussion of that example. Thanks for your support :)

    
    var margin = {top: 20, right: 20, bottom: 30, left: 40},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;
    
    var x0 = d3.scale.ordinal()
    .rangeRoundBands([0, width], .1);
    
    var x1 = d3.scale.ordinal();
    
    
    var y = d3.scale.linear()
    .range([height, 0]);
    
    
    var color = d3.scale.ordinal()
    .range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
    
    
    var xAxis = d3.svg.axis()
    .scale(x0)
    .orient("bottom");
    
    var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")
    .tickFormat(d3.format(".2s"));
    
    //console.log(margin.left);
    var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
    /*Our json object is [{letter: "A", frequency: .08167,depth:.32},{letter: "B", frequency: .01492,depth:.69}]
    
    To use csv file you just need to follow the link I provided
    */
    
    var data = [
  {letter: "A", frequency: .08167,depth:.32},
  {letter: "B", frequency: .01492,depth:.69}
];
    var groupNames=d3.keys(data[0]).filter(function(key){return key!="letter";})
    
    data.forEach(function(d){
        d.groups=groupNames.map(function(name){return {name:name,value:+d[name]};})
    });
    
    x0.domain(data.map(function(d){return d.letter;}));
    x1.domain(groupNames).rangeRoundBands([0, x0.rangeBand()]);
    
    y.domain([0,d3.max(data,function(d){
        return d3.max(d.groups,function(d){
            return d.value;
        });
    })]);
    
    svg.append("g")
    .attr("class", "y axis")
    .call(yAxis)
  .append("text")
    .attr("transform", "rotate(-90)")
    .attr("y", 6)
    .attr("dy", ".71em")
    .style("text-anchor", "end")
    .text("Letter Fun");

    var state = svg.selectAll(".state")
    .data(data)
  .enter().append("g")
    .attr("class", "g")
    .attr("transform", function(d) { return "translate(" + x0(d.letter) + ",0)"; });
    
    state.selectAll("rect")
    .data(function(d) { return d.groups; })
  .enter().append("rect")
     .attr("width", x1.rangeBand())
     .attr("x", function(d) { return x1(d.name); })
     .attr("y", function(d) { return y(d.value); })
     .attr("height", function(d) { return height - y(d.value); })
     .style("fill", function(d) { return color(d.name); });
    
    

Please let me know if you have anything to know about the code.

Upvotes: 1

Related Questions