Be the first user to complete this post

  • 0
Add to List

d3 Fundamentals: Creating a simple bar graph

null

After learnnig about how data binding takes place in d3 along with its notion of domains, ranges and scales, we are going to continue our data visualization series with the simplest example possible - creating a rectangle - and then we go on to create something more meaningful - a bar chart.

So, whats in a rectangle?

Rectangles are native to the svg specification like circles and groups and many other elements. A rectangle has a couple of attributes that are used to render it on an svg - x, y, width and height. We will start out this article by taking just one data point and then try to draw a rectangle for that data point. Then, we will build upon the same concept and the stuff we learned in our previous posts to draw multiple rectangles in order to create our bar graph.
TIP: When working with svgs, having a cheatsheet of the various attributes of the valid svg elements can go a long way in helping you become more productive. You can find a really good one over here.

Drawing a rectangle itself in SVG is pretty simple. All you gotta do is specify the values of each of its attributes and you're good to go.
var chart = d3.select('.chart');
var bar = chart.append('g');
bar.append('rect')
  .attr('width', 20)
  .attr('height', 50);
You can get your hands dirty with these values in this jsfiddle. In the above code, you can see that we simply created a rectangle with a width of 20px and a height of 50 px. If now specified, x and y take up their default values of 0. Also notice that we added our rectangled with a 'g' tag. This is helpful when you later want to add labels to your rectangle as it makes makes it easier to relatively position the label text within the groups.

Drawing our bar chart

Assume that we want to plot a bar chart of the age of a few people and the data is available in an array as follows
var ages = [30, 22, 33, 45];
Lets say tht we choose to represent the length of the bars on the y axis. We first start by defining the functions that will be used as converters.
var totalWidth = 500;
var totalHeight = 200;
var scale = {
  y: d3.scale.linear()
};
Assuming that a person lives a max of 100 years and starts at 0 years, we can safely define our domain as follows.
scale.y.domain([0, 100]);
Now lets say that you want to draw this on an area of size 200x400px. Since we are only concerned about the height, thats all we will need to define our range.
scale.y.range([0, 200]);
To actually plot this data on the graph, we will need to do two things. 1. Create as many group nodes on the svg as there are elements in our array and bind them to the data.
  • Create a rectangle inside of each group node.
  • For each rectangle, use its corresponding data point to determine its scaled height on the drawing area
Appending the bars and adding nodes can be done as shown below. If you are'nt sure about how things are working, I suggest you quickly breeze through our article on selections before you proceed.
var chart = d3.select('.chart')
    .attr({
        'width': totalWidth,
        'height': totalHeight
    });

var bars = chart
    .selectAll('g')
    .data(ages)
    .enter()
    .append('g');
Now, lets add a rectangle to each of these 'g' tags and use the 'inherited' data to specify the dimentions of the rect.
TIP : When you add child nodes to a node to which data is already bound, it inherits the data from its parent.

bars.append('rect')
    .attr({
        'x': function (d, i) {
            return i * barWidth;
        },
        'width': barWidth - 1,
        'height': scale.y
    });
There are a few key points to observe about the above code
  • Earlier we said that we wanted to append a rect to each bar('g' node) but as seen above, there isi'nt a loop of any kind to do that. Since the result of the previous operation resulted in the variable 'bars' to reference a number of elements after data binding and append, d3 automatically iterated over each of the bars and appended a 'rect' node to each of them.
  • The value of the variable 'height' is specified as scale.y. One has to remember that scale.y is actually a function that takes an argument and returnes a scaled value. In this case, scale.y will receive the data point as the argument and scale it for each node. In fact, the above code is just a shorthand for the following
bars.append('rect')
    .attr({
        'x': function (d, i) {
            return i * barWidth;
        },
        'width': barWidth - 1,
        'height': function (d, i) {
            return scale.y(d);
        }
    });
The full source code of the example so far can be found in this jsfiddle.

Turning it up

You must have noticed that the bar chart that we just drew appears inverted. Thats because in SVG, the top left is considered to be the origin - aka (x=0, y-0). While this works well for the computer, its doesnt really work that well for us. Therefore in most graphs that you draw which need to be presented bottom(s)-up, you'd need to alter the scale functions for the y axis to produce the correct output. In order to fix the bar graph in our example, we need to do two things
  • Invert the array that represents the range of y. This will cause a smaller values of y to be drawn lower on the chart and vice versa.
  • Start drawing the rectangle from the y value generated upto the maxHeight. i.e. height of rect = Total height - Y value.
// ...
// ...
scale.y.range([totalHeight, 0]);
// ...
// ...
bars.append('rect')
    .attr({
        'x': function (d, i) {
            return i * barWidth;
        },
        'y': scale.y,
        'height': function (d) {
            // This is how much height is left over after
            // applying the inverted scale for the y axis
            return totalHeight - scale.y(d);
        },
        'width': barWidth - 1
    });
The full source code for this example can be found in this jsfiddle.
In the next post, we will learn how to add an x and y axis to our graph and add a very basic hover state to bars. Follow us on twitter to get the next article the moment it gets freshly baked out of our oven.



Also Read:

  1. Creating an Animated Ring or Pie chart in d3js
  2. Render a d3 tree with a minimum distance between the tree nodes
  3. A visual explanation of the Enter, Update and Exit Selections in D3js
  4. Render a d3js Tree as a React Component