So I am using the same code as this D3 gallery example (with my own data):
I'd like to get a bubble chart where the circles are arranged with the biggest in the center and then radiating out to the smallest.
Here is a mock up I created in Photoshop:
Here is what I get when I use the example (the default circle packing algorithm with default sort):
I tried tweaking the sort (including trying d3.ascending and d3.descending). The best I could come up with just basically subverts the sort with a constant (ha!) but still is far from what I'd like:
//...
.sort( function(a, b) { return -1;} )
//...
Ok, so any chance this can be done without having to alter the actual D3 pack layout algorithm? If not, perhaps someone has extended or modified the pack layout and could tell me the 5 lines I could change in the D3 source to hack this.
Thanks in advance!
Edit:
As requested, here is the code I am using. Basically the same as the linked sample above, with a few superficial changes as indicated by the commented lines:
var diameter = 960,
format = d3.format(",d"),
color = d3.scale.category20c();
var bubble = d3.layout.pack()
// .sort(null)
// .sort(d3.ascending)
// .sort(d3.descending)
.sort( function(a, b) { return -1;} ) // basically a < b always
.size([diameter, diameter])
.padding(1.5);
var svg = d3.select("body").append("svg")
.attr("width", diameter)
.attr("height", diameter)
.attr("class", "bubble");
d3.json("data.json", function(error, root)
{
var node = svg.selectAll(".node")
.data(bubble.nodes(classes(root))
.filter(function(d) { return !d.children; }))
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
node.append("title")
.text(function(d) { return d.className + ": " + format(d.value); });
node.append("circle")
.attr("r", function(d) { return d.r; })
.style("fill", function(d)
{
// return color(d.packageName);
return color(d.value); // this gives a different color for every leaf node
});
node.append("text")
.attr("dy", ".3em")
.style("text-anchor", "middle")
// .text(function(d) { return d.className.substring(0, d.r / 3); });
});
// Returns a flattened hierarchy containing all leaf nodes under the root.
function classes(root)
{
var classes = [];
function recurse(name, node) {
if (node.children) node.children.forEach(function(child) { recurse(node.name, child); });
else classes.push({packageName: name, className: node.name, value: node.size});
}
recurse(null, root);
return {children: classes};
}
d3.select(self.frameElement).style("height", diameter + "px");
And my data.json file:
{
"name": "Root",
"children": [
{
"name": "Leaf",
"children": null,
"size": 2098629
},
{
"name": "Leaf",
"children": null,
"size": 104720
},
{
"name": "Leaf",
"children": null,
"size": 5430
},
{
"name": "Leaf",
"children": null,
"size": 102096
},
{
"name": "Leaf",
"children": null,
"size": 986974
},
{
"name": "Leaf",
"children": null,
"size": 59735
},
{
"name": "Leaf",
"children": null,
"size": 1902
},
{
"name": "Leaf",
"children": null,
"size": 120
},
{
"name": "Leaf",
"children": null,
"size": 870751
},
{
"name": "Leaf",
"children": null,
"size": 36672
},
{
"name": "Leaf",
"children": null,
"size": 274338
},
{
"name": "Leaf",
"children": null,
"size": 517693
},
{
"name": "Leaf",
"children": null,
"size": 145807
},
{
"name": "Leaf",
"children": null,
"size": 476178
},
{
"name": "Leaf",
"children": null,
"size": 11771
},
{
"name": "Leaf",
"children": null,
"size": 153
},
{
"name": "Leaf",
"children": null,
"size": 2138
},
{
"name": "Leaf",
"children": null,
"size": 8436
},
{
"name": "Leaf",
"children": null,
"size": 3572
},
{
"name": "Leaf",
"children": null,
"size": 120235
},
{
"name": "Leaf",
"children": null,
"size": 210945
},
{
"name": "Leaf",
"children": null,
"size": 56033
},
{
"name": "Leaf",
"children": null,
"size": 358704
},
{
"name": "Leaf",
"children": null,
"size": 295736
},
{
"name": "Leaf",
"children": null,
"size": 26087
},
{
"name": "Leaf",
"children": null,
"size": 33110
},
{
"name": "Leaf",
"children": null,
"size": 3828
},
{
"name": "Leaf",
"children": null,
"size": 1105544
},
{
"name": "Leaf",
"children": null,
"size": 98740
},
{
"name": "Leaf",
"children": null,
"size": 80723
},
{
"name": "Leaf",
"children": null,
"size": 5766
},
{
"name": "Leaf",
"children": null,
"size": 1453
},
{
"name": "Leaf",
"children": null,
"size": 10443176
},
{
"name": "Leaf",
"children": null,
"size": 14055
},
{
"name": "Leaf",
"children": null,
"size": 1890127
},
{
"name": "Leaf",
"children": null,
"size": 404575
},
{
"name": "Leaf",
"children": null,
"size": 272777
},
{
"name": "Leaf",
"children": null,
"size": 1269763
},
{
"name": "Leaf",
"children": null,
"size": 5081
},
{
"name": "Leaf",
"children": null,
"size": 3168510
},
{
"name": "Leaf",
"children": null,
"size": 717031
},
{
"name": "Leaf",
"children": null,
"size": 88418
},
{
"name": "Leaf",
"children": null,
"size": 762084
},
{
"name": "Leaf",
"children": null,
"size": 255055
},
{
"name": "Leaf",
"children": null,
"size": 535
},
{
"name": "Leaf",
"children": null,
"size": 81238
},
{
"name": "Leaf",
"children": null,
"size": 17075
},
{
"name": "Leaf",
"children": null,
"size": 5331
},
{
"name": "Leaf",
"children": null,
"size": 74834
},
{
"name": "Leaf",
"children": null,
"size": 110359
},
{
"name": "Leaf",
"children": null,
"size": 27333
},
{
"name": "Leaf",
"children": null,
"size": 143
},
{
"name": "Leaf",
"children": null,
"size": 12721
},
{
"name": "Leaf",
"children": null,
"size": 529
},
{
"name": "Leaf",
"children": null,
"size": 115684
},
{
"name": "Leaf",
"children": null,
"size": 3990850
},
{
"name": "Leaf",
"children": null,
"size": 6045060
},
{
"name": "Leaf",
"children": null,
"size": 2445766
},
{
"name": "Leaf",
"children": null,
"size": 479865
},
{
"name": "Leaf",
"children": null,
"size": 105743
},
{
"name": "Leaf",
"children": null,
"size": 183750
},
{
"name": "Leaf",
"children": null,
"size": 661
},
{
"name": "Leaf",
"children": null,
"size": 11181
}
],
"size": 41103329
}
All you need to do is to specify:
.sort(function(a, b) {
return -(a.value - b.value);
})
This is different than specifying .sort(d3.ascending)
or .sort(d3.descending)
, since d3.ascending
and d3.descending
are defined as
function(a, b) {
return a < b ? -1 : a > b ? 1 : 0;
}
and
function(a, b) {
return b < a ? -1 : b > a ? 1 : 0;
}
respecitevly, and the pack layout is affected by their "insensitivity" to the difference of data points.
This is my test example: (with your data) jsfiddle
Experimentally, I applied also following sort function: (it is a kind of hybrid)
.sort( function(a, b) {
var threshold = 10000000;
if ((a.value > threshold) && (b.value > threshold)) {
return -(a.value - b.value);
} else {
return -1;
}
})
... and for values for threshold of 10000000, 3000000, 1000000, 300000, 100000, 30000 respectively I got: jsfiddle