Simple Bubble Charts Using D3.js

Jay Raj
Share

At my workplace, I was assigned the task to visualize some data. That was when I bumped into D3.js, a JavaScript library for manipulating documents based on data in an interactive way. It makes use of HTML5, JavaScript, SVG and CSS3. In this tutorial, we’ll use D3 to visualize our data in the form of abubble chart.

Before getting started, download the D3 source.

Creating the X and Y Axes

Let’s begin by drawing some axes using D3. To get started, we’ll need SVG in our HTML page. SVG is a XML based vector image format that offers support for interaction and animation. The following code sample shows what our HTML page should look like. Notice the reference to D3 and the svg tag in the body.

<html>
  <head>
    <script src="jquery.js"></script>
    <script src="d3.v3.js"></script>
    <script>
      $(function() {
        InitChart();
      });

      function InitChart() {
        // Chart creation code goes here
      }
    </script>
  </head>
  <body>
    <svg id="svgVisualize" width="500" height="500"></svg>
  </body>
</html>

D3.js has a set of APIs which we’ll be using to draw our axes. One of the APIs that we’ll be using is d3.scale.linear(), which is used to create a quantitative scale. Using this API, we can define the range and domain of each axis. The domain defines the minimum and maximum values displayed on the graph, while the range is the amount of the SVG we’ll be covering. Our svg is 500×500 so, let’s define our range as 40×400.

var xRange = d3.scale.linear().range([40, 400]).domain([0,100]);
var yRange = d3.scale.linear().range([40, 400]).domain([0,100]);

Next, we need to scale to two axes. For this we’ll be using axis.scale():

var xAxis = d3.svg.axis().scale(xRange);
var yAxis = d3.svg.axis().scale(yRange);

Next, append the x and y axes to the SVG element via JavaScript:

vis.append("svg:g").call(xAxis);
vis.append("svg:g").call(yAxis);

At this point, our InitChart() function looks like this:

function InitChart() {
  var vis = d3.select("#svgVisualize");
  var xRange = d3.scale.linear().range([40, 400]).domain([0,100]);
  var yRange = d3.scale.linear().range([40, 400]).domain([0,100]);
  var xAxis = d3.svg.axis().scale(xRange);
  var yAxis = d3.svg.axis().scale(yRange);
  vis.append("svg:g").call(xAxis);
  vis.append("svg:g").call(yAxis);
}

You can view a demo of the code up to this point. You should see a bold black line. Actually, there are two lines overlapping each other. To separate the axes, modify the code where we appended the y-axis as shown below:

vis.append("svg:g").call(yAxis).attr("transform", "translate(0,40)");

The updated code is available here. Now, you can see both axes because we have moved our y-axis by 40 units.

Next, we need to do two things: 1.) move the y-axis 40 units from the x-axis and 0 units from the y-axis and 2.) change its orientation to left. The updated InitChart() is shown below, with the updated demo available here.

function InitChart() {
  var vis = d3.select("#svgVisualize");
  var xRange = d3.scale.linear().range([40, 400]).domain([0,100]);
  var yRange = d3.scale.linear().range([40, 400]).domain([0,100]);
  var xAxis = d3.svg.axis().scale(xRange);
  var yAxis = d3.svg.axis().scale(yRange).orient("left");
  vis.append("svg:g").call(xAxis);
  vis.append("svg:g").call(yAxis).attr("transform", "translate(40,0)");
}

Now, our y-axis looks good, but the x-axis needs to be moved down. Let’s use transform to bring it down:

vis.append("svg:g").call(xAxis).attr("transform", "translate(0,400)");

Now, if we have a look at the chart, we see that the y-axis scale goes from 100 to 0. We need to invert it like so:

var yRange = d3.scale.linear().range([400, 40]).domain([0,100]);

The modified InitChart() function looks like this:

function InitChart() {
  var vis = d3.select("#svgVisualize");
  var xRange = d3.scale.linear().range([40, 400]).domain([0,100]);
  var yRange = d3.scale.linear().range([400, 40]).domain([0,100]);
  var xAxis = d3.svg.axis().scale(xRange);
  var yAxis = d3.svg.axis().scale(yRange).orient("left");
  vis.append("svg:g").call(xAxis).attr("transform", "translate(0,400)");
  vis.append("svg:g").call(yAxis).attr("transform", "translate(40,0)");
}

And here is the updated demo.

Bubble Chart

Now that the axes are setup, it’s time to create the bubble chart. The first thing we’re going to need is some data:

var sampleData = [{
  "x": 1,
  "y": 5
}, {
  "x": 20,
  "y": 20
}, {
  "x": 40,
  "y": 10
}, {
  "x": 60,
  "y": 40
}, {
  "x": 80,
  "y": 5
}, {
  "x": 100,
  "y": 60
}];

Earlier, we hard coded our domain for each axis from 0 to 100. Now that we have data, we can set the domain dynamically. D3 has min() and max() functions to make our lives easier. Simply modify the xRange and yRange variable as shown below.

var xRange = d3.scale.linear()
                .range([40, 400])
                .domain([d3.min(sampleData, function(d) {
                  return (d.x);
                }), d3.max(sampleData, function(d) {
                  return d.x;
                })]);
var yRange = d3.scale.linear()
                .range([400, 40])
                .domain([d3.min(sampleData, function(d) {
                  return d.y;
                }), d3.max(sampleData, function(d) {
                  return d.y;
                })]);

Creating Circles

Now, we need to plot circles based on the x and y values from sampleData. First, we need to bind the sampleData to circles:

var circles = vis.selectAll("circle").data(sampleData);

circles.enter();

The above code selects circle from the document and returns a placeholder. Now, we need to apply xRange and yRange to the coordinates to tranform them into the plotting space:

var circles = vis.selectAll("circle").data(sampleData);

circles
    .enter()
    .insert("circle")
    .attr("cx", function(d) { return xRange (d.x); })
    .attr("cy", function(d) { return yRange (d.y); })
    .attr("r", 10)
    .style("fill", "red");

Conclusion

D3.js is an awesome library for data visualization. In this tutorial, we focused on creating a bubble chart. Our chart is fairly simple, but the visualization can be made more interactive with the use of transitions, which we’ll discuss and implement in a future tutorial. A demo of this article’s finished product is available here.