This block uses the d3-beeswarm plugin I made (see the Github project). This plugin produces a beeswarm arrangement, thanks to a dedicated algorithm and without the use a the d3.force layout.
Beeswarm is a one-dimensional scatter plot with closely-packed, non-overlapping points. The beeswarm plot is a useful technique when we wish to see not only the measured values of interest for each data point, but also the distribution of these values
Some beeswarm-like plot implementation uses force layout, but the force layout simulation has some drawbacks:
This beeswarm plugin uses a dedicated one pass algorithm. By default, the plugin arranges data in an horizontal way. In this case, the final arrangement is contraint in x and free in y. This means that data are arranged along the x-axis, and that the position of each data reflects its precise x value. y position doesn’t reflect any data-related value, and only serves the non-overlapping constraint. This plugin can also arrange data in a vertical way.
Even if this block works fine, the plugin is an on-going work. See the Github project’s issues for possible enhancements.
var swarm = d3.beeswarm()
.data(data) // set the data to arrange
.distributeOn(function(d){ // set the value accessor to distribute on
return xScale(d.trend); // evaluated once on each element of data
}) // when starting the arrangement
.radius(4) // set the radius for overlapping detection
.orientation("horizontal") // set the orientation of the arrangement
// could also be 'vertical'
.side("symetric") // set side(s) available for accumulation;
// could also be 'positive' or 'negative'
.arrange(); // launch arrangement computation;
// return an array of {datum: , x: , y: }
// where datum refers to an element of data
// each element of data remains unchanged
Then, later in your code, in order to draw the swarm:
d3.selectAll("circle")
.data(swarm)
.enter()
.append("circle")
.attr("cx", function(bee) { return bee.x; })
.attr("cy", function(bee) { return bee.y; })
.attr("r", 4)
.style("fill", function(bee) { return fill(bee.datum.rank); })
In the last line, bee.datum
refers to the original datum.