Size of rectangle in d3js based on sum of values but not count of items

拥有回忆 提交于 2020-02-22 05:29:28

问题


I am working on a d3js visualization where I want to show groups and subgroups based on size.I have most of it working except for the size of the rectangles. If you see on load viz all four box size are of same size because all of them have same no. of grant programs. I want to change the size of these rectangles based on grant_award. Here is the code I have published at :

https://gist.github.com/senthilthyagarajan/bfa1b611c0e91b1a304c0b8f32555daf

Thank you


回答1:


UPDATED:index.js .value() to refer to the sum, so that each block would be assigned dimensions based on value=>sum

index.js

var log = console.log;
var margin = {top: 30, right: 0, bottom: 0, left: 0};
var width = 800;
var height = 670 - margin.top - margin.bottom;
var transitioning;

// x axis scale
var x = d3.scale.linear()
.domain([0, width])
.range([0, width]);

// y axis scale
var y = d3.scale.linear()
.domain([0, height])
.range([0, height]);



// treemap layout
var treemap = d3.layout.treemap()
.children(function(d, depth) { return depth ? null : d._children; })
.sort(function(a, b) { return a.value - b.value; })
.ratio(height / width * 0.5 * (1 + Math.sqrt(5)))
.value(d=>d.sum)
.round(false);

