Размер прямоугольника в d3 js на основе суммы значений, но не количества элементов - PullRequest
3 голосов
/ 06 февраля 2020

Я работаю над визуализацией d3 js, в которой я хочу показать группы и подгруппы в зависимости от размера. У меня большая часть работает, за исключением размера прямоугольников. Если вы видите на загрузке, то есть все четыре размера блока имеют одинаковый размер, потому что все они имеют одинаковое нет. грантовых программ. Я хочу изменить размер этих прямоугольников на основе grant_award. Вот код, который я опубликовал по адресу:

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

Спасибо

1 Ответ

0 голосов
/ 10 февраля 2020

ОБНОВЛЕНО: индекс. js .value () для ссылки на сумму, так что каждому блоку будут назначены измерения на основе значения => сумма

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;
  };

};
...