首页 > 解决方案 > 将所有叶子添加到节点 d3

问题描述

我正在尝试使用 d3 制作一个图表(树),但我想显示没有子属性的数据(即它们只是一片叶子),分组在一个圆圈中,单击时显示所有它包含的节点。目前我只实现了这一点: 在此处输入图像描述

这是我的data.json:

{"name": "flatmap",
  "children": [
    {"operator_number": 1, "operator": "flatmap", "word": "WordCount example reads text files and counts how often words occur. The input is text files and the output is text files. each line of which contains a word and the count of how often it occured, separated by a tab."},
    {"operator_number": 1, "operator": "flatmap", "word": "Each mapper takes a line as input and breaks it into words. It then emits a key/value pair of the word and 1. Each reducer sums the counts for each word and emits a single key/value with the word and sum."},
    {"operator_number": 1, "operator": "flatmap", "word": "As an optimization. the reducer is also used as a combiner on the map outputs. This reduces the amount of data sent across the network by combining each word into a single record."},
    {
      "name": "map",
      "children": [
        {"operator_number": 2, "operator": "map", "word": "line", "count": 1, "id": 325590249},
        {"operator_number": 2, "operator": "map", "word": "example", "count": 1, "id": -1423699041},
        {"operator_number": 2, "operator": "map", "word": "of", "count": 2, "id": 1018490952},
        {"operator_number": 2, "operator": "map", "word": "count", "count": 1, "id": 220983708},
        {"operator_number": 2, "operator": "map", "word": "and", "count": 3, "id": 101213523},
        {"operator_number": 2, "operator": "map", "word": "WordCount", "count": 1, "id": -1806131025},
        {"operator_number": 2, "operator": "map", "word": "occured.", "count": 1, "id": -1230160008},
        {"operator_number": 2, "operator": "map", "word": "The", "count": 1, "id": -1004784477},
        {"operator_number": 2, "operator": "map", "word": "of", "count": 1, "id": 1018490952},
        {"operator_number": 2, "operator": "map", "word": "mapper", "count": 1, "id": 1207904727},
        {"operator_number": 2, "operator": "map", "word": "and", "count": 4, "id": 101213523},
        {"operator_number": 2, "operator": "map", "word": "as", "count": 1, "id": -680231157},
        {"operator_number": 2, "operator": "map", "word": "It", "count": 1, "id": 1027745163},
        {"operator_number": 2, "operator": "map", "word": "1.", "count": 1, "id": 1677874983},
        {"operator_number": 2, "operator": "map", "word": "across", "count": 1, "id": -762857328},
        {"operator_number": 2, "operator": "map", "word": "reduces", "count": 1, "id": -245012130},
        {"operator_number": 2, "operator": "map", "word": "map", "count": 1, "id": -683473284},
        {"operator_number": 2, "operator": "map", "word": "sent", "count": 1, "id": -1873763238},
        {"operator_number": 2, "operator": "map", "word": "also", "count": 1, "id": -1106031042},
        {"operator_number": 2, "operator": "map", "word": "outputs.", "count": 1, "id": 1544351802},
        {"operator_number": 2, "operator": "map", "word": "an", "count": 1, "id": 1302757461},
        {"operator_number": 2, "operator": "map", "word": "As", "count": 1, "id": 1898778492},
        {"operator_number": 2, "operator": "map", "word": "separated", "count": 1, "id": -1355638496},
        {"operator_number": 2, "operator": "map", "word": "it", "count": 1, "id": -147334043},
        {"operator_number": 2, "operator": "map", "word": "text", "count": 3, "id": 1471308580},
        {"operator_number": 2, "operator": "map", "word": "counts", "count": 1, "id": -341063633},
        {"operator_number": 2, "operator": "map", "word": "word", "count": 1, "id": -1996542359},
        {"operator_number": 2, "operator": "map", "word": "reads", "count": 1, "id": 1581597220},
        {"operator_number": 2, "operator": "map", "word": "tab.", "count": 1, "id": 64393762},
        {"operator_number": 2, "operator": "map", "word": "often", "count": 2, "id": -1659162332},
        {"operator_number": 2, "operator": "map", "word": "how", "count": 2, "id": -394402229},
        {"operator_number": 2, "operator": "map", "word": "input", "count": 1, "id": 398743969},
        {"operator_number": 2, "operator": "map", "word": "by", "count": 1, "id": 984685240},
        {"operator_number": 2, "operator": "map", "word": "pair", "count": 1, "id": -1791332846},
        {"operator_number": 2, "operator": "map", "word": "into", "count": 1, "id": 660477844},
        {"operator_number": 2, "operator": "map", "word": "sum.", "count": 1, "id": 1546120510},
        {"operator_number": 2, "operator": "map", "word": "reducer", "count": 1, "id": -162177284},
        {"operator_number": 2, "operator": "map", "word": "word", "count": 3, "id": -1996542359},
        {"operator_number": 2, "operator": "map", "word": "emits", "count": 2, "id": -512399732},
        {"operator_number": 2, "operator": "map", "word": "key/value", "count": 2, "id": 40637392},
        {"operator_number": 2, "operator": "map", "word": "data", "count": 1, "id": -1989676802},
        {"operator_number": 2, "operator": "map", "word": "This", "count": 1, "id": -144610220},
        {"operator_number": 2, "operator": "map", "word": "network", "count": 1, "id": 1134884044},
        {"operator_number": 2, "operator": "map", "word": "used", "count": 1, "id": 1115971993},
        {"operator_number": 2, "operator": "map", "word": "output", "count": 1, "id": 1728288230},
        {"operator_number": 2, "operator": "map", "word": "files", "count": 2, "id": 1433752967},
        {"operator_number": 2, "operator": "map", "word": "words", "count": 1, "id": 759231116},
        {"operator_number": 2, "operator": "map", "word": "which", "count": 1, "id": -112411180},
        {"operator_number": 2, "operator": "map", "word": "the", "count": 2, "id": -1147111243},
        {"operator_number": 2, "operator": "map", "word": "is", "count": 2, "id": 648427280},
        {"operator_number": 2, "operator": "map", "word": "files.", "count": 1, "id": 1064683136},
        {"operator_number": 2, "operator": "map", "word": "occur.", "count": 1, "id": -244884859},
        {"operator_number": 2, "operator": "map", "word": "a", "count": 2, "id": 1867108634},
        {"operator_number": 2, "operator": "map", "word": "contains", "count": 1, "id": -553938667},
        {"operator_number": 2, "operator": "map", "word": "each", "count": 1, "id": 1547773355},
        {"operator_number": 2, "operator": "map", "word": "breaks", "count": 1, "id": 1400666204},
        {"operator_number": 2, "operator": "map", "word": "then", "count": 1, "id": 1571541968},
        {"operator_number": 2, "operator": "map", "word": "takes", "count": 1, "id": -2142438010},
        {"operator_number": 2, "operator": "map", "word": "sums", "count": 1, "id": -957763246},
        {"operator_number": 2, "operator": "map", "word": "with", "count": 1, "id": -787307800},
        {"operator_number": 2, "operator": "map", "word": "for", "count": 1, "id": 429617153},
        {"operator_number": 2, "operator": "map", "word": "the", "count": 3, "id": -1147111243},
        {"operator_number": 2, "operator": "map", "word": "Each", "count": 2, "id": -827945881},
        {"operator_number": 2, "operator": "map", "word": "a", "count": 3, "id": 1867108634},
        {"operator_number": 2, "operator": "map", "word": "words.", "count": 1, "id": 1130073758},
        {"operator_number": 2, "operator": "map", "word": "single", "count": 1, "id": 667253393},
        {"operator_number": 2, "operator": "map", "word": "combining", "count": 1, "id": -1048244161},
        {"operator_number": 2, "operator": "map", "word": "amount", "count": 1, "id": -71540878},
        {"operator_number": 2, "operator": "map", "word": "combiner", "count": 1, "id": 1602507455},
        {"operator_number": 2, "operator": "map", "word": "optimization.", "count": 1, "id": 2044230185},
        {"operator_number": 2, "operator": "map", "word": "the", "count": 4, "id": -1147111243},
        {"operator_number": 2, "operator": "map", "word": "is", "count": 1, "id": 648427280},
        {"operator_number": 2, "operator": "map", "word": "record.", "count": 1, "id": 458306687},
        {"operator_number": 2, "operator": "map", "word": "on", "count": 1, "id": -2052619489},
        {
          "name": "reduceBykey",
          "children": [
            {"operator_number": 3, "operator": "reduceBykey", "word": "sent", "count": 1, "id": -1873763238},
            {"operator_number": 3, "operator": "reduceBykey", "word": "example", "count": 1, "id": -1423699041},
            {"operator_number": 3, "operator": "reduceBykey", "word": "The", "count": 1, "id": -1004784477},
            {"operator_number": 3, "operator": "reduceBykey", "word": "WordCount", "count": 1, "id": -1806131025},
            {"operator_number": 3, "operator": "reduceBykey", "word": "mapper", "count": 1, "id": 1207904727},
            {"operator_number": 3, "operator": "reduceBykey", "word": "line", "count": 2, "id": 325590249},
            {"operator_number": 3, "operator": "reduceBykey", "word": "across", "count": 1, "id": -762857328},
            {"operator_number": 3, "operator": "reduceBykey", "word": "As", "count": 1, "id": 1898778492},
            {"operator_number": 3, "operator": "reduceBykey", "word": "outputs.", "count": 1, "id": 1544351802},
            {"operator_number": 3, "operator": "reduceBykey", "word": "1.", "count": 1, "id": 1677874983},
            {"operator_number": 3, "operator": "reduceBykey", "word": "as", "count": 2, "id": -680231157},
            {"operator_number": 3, "operator": "reduceBykey", "word": "map", "count": 1, "id": -683473284},
            {"operator_number": 3, "operator": "reduceBykey", "word": "of", "count": 4, "id": 1018490952},
            {"operator_number": 3, "operator": "reduceBykey", "word": "reduces", "count": 1, "id": -245012130},
            {"operator_number": 3, "operator": "reduceBykey", "word": "also", "count": 1, "id": -1106031042},
            {"operator_number": 3, "operator": "reduceBykey", "word": "It", "count": 1, "id": 1027745163},
            {"operator_number": 3, "operator": "reduceBykey", "word": "count", "count": 1, "id": 220983708},
            {"operator_number": 3, "operator": "reduceBykey", "word": "an", "count": 1, "id": 1302757461},
            {"operator_number": 3, "operator": "reduceBykey", "word": "and", "count": 7, "id": 101213523},
            {"operator_number": 3, "operator": "reduceBykey", "word": "occured.", "count": 1, "id": -1230160008},
            {"operator_number": 3, "operator": "reduceBykey", "word": "it", "count": 2, "id": -147334043},
            {"operator_number": 3, "operator": "reduceBykey", "word": "reducer", "count": 2, "id": -162177284},
            {"operator_number": 3, "operator": "reduceBykey", "word": "counts", "count": 2, "id": -341063633},
            {"operator_number": 3, "operator": "reduceBykey", "word": "text", "count": 3, "id": 1471308580},
            {"operator_number": 3, "operator": "reduceBykey", "word": "pair", "count": 1, "id": -1791332846},
            {"operator_number": 3, "operator": "reduceBykey", "word": "how", "count": 2, "id": -394402229},
            {"operator_number": 3, "operator": "reduceBykey", "word": "reads", "count": 1, "id": 1581597220},
            {"operator_number": 3, "operator": "reduceBykey", "word": "tab.", "count": 1, "id": 64393762},
            {"operator_number": 3, "operator": "reduceBykey", "word": "into", "count": 2, "id": 660477844},
            {"operator_number": 3, "operator": "reduceBykey", "word": "This", "count": 1, "id": -144610220},
            {"operator_number": 3, "operator": "reduceBykey", "word": "key/value", "count": 2, "id": 40637392},
            {"operator_number": 3, "operator": "reduceBykey", "word": "emits", "count": 2, "id": -512399732},
            {"operator_number": 3, "operator": "reduceBykey", "word": "word", "count": 5, "id": -1996542359},
            {"operator_number": 3, "operator": "reduceBykey", "word": "data", "count": 1, "id": -1989676802},
            {"operator_number": 3, "operator": "reduceBykey", "word": "by", "count": 2, "id": 984685240},
            {"operator_number": 3, "operator": "reduceBykey", "word": "used", "count": 1, "id": 1115971993},
            {"operator_number": 3, "operator": "reduceBykey", "word": "separated", "count": 1, "id": -1355638496},
            {"operator_number": 3, "operator": "reduceBykey", "word": "network", "count": 1, "id": 1134884044},
            {"operator_number": 3, "operator": "reduceBykey", "word": "often", "count": 2, "id": -1659162332},
            {"operator_number": 3, "operator": "reduceBykey", "word": "sum.", "count": 1, "id": 1546120510},
            {"operator_number": 3, "operator": "reduceBykey", "word": "input", "count": 2, "id": 398743969},
            {"operator_number": 3, "operator": "reduceBykey", "word": "words.", "count": 1, "id": 1130073758},
            {"operator_number": 3, "operator": "reduceBykey", "word": "output", "count": 1, "id": 1728288230},
            {"operator_number": 3, "operator": "reduceBykey", "word": "sums", "count": 1, "id": -957763246},
            {"operator_number": 3, "operator": "reduceBykey", "word": "is", "count": 3, "id": 648427280},
            {"operator_number": 3, "operator": "reduceBykey", "word": "contains", "count": 1, "id": -553938667},
            {"operator_number": 3, "operator": "reduceBykey", "word": "combiner", "count": 1, "id": 1602507455},
            {"operator_number": 3, "operator": "reduceBykey", "word": "files", "count": 2, "id": 1433752967},
            {"operator_number": 3, "operator": "reduceBykey", "word": "record.", "count": 1, "id": 458306687},
            {"operator_number": 3, "operator": "reduceBykey", "word": "takes", "count": 1, "id": -2142438010},
            {"operator_number": 3, "operator": "reduceBykey", "word": "a", "count": 7, "id": 1867108634},
            {"operator_number": 3, "operator": "reduceBykey", "word": "on", "count": 1, "id": -2052619489},
            {"operator_number": 3, "operator": "reduceBykey", "word": "each", "count": 3, "id": 1547773355},
            {"operator_number": 3, "operator": "reduceBykey", "word": "with", "count": 1, "id": -787307800},
            {"operator_number": 3, "operator": "reduceBykey", "word": "then", "count": 1, "id": 1571541968},
            {"operator_number": 3, "operator": "reduceBykey", "word": "which", "count": 1, "id": -112411180},
            {"operator_number": 3, "operator": "reduceBykey", "word": "files.", "count": 1, "id": 1064683136},
            {"operator_number": 3, "operator": "reduceBykey", "word": "breaks", "count": 1, "id": 1400666204},
            {"operator_number": 3, "operator": "reduceBykey", "word": "combining", "count": 1, "id": -1048244161},
            {"operator_number": 3, "operator": "reduceBykey", "word": "for", "count": 1, "id": 429617153},
            {"operator_number": 3, "operator": "reduceBykey", "word": "words", "count": 1, "id": 759231116},
            {"operator_number": 3, "operator": "reduceBykey", "word": "Each", "count": 2, "id": -827945881},
            {"operator_number": 3, "operator": "reduceBykey", "word": "occur.", "count": 1, "id": -244884859},
            {"operator_number": 3, "operator": "reduceBykey", "word": "amount", "count": 1, "id": -71540878},
            {"operator_number": 3, "operator": "reduceBykey", "word": "optimization.", "count": 1, "id": 2044230185},
            {"operator_number": 3, "operator": "reduceBykey", "word": "single", "count": 2, "id": 667253393},
            {"operator_number": 3, "operator": "reduceBykey", "word": "the", "count": 9, "id": -1147111243}
          ]
        }
      ]
    }
  ]
}

    <!DOCTYPE html>
    <meta charset="UTF-8">

    <!-- Custom styling -->
    <link rel="stylesheet" href="less/style.css">
    <body>

    <!-- load the d3.js library -->
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script src="js/d3-tip.js"></script>
    <script>

    //var treeData = d3.json("data/data.json");




    // Set the dimensions and margins of the diagram
    var margin = {top: 20, right: 90, bottom: 30, left: 90},
        width = 960 - margin.left - margin.right,
        height = 600 - margin.top - margin.bottom;

    var colorScale = d3.scaleLinear()
        .domain([0, 1])
            .range(['red', 'green']);
    var widthScale = d3.scaleLinear()
            .domain([1,80])
            .range([1, 10]);

    // append the svg object to the body of the page
    // appends a 'group' element to 'svg'
    // moves the 'group' element to the top left margin
    var svg = d3.select("body").append("svg")
        .attr("width", width + margin.right + margin.left)
        .attr("height", height + margin.top + margin.bottom)
      .append("g")
        .attr("transform", "translate("
              + margin.left + "," + margin.top + ")");

    var i = 0,
        duration = 750,
        root;
    // show information about data by mouseover
    var tip = d3.tip()
      .attr('class', 'd3-tip')
      .offset([-10, 0])
      .html(function(d) {
        var content;
        if(d.data.hasOwnProperty("name")){
          //console.log("hier 0");
          content =  "<strong>Operator Number:</strong> <span>" + d.data.name + "</span>";
        }
        if(d.data.hasOwnProperty("children")){
          //console.log("hier 5" + d.data.children.length);
          var count_children = d.data.children.length
          content +=  "<br /><strong>Number of Data:</strong> <span>" + count_children  + "</span>";
        } else{
          if(d.data.hasOwnProperty("operator_number")){
            //console.log("hier 1"+ d.data.operator_number);
            content += "<strong>Operator Number:</strong> <span>" + d.data.operator_number + "</span>";
          }
          if(d.data.hasOwnProperty("operator")){
            //console.log("hier 2"+ d.data.operator);
            content += "<br /><strong>Operator Name:</strong> <span>" + d.data.operator + "</span>";
          }
          if(d.data.hasOwnProperty("word")){
            //console.log("hier 3"+ d.data.word);
            content += "<br /><strong>Word:</strong> <span>" + d.data.word + "</span>";
          }
          if(d.data.hasOwnProperty("id")){
            //console.log("hier 4", d.data.id);
            content+= "<strong>ID:</strong> <span>" + d.data.id + "</span>";
          }
        }
        if (content.length >0 ){
          return content;
        }else{
          return -1;
        }
      })

    svg.call(tip);
    // declares a tree layout and assigns the size
    var treemap = d3.tree().size([height, width]);

    d3.json("data/data-without-brackes.json",function(error, treeData){
      // Assigns parent, children, height, depth
      console.log("correct");
      root= d3.hierarchy(treeData, function(d) {
          return d.children;
      });
      console.log("root", root.children[1]);
      root.x0 = height / 4;
      root.y0 = 0;

      // Collapse after the second level
      root.children.forEach(collapse);

      update(root);

      // Collapse the node and all it's children
      function collapse(d) {
        if(d.children) {
          d._children = d.children
          d._children.forEach(collapse)
          d.children = null
        }
      }

      function update(source) {

        // Assigns the x and y position for the nodes
        var treeData = treemap(root);
        console.log("line 113- treeData" , treeData);
        // Compute the new tree layout.
        var nodes = treeData.descendants(),
            links = treeData.descendants().slice(1);

        // Normalize for fixed-depth.
        nodes.forEach(function(d){
          // sort nodes, just the operator_nodes
          if(d.hasOwnProperty("children")){
            operator_nodes.push(d);
          }else{
            data_nodes.push(d);
          }
        });

        nodes.forEach(function(d){
          if(d.hasOwnProperty("children")){
            d.y = d.depth * 180;
            d.x = 275;
          }else{
            d.y= (d.depth * 90) + (90 * (d.depth -1));
            d.x = 275;
          }

        });
        //nodes.forEach(function(d){ d.y = 180});


        // ****************** Nodes section ***************************

        // Update the nodes

        var node = svg.selectAll('g.node')
            .data(nodes, function(d) { return d.id || (d.id = ++i);  })
            .on('mouseover', tip.show)
            .on('mouseout', tip.hide);

        // Enter any new modes at the parent's previous position.

        var nodeEnter = node.enter().append('g')
          .attr("transform", function(d) {
              return "translate(" + source.y0 + "," + source.x0 + ")";
          })
          .attr("class", function(d, i ){
            return "node_" + i // i provides a unique id
            + " node"
            + " cell-level-" + d.depth
            + (!d.children
            ? " leaf"
            :(d.depth === 0
            ? " root"
            : " internal"));
          })
          .attr("id", function(d){
            if(d.children){
              return "operator";
            }else{
              return "data";
            }
          })
          .attr("display", function(d){
            if(d.hasOwnProperty("children")){
              return "block";
            }else{
              return "block";
            }
          })
          .on('click', click);

        // Add Circle for the nodes
        var symbolGeneratorDiamond = d3.symbol()
            .type(d3.symbolDiamond)
            .size(180);
        var pathDataDiamond = symbolGeneratorDiamond();

        var symbolGeneratorCircle = d3.symbol()
          .type(d3.symbolCircle)
          .size(180);
        var pathDataCircle = symbolGeneratorCircle();

    // Give shape, if has children then diamond, else circle (is data)

        nodeEnter.append("path")
          .style("stroke", "black")
          .style("fill", "white")
          .attr("d", function(d) {
            if(d.hasOwnProperty("children")){
              return pathDataDiamond;
            }else{
              return pathDataCircle;
            }
          })
          .style("fill", function(d) {
              return d._children ? "lightsteelblue" : "#fff";
           })


        // Add labels for the nodes
        nodeEnter.append('text')
            .attr("dy", ".35em")
            .attr("x", function(d) {
                return d.children || d._children ? -13 : 13;
            })
            .attr("text-anchor", function(d) {
                return d.children || d._children ? "end" : "start";
            })
            .text(function(d) { if(d.data.name )return d.data.name;
            else{
              return d.data.word;
            }  })
            .attr("display", function(d){
              if(d.data.children){
                return "block";
              }else{
                return "none";
              }
            });

        // UPDATE
        var nodeUpdate = nodeEnter.merge(node);

        // Transition to the proper position for the node
        nodeUpdate.transition()
          .duration(duration)
          .attr("transform", function(d) {
              return "translate(" + d.y + "," + d.x + ")";
           });

        // Update the node attributes and style
        nodeUpdate.select('path')
          .style("fill", function(d) {
              return d._children ? "lightsteelblue" : "#fff";
          })
          .attr('cursor', 'pointer');


        // Remove any exiting nodes
        var nodeExit = node.exit().transition()
            .duration(duration)
            .attr("transform", function(d) {
                return "translate(" + source.y + "," + source.x + ")";
            })
            .remove();

        // On exit reduce the node circles size to 0
        nodeExit.select('path')
          .attr('r', 1e-6);

        // On exit reduce the opacity of text labels
        nodeExit.select('text')
          .style('fill-opacity', 1e-6);

        // ****************** links section ***************************

        // Update the links...
        var link = svg.selectAll('path.link')
            .data(links, function(d) { return d.id; })
                .style('stroke-width', function(d){
              return widthScale(d.data.value)
            });

        // Enter any new links at the parent's previous position.
        var linkEnter = link.enter().insert('path', "g")
            .attr("class", "link")
            .attr('d', function(d){
              var o = {x: source.x0, y: source.y0}
              return diagonal(o, o)
            })
            .attr("display", function(d){
              if(d.hasOwnProperty("children")){
                return "block";
              }else{
                return "block";
              }
            })
                .style('stroke-width', function(d){
              return widthScale(d.data.value)
            });

        // UPDATE
        var linkUpdate = linkEnter.merge(link);

        // Transition back to the parent element position
        linkUpdate.transition()
            .duration(duration)
            .attr('d', function(d){ return diagonal(d, d.parent) });

        // Remove any exiting links
        var linkExit = link.exit().transition()
            .duration(duration)
            .attr('d', function(d) {
              var o = {x: source.x, y: source.y}
              return diagonal(o, o)
            })
                .style('stroke-width', function(d){
              return widthScale(d.data.value)
            })
            .remove();

        // Store the old positions for transition.
        nodes.forEach(function(d){
          d.x0 = d.x;
          d.y0 = d.y;
        });

        // Creates a curved (diagonal) path from parent to the child nodes
        function diagonal(s, d) {

          path = `M ${s.y} ${s.x}
                  C ${(s.y + d.y) / 2} ${s.x},
                    ${(s.y + d.y) / 2} ${d.x},
                    ${d.y} ${d.x}`

          return path
        }

        // Toggle children on click.
        function click(d) {
          if (d.children) {
              d._children = d.children;
              d.children = null;
            } else {
              d.children = d._children;
              d._children = null;
            }
          update(d);
        }
      }
    })




    </script>


    </body>

标签: jquerysvgd3.jsgroup-by

解决方案


推荐阅读