Anna Tol
Anna Tol

Reputation: 157

How can I make a triple bar chart using D3?

The code below is working perfectly fine but I want to use another dataset. A dataset where the attributes are arrays themselves. They are structured likes this: [year, population, man, woman]. The year will be the X-axis and the other three will be displayed as bars next to each other. How can I make an triple bar chart? Where to start?

    <script type="text/javascript">
        //Width and height
        var w = 500;
        var h = 250;
        var barPadding = 5;

        //Dataset
        var dataset = [10027, 10200, 10328, 10436, 10551, 10680, 10822, 10957, 11096, 11278,
                        11417, 11556, 11721, 11890 ];

        //The other dataset
        //[year, population, man, woman]
        //var dataset = [[1950, 10027, 4998, 5029], [1950, 10027, 4998, 5029], [1950, 10027, 4998, 5029], etc];

        //Scale
        var x = d3.scale.ordinal().range([0, w])
        var y = d3.scale.linear().range([h, 0]);

        // Axis
        var xAxis = d3.svg.axis()
            .scale(x)
            .orient("bottom")

        var yAxis = d3.svg.axis()
            .scale(y)
            .orient("left")
            .ticks(10);

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

        svg.selectAll("rect")
           .data(dataset)
           .enter()
           .append("rect")
           .attr("x", function(d, i) {
                return i * (w / dataset.length);
           })
           .attr("y", function(d) {
                return h - (d / 50);
           })
           .attr("width", w / dataset.length - barPadding)
           .attr("height", function(d) {
                return d / 50;
           })
           .attr("fill", function(d) {
                return "rgb(102, 0, 51)";
           });

            svg.append("g")
                .attr("class", "x axis")
                .attr("transform", "translate(0," + h + ")")
                .call(xAxis)
                .style("text-anchor", "end")
                .text("Years");

            svg.append("g")
                .attr("class", "y axis")
                .call(yAxis)
                .append("text")
                .attr("transform", "rotate(-90)")
                .attr("y", 6)
                .attr("dy", "0.5em")
                .style("text-anchor", "end")
                .text("Population in millions");

    </script>

(little question: why doesn't my x-axis appear?)

Thanks in advance

Upvotes: 0

Views: 311

Answers (1)

jhinzmann
jhinzmann

Reputation: 988

Towards your little question: your x- and y-axis get rendered, but outside of the svg visible space. The common way for dealing with this is to add a margin inside the svg and render the chart with the width and height inside of the margin. This is described by the author of D3 as conventional margin

I have created a snippet with what you call a tripple bar chart. This code is the grouped barchart of Bostock applied to your dataset. I changed the dataset a little bit, so that each year is an object with the properties population, men, women and year. With this setup it is easer to process the values.

The idea behind the code for the grouped bar chart is to add a group element for each object in your dataset and render the bars inside this group. Therefore you bind the dataset to the year groups. Inside the years selection, each element is bound to the corresponding object from your dataset array. If this all doesn't make that much sense to you, maybe you should read the Let's make a Bar Chart tutorials by Bostock. They give a great overview on the basic steps ;)

var dataset = [{year:1950, population:10000, men:4500, women:5500}, {year:1951, population:10200, men:5000, women:6200}, {year:1952, population:11000, men:6000, women:5000}, {year:1953, population:12000, men:5900, women:6100}];

  // setup conventional margin, see http://bl.ocks.org/mbostock/3019563
  var margin = {top: 20, right: 30, bottom: 30, left: 60},
      width = 700 - margin.left - margin.right,
      height = 400 - margin.top - margin.bottom;

  // accessor functions for the 4 values
  var year = function(d) { return d.year; };
  var population = function(d) { return d.population; };
  var men = function(d) { return d.men; };
  var women = function(d) { return d.women; };
  var valueObject = function(d) { return [{name:"population", value:population(d)},{name:"men", value: men(d)}, {name: "women", value: women(d)}]; };

  var color = d3.scale.ordinal()
    .domain(["population","men","women"])
    .range(["#31a354","#3182bd","#e6550d"]);

  var x0 = d3.scale.ordinal()
      .rangeRoundBands([0, width], .2)
      .domain(dataset.map(year)); // use the year for the xAxis

  var x1 = d3.scale.ordinal()
      .domain(["population","men","women"])
      .rangeRoundBands([0, x0.rangeBand()]);

  var y = d3.scale.linear()
      .range([height, 0])
      .domain([0, d3.max(dataset.map(population))]); // use the population for calculating the maximum value
  
  var xAxis = d3.svg.axis()
      .scale(x0)
      .orient("bottom");
  
  var yAxis = d3.svg.axis()
      .scale(y)
      .orient("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 + ")");

  svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);

  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("Population");

  var years = svg.selectAll(".year")
      .data(dataset)
      .enter()
    .append("g")
      .attr("class", "year")
      .attr("transform", function(d) { return "translate(" + x0(year(d)) + ",0)"; });

  years.selectAll("rect")
      .data(valueObject)
      .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); })
      .attr("fill", function(d) { return color(d.name); });

  var legend = svg.selectAll(".legend")
      .data(["population","men","women"])
    .enter().append("g")
      .attr("class", "legend")
      .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

  legend.append("rect")
      .attr("x", width - 18)
      .attr("width", 18)
      .attr("height", 18)
      .style("fill", color);

  legend.append("text")
      .attr("x", width - 24)
      .attr("y", 9)
      .attr("dy", ".35em")
      .style("text-anchor", "end")
      .text(function(d) { return d; });
.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Upvotes: 1

Related Questions