How to make a basic Bubble Chart in D3
Getting your data into CSV format
Use a spreadsheet program to export your data as a CSV
The var diameter = 500
will determine the maximum dimensions of the area of the circle pack chart.
The color scale we use is d3.scaleOrdinal(d3.schemeCategory10)
, but you may use one of the other color scales provided by d3.
Alternatively, you can use your own color scale by setting up an ordinal or linear scale, and specifying the domain as several colors .range(["#000000", "#ffffff", "#CCCCCC", "#333333"])
.
NOTE: Example below uses D3js Version 5.
Full example below
This project requires fruit.csv file to be in the same folder, and the project has to be run from a webserver.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Basic Bubble Chart</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
/* custom css styles will go here */
</style>
</head>
<body>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
var diameter = 500, //max size of the bubbles
format = d3.format(",d"),
color = d3.scaleOrdinal(d3.schemeCategory10);
//more color options: https://github.com/d3/d3-scale-chromatic
var bubble = d3.pack()
.size([diameter, diameter])
.padding(1.5);
var svg = d3.select("body")
.append("svg")
.attr("width", diameter)
.attr("height", diameter)
.attr("class", "bubble");
d3.csv("fruit.csv").then(function(data){
//convert numerical values from strings to numbers
data = data.map(function(d){ d.value = +d["Amount"]; return d; });
//Sets up a hierarchy of data object
var root = d3.hierarchy({children:data})
.sum(function(d) { return d.value; })
.sort(function(a, b) { return b.value - a.value; });
//Once we have hierarchal data, run bubble generator
bubble(root);
//setup the chart
var bubbles = svg.selectAll(".bubble")
.data(root.children)
.enter();
//create the bubbles
bubbles.append("circle")
.attr("class", "circle")
.attr("r", function(d){ return d.r; })
.attr("cx", function(d){ return d.x; })
.attr("cy", function(d){ return d.y; })
.style("fill", function(d) { return color(d.value); });
//format the text for each bubble
bubbles.append("text")
.attr("x", function(d){ return d.x; })
.attr("y", function(d){ return d.y + 5; })
.attr("text-anchor", "middle")
.text(function(d){ return d.data["Fruit"]; })
.style("fill","white")
.style("font-family", "Helvetica Neue, Helvetica, Arial, san-serif")
.style("font-size", "12px");
});
</script>
</body>
</html>
Wrapping Long Lables on the Chart
Sometimes, these charts have labels that are too long to fit in the bubbles. There is a solution to wrapping the labels noted by this StackOverflow post.
To the end of your code, add the following JavaScript function:
function wrap(d) {
var text = d3.select(this),
width = d.r * 2,
x = d.x,
y = d.y,
words = text.text().split(/\s+/).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.1,
tspan = text.text(null).append("tspan").attr("x", x).attr("y", y);
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dy", ++lineNumber * lineHeight + "em").text(word);
}
}
}
Then, to the last line of the block of code where you appended the text lables, add .each(wrap)
.
You can also limit the size of any bubble that should be labeled by putting a conditional statement in the text function. This example shows how to do both of these things:
bubbles.append("text")
.attr("x", function(d){ return d.x; })
.attr("y", function(d){ return d.y + 5; })
.attr("text-anchor", "middle")
.text(function(d){
//Only show text if bubble's radius is more than 25
if(d.r > 25){
return d.data["Fruit"];
} else {
//bubble has a radius smaller than 25, don't show label
return "";
}
})
.style("fill","white")
.style("font-family", "Helvetica Neue, Helvetica, Arial, san-serif")
.style("font-size", "12px")
//add this wrap function to wrap long text
.each(wrap);