// define svg
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.bottom + margin.top)
.style("margin-left", -margin.left + "px")
.style("margin.right", -margin.right + "px")
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`)
.style("shape-rendering", "crispEdges");

// top gray rectangle
var grandparent = svg.append("g")
.attr("class", "grandparent");

// Add grandparent rect
grandparent.append("rect")
.attr("y", -margin.top)
.attr("width", width)
.attr("height", margin.top)
.style("fill", "#d9d9d9");

// Add grandparent text
grandparent.append("text")
.attr("class", "title")
.attr("x", 6)
.attr("y", 6 - margin.top)
.attr("dy", ".75em");        

// custom root initializer
function initialize(root) {
  root.x = root.y = 0;
  root.dx = width;
  root.dy = height;
  root.depth = 0;
};

// Aggregate the values for internal nodes. This is normally done by the
// treemap layout, but not here because of our custom implementation.
// We also take a snapshot of the original children (_children) to avoid
// the children being overwritten when when layout is computed.
// Alteration made for function to be iterative
function accumulateVal(d, attr) {
  return accumulate(d);

  function accumulate(d) {
    return (d._children = d.children)
    // recursion step, note that p and v are defined by reduce
    ? d[attr] = d.children.reduce(function(p, v) {return p + accumulate(v); }, 0)
    : d[attr];
  };
};

// Compute the treemap layout recursively such that each group of siblings
// uses the same size (1×1) rather than the dimensions of the parent cell.
// This optimizes the layout for the current zoom state. Note that a wrapper
// object is created for the parent node for each group of siblings so that
// the parent’s dimensions are not discarded as we recurse. Since each group
// of sibling was laid out in 1×1, we must rescale to fit using absolute
// coordinates. This lets us use a viewport to zoom.
function layout(d) {
  if (d._children) {
    // treemap nodes comes from the treemap set of functions as part of d3
    treemap.nodes({_children: d._children});
    d._children.forEach(function(c) {
      c.x = d.x + c.x * d.dx;
      c.y = d.y + c.y * d.dy;
      c.dx *= d.dx;
      c.dy *= d.dy;
      c.parent = d;
      // recursion
      layout(c);
    });
  }
};

d3.csv("Public_Grants_2010-2012(July-28-2018).csv", treeMapZoomable);

function treeMapZoomable(error, grants) {
  if (error) throw error;

  // Define color scale
  var allValues = grants.map(function(d) { return [d["GRANT_PROGRAM"], d["YEAR"]]; })
  .reduce(function(acc, curVal) { return acc.concat(curVal); }, []);
  var scaleOrdNames = [...new Set(allValues)];
  var colorScale = d3.scale.ordinal().domain(scaleOrdNames)
  .range(['#66c2a5','#fc8d62','#8da0cb','#e78ac3','#a6d854','#ffd92f','#e5c494']);

  // Define aggregrated grant programs
  var aggGrantPrograms = d3.nest().key(function(d) { return d.YEAR; })
  .key(function(d) { return d["GRANT_PROGRAM"]; })
  .entries(grants)
  .map(function(d) {
    var dValues = d.values.map(function(g){
      var gValues = g.values;
      var sum = gValues.map(function(p) { return p["GRANT_AWARD"]; })
      .reduce(function(acc, curVal) { return acc + (+curVal); }, 0);

      return {
        name: g.key,
        value: gValues.length,
        sum: sum
      };
    });

    return {
      name: d.key,
      children: dValues
    };
  });

  // Root for hierarchy
  var rootObject = {name: "Audience Segment", children: aggGrantPrograms};

  initialize(rootObject);
  ["value", "sum"].forEach(function(d) { accumulateVal(rootObject, d); });
  layout(rootObject);
  display(rootObject);

  function display(d) {
    // Grandparent when clicked transitions the children out to parent 
    grandparent
    .datum(d.parent)
    .on("click", transition)
    .select("text.title")
    .text(name(d));

    grandparent
    .datum(d.parent)
    .select("rect");

    var g1 = svg.insert("g", ".grandparent")
    .datum(d)
    .attr("class", "depth");

    var g = g1.selectAll("g")
    .data(d._children)
    .enter().append("g");

    // When parent is clicked transitions to children
    g.filter(function(d) { return d._children; })
    .classed("children", true)
    .on("click", transition);

    g.selectAll(".child")
    .data(function(d) { return d._children || [d]; })
    .enter().append("rect")
    .attr("class", "child")
    .call(rect);

    g.append("rect")
    .attr("class", "parent")
    .call(rect)
    .append("title")
    .text(function(d) { return `Audience Segment: ${d.name}`; });

    // Appending year, grants, and sum texts for each g
    var textClassArr = [
      {class: "year", accsor: function(d) { return d.name; }}, 
      {class: "grants", accsor: function(d) { return `${d.value} segments`; }}, 
      {class: "sum", accsor: function(d) { return `${d.sum.toLocaleString()} sample size`; }}
      ];

    textClassArr.forEach(function(p, i) {
      g.append("text")
      .attr("class", p.class)
      .attr("dy", ".5em")
      .text(p.accsor)
      .call(rectText(i));
    });

    function transition(d) {
      if (transitioning || !d) return;
      transitioning = true;

      var g2 = display(d);
      var t1 = g1.transition().duration(750);
      var t2 = g2.transition().duration(750);

      // Update the domain only after entering new elements.
      x.domain([d.x, d.x + d.dx]);
      y.domain([d.y, d.y + d.dy]);

      // Enable anti-aliasing during the transition.
      svg.style("shape-rendering", null);

      // Draw child nodes on top of parent nodes.
      svg.selectAll(".depth").sort(function(a, b) { return a.depth - b.depth; });

      // Fade-in entering text.
      g2.selectAll("text").style("fill-opacity", 0);

      // Transition to the new view.
      textClassArr.forEach(function(d, i) {
        var textClass = `text.${d.class}`;
        var textPlacmt = rectText(i);

        t1.selectAll(textClass).call(textPlacmt).style("fill-opacity", 0);
        t2.selectAll(textClass).call(textPlacmt).style("fill-opacity", 1);
      });

      t1.selectAll("rect").call(rect);
      t2.selectAll("rect").call(rect);

      // Remove the old node when the transition is finished.
      t1.remove().each("end", function() {
        svg.style("shape-rendering", "crispEdges");
        transitioning = false;
      });                 
    };

    return g;
  };

  function rectText(i) {
    return grantText;

    function grantText(text) {
      return text.attr("x", function(d) { return x(d.x) + 6; })
      .attr("y", function(d) { return y(d.y) + (25 + (i * 18)); })
      .attr("fill", "black");
    };
  };

  function rect(rect) {
    rect.attr("x", function(d) { return x(d.x); })
    .attr("y", function(d) { return y(d.y); })
    .attr("width", function(d) { return x(d.x + d.dx) - x(d.x); })
    .attr("height", function(d) { return y(d.y + d.dy) - y(d.y); })
    .attr("fill", function(d) { return colorScale(d.name);});
  };

  function name(d) {
    return d.parent
    ? `${name(d.parent)}.${d.name} - Total Segments: ${d.value} - 
      Sample Size: ${d.sum.toLocaleString()}`
    : d.name;
  };

};


来源:https://stackoverflow.com/questions/60100877/size-of-rectangle-in-d3js-based-on-sum-of-values-but-not-count-of-items

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!