
Creating a bubble chart
Bubble charts can display sets of values as circles. They're usable for datasets with sizes in the range 10 through 100. They're particularly useful for visualizing values that differ by orders of magnitude and can replace pie charts in those situations.
As bubble charts are more complex and slightly less common, we're going to need a flexible library to draw them. The excellent D3 library (http://d3js.org/) is a great fit; it provides a set of tools, (the core data-driven DOM API plus the "pack" data layout) that enables the creation of bubble charts.
We're going to draw a bubble chart displaying the numbers of visitors coming to our website from referring websites.
How to do it...
Let's write the HTML and JavaScript code.
- We're going to create an HTML page containing our chart placeholder. We're going to include the chart library D3, and the code that will draw the bubble chart from our
example.js
file:<!DOCTYPE HTML> <html> <head> <title>Chart example</title> <style type="text/css"> #chart text { font-family: Verdana; font-size:10px; } </style> </head> <body> <div id="chart"></div> <script src="http://mbostock.github.com/d3/d3.v2.js?2.9.5"></script> <script type="text/javascript" src="example.js"></script> </body> </html>
- Then we're going to add the following code in
example.js
:(function() { var getData = function(cb) { cb({children:[ {domain: 'google.com', value: 6413}, {domain: 'yahoo.com', value: 831}, {domain: 'bing.com', value: 1855}, {domain: 'news.ycombinator.com', value: 5341}, {domain: 'reddit.com', value: 511}, {domain: 'blog.someone.com', value: 131}, {domain: 'blog.another.com', value: 23}, {domain: 'slashdot.org', value: 288}, {domain: 'twitter.com', value: 327}, {domain: 'review-website.com', value: 231} ]}); } // r is the dimension of the bubble chart var r = 640, fill = d3.scale.category20c(); // create the visualization placeholder var vis = d3.select("#chart").append("svg") .attr("width", r) .attr("height", r) .attr("class", "bubble"); // create a pack layout for the bubbles var bubble = window.bubble = d3.layout.pack() .sort(null) .size([r, r]) .padding(1.5); getData(function(json) { // Process the data with the pack layout var data = bubble.nodes(json); // Create a node for every leaf data element var selection = vis.selectAll("g.node") .data(data.filter(function(d) { return !d.children; })); var node = selection.enter().append("g"); node.attr("class", "node"); node.append("title") .text(function(d) { return d.domain }); node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); node.append("circle") .attr("r", function(d) { return d.r; }) .style("fill", function(d) { return fill(d.domain); }); node.append("text") .attr("text-anchor", "middle") .attr("dy", ".3em") .text(function(d) { return d.domain.substring(0, d.r / 3); }); }); }());
In the following section, we're going to explain how D3 works and how we're using it to create a bubble chart:
How it works...
Unlike most other chart libraries, D3 doesn't have any predefined chart types that it is capable of drawing. Instead, it comes with a set of modular tools that you can freely mix and match to create any kind of data-driven documents.
However, D3 contains some very visualization-specific tools.
For example, d3.scale.category20c
creates an ordinal scale. An ordinal scale maps input values to a discrete set of output values. In this case, the discrete set of values is a set of 20 predefined output colors. The scale is a function—it maps the input values to the output. We can specify explicitly which input values map to which outputs, but if we don't, it's inferred from usage. In our case, it means that the first domain name will be mapped to the first color, the second to the second, and so on.
Other tools include jQuery-like DOM selection tools, which, in our recipe, we use to add the SVG element to our chart placeholder.
Another example are D3 layouts. To draw a bubble chart we need a pack layout. Layouts map a set of objects with values to a set of output coordinates based on certain rules and constrains. A popular example is a force layout, which is a graph layout that arranges the objects by iteratively applying virtual forces between graph nodes.
We're using the pack layout that produces hierarchical packing of objects into circles. Our data is flat, therefore the pack layout is only used to arrange our circles automatically. A pack layout is created and assigned to the bubble
variable.
The pack layout works by applying the bubble.nodes
function to the input data. This function looks for the value
property in each of the objects in the input data. Based on this property (which it treats as a relative radius) and the size of the layout, it adds the following properties into our data: x, y, and r and returns the resulting array of objects.
At this point we have most of the data needed for our bubble chart: we have the positions and dimensions of our bubbles. All we need to do now is to turn them into the appropriate SVG elements. The tool we use to do this is D3's selectAll
function.
Unlike jQuery selectors, D3's selectAll
can be used to maintain a two-way mapping between the document and a data object. We specify the data array mapped to our selection by using the selection's .data
function.
After we declare this mapping, we can decide what happens when an element is added to our data array using the .enter
function. In our recipe, we declare that a new SVG graphic element is added to the SVG canvas, and assign that declaration to the node
variable.
It's important to note that our node variable is not holding the SVG element; rather, it's a selection of every graphics SVG element in the set of nodes that will be created in the future, whenever a new data element "enters" the selection. Therefore, operations on the node specify the operations that will be executed on every added SVG element.
We specify that every node will have a title
attribute (which will appear on mouse over). The inner text of this title is dependent on the specific element in the data array. To describe this, we pass a function as the argument to the .text()
call. The first argument of the passed function will be the data element of the particular node, and the returned value should be the text that will be set as the title.
Similarly, we move our bubbles to the position calculated by the pack layout. Afterwards, we add the circle with a radius calculated by the pack layout for the circle and the colors scale to generate colors for the circle.
Finally, a text node is appended in the same way.
The following is how the result looks like:

There's more...
This example used SVG (scalable vector graphics) markup to render the visualization. Most modern browsers support SVG, but Internet Explorer versions prior to IE9 don't. However, D3 isn't limited to SVG—it is also able to generate HTML elements that could be used as a replacement in older versions of IE.