为什么`treemap.nodes`看不到对象的更新?

时间:2015-04-11 22:49:10

标签: javascript json d3.js

我用d3.js制作树形图布局。我的树有比我可能想象的更多的叶节点,所以我想修剪树以保持每个分支的叶节点的百分比,但是将叶的总数减少到一些小的n

我已经成功实现了一个for循环,它以所需的方式修剪了data对象的子节点,但当我调用treemap.nodes(data)时,我得到一个包含原始节点数的列表作为返回值而不是具有较少叶子的减少的节点列表。

这是一个重现问题的最小例子。

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>

<body>   
    <script type="text/javascript">

    var w = 1280 - 80,
        h = 800 - 180;

    // Declare treemap
    var treemap = d3.layout.treemap()
        .round(false)
        .size([w, h])
        .sticky(true)
        .value(function(d) { return d.size; });

    // Load data
    d3.json("demo.json", function(data) {   

        // Trim data 
        var categories = treemap.nodes(data)
            .filter(function(d) { return d.depth==1; });

        n = 10; //This is the maximum number of leaves I want to have
        var total_value = treemap.nodes(data)[0].value;
        for(var vertex of categories) 
        {
            var num_children = vertex.children.length;
            var percentage_screen = num_children / total_value;
            var max_leaves = Math.max(1, Math.floor(n * percentage_screen));
            vertex.children = vertex.children.slice(0,max_leaves)
        }   

        // Here I try to get a list of the leaves       
        var nodes = treemap.nodes(data)
            .filter(function(d) { return !d.children; });

        // This print statement prints 20, the original number of leaves
        console.log(nodes.length);
        // But if I recursively count the number of leaves, I get the new smaller number, 10
        console.log(recursive_node_counter(data));
    });

    function recursive_node_counter(d){
        if('children' in d){
            var num_child = 0;
            for(child of d.children){
                num_child += recursive_node_counter(child);
            } 
            return num_child;
        } else {
            return 1
        }
    };


    </script>
</body>
</html>

与此json一起使用:demo.json

{"name": "root", "children": 
    [{"name": "branch_A", "children": 
        [{"name": "A_leaf", "size": 1},
        {"name": "A_leaf", "size": 1},
        {"name": "A_leaf", "size": 1},
        {"name": "A_leaf", "size": 1},
        {"name": "A_leaf", "size": 1}, 
         {"name": "A_leaf", "size": 1}]},
    {"name": "branch_B", "children": 
        [{"name": "B_leaf", "size": 1},
        {"name": "B_leaf", "size": 1},
        {"name": "B_leaf", "size": 1},
        {"name": "B_leaf", "size": 1},
        {"name": "B_leaf", "size": 1},
        {"name": "B_leaf", "size": 1},
        {"name": "B_leaf", "size": 1},
        {"name": "B_leaf", "size": 1},
        {"name": "B_leaf", "size": 1}, 
         {"name": "B_leaf", "size": 1}]},
    {"name": "branch_C", "children": 
        [{"name": "C_leaf", "size": 1},
        {"name": "C_leaf", "size": 1},
        {"name": "C_leaf", "size": 1}, 
         {"name": "C_leaf", "size": 1}]}]}

为什么treemap.nodes(data)看不到data变量的更改?

2 个答案:

答案 0 :(得分:2)

我的观点是d3意味着数据驱动所以不是试图破解布局对象,而是在将数据加入布局之前修改数据总是更好。< / p>

一个解决方案:使用数据的暂存器版本和树形图来获得树图行为的好处来进行修剪,然后使用修改后的数据创建一个新实例...

var w = 1280 - 80,
        h = 800 - 180;

