Search code examples
javascriptd3.jssvgtreemap

Why is my d3.treemap() returning a data viz with big gaps?


I am trying to create a treemap depicting budget data.

Expected: There should NOT be any gaps within treemap; all the rectangles depicting each node should fit like legos inside the SVG.

Actual: The rectangles of my treemap do not line up nicely within my SVG; see image below:

enter image description here

For now, my dataset is quite shallow and is mostly dummy data that I made up. The structure of CSV file is:

enter image description here

These are the relevant steps from code:

Step 1: After loading in the CSV file, I converted it into hierarchy using d3.stratify():

`let dataStratified = d3
.stratify()
.id(function (d) {
  return d.Child;
})
.parentId(function (d) {
  return d.Parent;
})(results);`

Step 2: Then I passed to a hierarchical layout, d3.treemap():

let myTreemap = (data) =>
d3.treemap().size([width, height]).padding(1).round(true)(
  d3
    .hierarchy(data)
    .sum((d) => d.data["Rs,millions"])
    .sort((a, b) => b.data["Rs,millions"] - a.data["Rs,millions"])
);

  const root = myTreemap(dataStratified);

Step 3: Using this Observable notebook as a guide, I proceeded to build the leaves of the treemap:

 const leaf = g
.selectAll("g.leaf")
// root.leaves() returns all of the leaf nodes
.data(root.leaves())
.enter()
.append("g")
.attr("class", "leaf")
// position each group at the top left corner of the rect
.attr("transform", (d) => `translate(${d.x0},${d.y0})`)
.style("font-size", 10);

Step 4: And appended it to the SVG I had created:

// Now we append the rects.


 leaf
    .append("rect")
    .attr("id", (d) => d.data.id)
    .attr("fill", (d) => {
      while (d.depth > 1) d = d.parent;
      return color(d.data.data.Child);
    })
    .attr("opacity", 0.7)
    // the width is the right edge position - the left edge position
    .attr("width", (d) => d.x1 - d.x0)
    // same for height, but bottom - top
    .attr("height", (d) => d.y1 - d.y0)
    // make corners rounded
    .attr("rx", 3)
    .attr("ry", 3);

The rest of the code is mostly styling and label placement so I don't think it's relevant to my question here but it can be viewed here: Github or CodeSandbox.


Solution

  • The mis-sized rects are due the total value of the treemap, you are essentially adding up the totals again on the parent node (because it is in the CSV column).

    You should only sum if it is the leaf node (meaning if the obj has no children). In short, if you check for children in the hierarchy.sum function, and only sum if there is no children, then it should calculate up the totals correctly

    let dataHierarchy = d3
        .hierarchy(dataStratified)
        .sum(d => (d.children ? 0 : d.data["Rs,millions"]))
        .sort((a, b) => b.data["Rs,millions"] - a.data["Rs,millions"]);