我有一个如下所示的数组:
var arrays = [[1,2,3,4,5],
[1,2,6,4,5],
[1,3,6,4,5],
[1,2,3,6,5],
[1,7,5],
[1,7,3,5]]
我想使用d3.nest()甚至只是标准的javascript将这些数据转换为我可以与d3.partition一起使用的嵌套数据结构。具体来说,我想创建此处找到的flare.json
data format。
我想用d3.nest()创建的json对象的级别对应于数组中的索引位置。请注意,1
位于上述示例数据中所有子数组中的第一个位置;因此,它在树的根部。在数组的下一个位置有三个值2
,3
和7
,因此,根值1
有3个子项。此时树看起来像这样:
1
/ | \
2 3 7
在子阵列的第三个位置,有四个值3
,5
和6
。这些孩子将按照以下方式进入树中:
1
____|___
/ | \
2 3 7
/ \ / / \
3 6 6 3 5
如何使用d3.nest()生成此数据结构?上面显示的示例数据的完整数据结构应如下所示:
{"label": 1,
"children": [
{"label": 2, "children": [
{"label": 3, "children": [
{"label": 4, "children": [
{"label": 5}
]},
{"label": 6, "children": [
{"label": 5}
]}
]},
{"label": 6, "children": [
{"label": 4, "children": [
{"label": 5}
]}
]},
{"label": 3, "children": [
{"label": 6, "children": [
{"label": 4, "children": [
{"label": 5}
]}
]}
]},
{"label": 7, "children": [
{"label": 3, "children": [
{"label": 5}
]},
{"label": 5}
]}
]}
]}
我正在尝试使用类似的东西转换我的数组数据结构(非常错误):
var data = d3.nest()
.key(function(d, i) { return d.i; })
.rollup(function(d) { return d.length; })
我已经敲打了一个星期,试图理解如何从阵列数组中产生这种分层数据结构。如果有人可以帮助我,我将非常感激。
.keys()
应用于数据,所以我无法手动编写这样的函数。
答案 0 :(得分:12)
这是一个更简单的函数,只使用嵌套的for
- 循环来循环遍历每组数组中的所有路径指令。
为了更容易找到具有给定标签的子元素,我已将children
实现为数据对象/关联数组而不是编号数组。如果你想要非常健壮,可以使用d3.map来解释该链接所描述的原因,但是如果你的标签实际上是整数而不是问题。无论哪种方式,它只是意味着当你需要作为一个数组访问子节点时(例如,对于d3布局函数),你必须指定一个函数来从对象的值中创建一个数组 - {{3实用程序功能为你做到了。
关键代码:
var root={},
path, node, next, i,j, N, M;
for (i = 0, N=arrays.length; i<N; i++){
//for each path in the data array
path = arrays[i];
node = root; //start the path from the root
for (j=0,M=path.length; j<M; j++){
//follow the path through the tree
//creating new nodes as necessary
if (!node.children){
//undefined, so create it:
node.children = {};
//children is defined as an object
//(not array) to allow named keys
}
next = node.children[path[j]];
//find the child node whose key matches
//the label of this step in the path
if (!next) {
//undefined, so create
next = node.children[path[j]] =
{label:path[j]};
}
node = next;
// step down the tree before analyzing the
// next step in the path.
}
}
使用示例数据数组和基本的d3.values(object)
图表方法实现:
cluster dendogram 德尔>
编辑添加: 正如评论中所提到的,要使输出完全符合要求:
像这样:
root = d3.values(root.children)[0];
//this is the root from the original data,
//assuming all paths start from one root, like in the example data
//recurse through the tree, turning the child
//objects into arrays
function childrenToArray(n){
if (n.children) {
//this node has children
n.children = d3.values(n.children);
//convert to array
n.children.forEach(childrenToArray);
//recurse down tree
}
}
childrenToArray(root);
答案 1 :(得分:1)
如果扩展Array
的规范,那实际上并不复杂。基本思想是逐级构建树级,一次获取每个数组元素并与前一个数组元素进行比较。这是代码(减去扩展名):
function process(prevs, i) {
var vals = arrays.filter(function(d) { return prevs === null || d.slice(0, i).compare(prevs); })
.map(function(d) { return d[i]; }).getUnique();
return vals.map(function(d) {
var ret = { label: d }
if(i < arrays.map(function(d) { return d.length; }).max() - 1) {
tmp = process(prevs === null ? [d] : prevs.concat([d]), i+1);
if(tmp.filter(function(d) { return d.label != undefined; }).length > 0)
ret.children = tmp;
}
return ret;
});
}
不保证它不会因边缘情况而中断,但它似乎可以正常处理您的数据。
完成jsfiddle here。
一些更详细的解释:
filter
完成那些与我们当前(部分)路径不同的prevs
来完成的。一开始,prevs
为null
,不会过滤任何内容。i
元素)对应的值。重复过滤。这是由.map()
和.getUnique()
。vals.map()
)。对于每个,我们设置label
属性。其余代码确定是否有子节点并通过递归调用获取它们。为此,我们首先检查数组中是否还有元素,即如果我们处于树的最深层。如果是这样,我们进行递归调用,传入包含我们当前正在处理的元素和下一级(prev
)的新i+1
。最后,我们检查这个递归调用的结果是否为空元素 - 如果只有空子元素,我们不保存它们。这是必要的,因为并非所有阵列(即并非所有路径)都具有相同的长度。答案 2 :(得分:0)
由于不赞成使用d3-collection
而推荐使用d3.array
,因此我们可以使用d3.groups
来实现与d3.nest
一起使用的功能:
var input = [
[1, 2, 3, 4, 5],
[1, 2, 6, 4, 5],
[1, 3, 6, 4, 5],
[1, 2, 3, 6, 5],
[1, 7, 5],
[1, 7, 3, 5]
];
function process(arrays, depth) {
return d3.groups(arrays, d => d[depth]).map(x => {
if (
x[1].length > 1 || // if there is more than 1 child
(x[1].length == 1 && x[1][0][depth+1]) // if there is 1 child and the future depth is inferior to the child's length
)
return ({
"label": x[0],
"children": process(x[1], depth+1)
});
return ({ "label": x[0] }); // if there is no child
});
};
console.log(process(input, 0));
<script src="https://d3js.org/d3-array.v2.min.js"></script>
此:
d3.groups
)在索引等于深度的数组元素上。这是d3.groups
在递归步骤(将第三个元素上的数组分组)内产生的中间结果:
var input = [
[1, 2, 3, 4, 5],
[1, 2, 6, 4, 5],
[1, 2, 3, 6, 5]
];
console.log(d3.groups(input, d => d[2]));
<script src="https://d3js.org/d3-array.v2.min.js"></script>
答案 3 :(得分:0)
编辑-固定
这是我的解决方案 Pro:一劳永逸(不需要将对象转换为上述数组) 优点:保持大小/值计数 Pro:输出与带有孩子的D3耀斑完全相同 缺点:比较丑陋,效率可能较低 非常感谢以前的评论对我的帮助。
var data = [[1,2,3,4,5],
[1,2,6,4,5],
[1,3,6,4,5],
[1,2,3,6,5],
[1,7,5],
[1,7,3,5]]
var root = {"name":"flare", "children":[]} // the output
var node // pointer thingy
var row
// loop through array
for(var i=0;i<data.length;i++){
row = data[i];
node = root;
// loop through each field
for(var j=0;j<row.length;j++){
// set undefined to "null"
if (typeof row[j] !== 'undefined' && row[j] !== null) {
attribute = row[j]
}else{
attribute = "null"
}
// using underscore.js, does this field exist
if(_.where(node.children, {name:attribute}) == false ){
if(j < row.length -1){
// this is not the deepest field, so create a child with children
var oobj = {"name":attribute, "children":[] }
node.children.push(oobj)
node = node.children[node.children.length-1]
}else{
// this is the deepest we go, so set a starting size/value of 1
node.children.push({"name":attribute, "size":1 })
}
}else{
// the fields exists, but we need to find where
found = false
pos = 0
for(var k=0;k< node.children.length ;k++){
if(node.children[k]['name'] == attribute){
pos = k
found = true
break
}
}
if(!node.children[pos]['children']){
// if no key called children then we are at the deepest layer, increment
node.children[pos]['size'] = parseInt(node.children[pos]['size']) + 1
}else{
// we are not at the deepest, so move the pointer "node" and allow code to continue
node = node.children[pos]
}
}
}
}
// object here
console.log(root)
// stringified version to page
document.getElementById('output').innerHTML = JSON.stringify(root, null, 1);
工作示例 https://jsfiddle.net/7qaz062u/
输出
{ "name": "flare", "children": [ { "name": 1, "children": [ { "name": 2, "children": [ { "name": 3, "children": [ { "name": 4, "children": [ { "name": 5, "size": 1 } ] } ] }, { "name": 6, "children": [ { "name": 4, "children": [ { "name": 5, "size": 1 } ] } ] } ] }, { "name": 3, "children": [ { "name": 6, "children": [ { "name": 4, "children": [ { "name": 5, "size": 1 } ] } ] }, { "name": 3, "children": [ { "name": 6, "children": [ { "name": 5, "size": 1 } ] } ] } ] }, { "name": 7, "children": [ { "name": 5, "size": 1 }, { "name": 3, "children": [ { "name": 5, "size": 1 } ] } ] } ] } ] }