d3js中矩形的大小基于值的总和,而不是项目数 [英] Size of rectangle in d3js based on sum of values but not count of items

查看:65
本文介绍了d3js中矩形的大小基于值的总和,而不是项目数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在d3js可视化上工作,我想根据大小显示组和子组,除了矩形的大小外,我大部分工作都在进行.如果在加载时看到,则所有四个框的大小都相同,因为它们的编号均相同.赠款计划.我想根据grant_award更改这些矩形的大小.这是我在以下位置发布的代码:

https://gist.github.com/senthilthyagarajan/bfa1b611c0e91b1a304c0b8f32555>

谢谢

UPDATED:index.js .value()指代总和,以便根据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;
  };

};

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

解决方案

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

};

这篇关于d3js中矩形的大小基于值的总和,而不是项目数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