// Load data
d3.json("demo.json", function (data) {

    //  make a temp object to use treemap behaviour
    var treemap = d3.layout.treemap()
            .sticky(true)
            .value(function (d) { return d.size; }),
    //  cache original data
            tempData = $.extend(true, {}, data),
            //***snap shot 1
    // Trim original data using temp data
            tempNodesData = treemap.nodes(tempData),
            //***snap shot 2
            total_value = tempNodesData[0].value,

            categories = tempNodesData
            .filter(function (d) { return d.depth == 1; }),

            n = 10; //This is the maximum number of leaves I want to have

    categories.forEach(function (vertex, i, categories) {
        var num_children = vertex.children.length,
                percentage_screen = num_children / total_value,
                max_leaves = Math.max(1, Math.floor(n * percentage_screen));

        data.children[i].children = vertex.children.slice(0,max_leaves)
    })

    // re-Declare treemap
    var treemap = d3.layout.treemap()
            .round(false)
            .size([w, h])
            .sticky(true)
            .value(function (d) { return d.size; }),

    // Here I try to get a list of the leaves       
            nodes = treemap.nodes(data)
                        .filter(function(d) { return !d.children; });

    // This print statement prints 20, the original number of leaves
    alert('nodes.length: ' + nodes.length + '\n' + 'recursive_node_counter: ' + recursive_node_counter(data));
    d3.select('body').insert('div', 'script').attr('class', 'logWindow').style({'color': 'red;'})
    .html('nodes.length: ' + nodes.length + '</br>' + 'recursive_node_counter: ' + recursive_node_counter(data));

});

function recursive_node_counter(d){
    if('children' in d){
        var num_child = 0;

        d.children.forEach( function(child){
                num_child += recursive_node_counter(child);
        })
    return num_child;
} else {
                        return 1
}
};

在回复下面的评论时,tempData最初看起来像这样:

***snap shot 1

treemap.nodes(tempData)语句后面看起来像这个

***snap shot 2

如果我保留原始形式的数据,那么我不必浪费时间来确定额外的属性是否重要(现在和未来!)

答案 1 :(得分:1)

以下控制台声明更能说明问题:

d3.json("./demo.json", function(data) {   

    // Trim data 
    var categories = treemap.nodes(data)
        .filter(function(d) { return d.depth==1; });

    n = 10; //This is the maximum number of leaves I want to have
    var total_value = treemap.nodes(data)[0].value;
    for(var vertex of categories) 
    {
        var num_children = vertex.children.length;
        var percentage_screen = num_children / total_value;
        var max_leaves = Math.max(1, Math.floor(n * percentage_screen));
        vertex.children = vertex.children.slice(0,max_leaves);
    }

    // Here I try to get a list of the leaves       
    console.log(data);
    var nodes = treemap.nodes(data)
        .filter(function(d) { return !d.children; });
    console.log(data);
});

当我们运行此代码时,我们得到

的相同意外输出
20
10


为什么?如果我们查看treemap.nodes(data)调用,它就变得清晰了:

function treemap(d) {
  var nodes = stickies || hierarchy(d), root = nodes[0];
  root.x = 0;
  root.y = 0;
  root.dx = size[0];
  root.dy = size[1];
  if (stickies) hierarchy.revalue(root);
  scale([ root ], root.dx * root.dy / root.value);
  (stickies ? stickify : squarify)(root);
  if (sticky) stickies = nodes;
  return nodes;
}

...
...

hierarchy.revalue = function(root) {
  if (value) {
    d3_layout_hierarchyVisitBefore(root, function(node) {
        if (node.children) node.value = 0;
    });
    d3_layout_hierarchyVisitAfter(root, function(node) {
      var parent;
      if (!node.children) node.value = +value.call(hierarchy, node, node.depth) || 0;
      if (parent = node.parent) parent.value += node.value;
    });
  }
  return root;
};

在第一次调用treemap.nodes(data)时,stickies被设置为未修剪的数据。在第二次调用treemap.nodes(data)时,该函数使用来自第一次调用的数据通过&#34; stickies&#34;变量。在同一个第二次调用中,函数然后对传入的修剪数据调用hierarchy.revalue(root),这会更新相关对象。