如何在 VEGA Api 中使用 D3 树数据?

问题描述

我正在尝试使用 Vega API 呈现 D3 树数据。

Violin plot

声明 Vega 使用 d3 树层次结构。这表明它应该是可能的。 Vega 页面甚至包含一个指向 D3 层次结构的链接,如下所示:

{
"name": "Eve","children": [
    {
      "name": "Cain"
    },{
      "name": "Seth","children": [
        {
          "name": "Enos"
        },{
          "name": "Noam"
        }
      ]
    },{
      "name": "Abel"
    },{
      "name": "Awan","children": [
        {
          "name": "Enoch"
        }
      ]
    },{
      "name": "Azura"
    }
  ]
}

但是在 Vega Tree 图中直接使用它是行不通的。

Vega Tree documentation,但数据格式看起来完全不同。有没有办法将 D3 数据层次结构转换为 Vega 支持的格式? Vega 文档讨论了转换,但我找不到示例。

所需的 Vega 格式如下所示。它使用 ID 来知道父节点是谁,而不是嵌套数组。

[
{
    "id": 1,"name": "flare"
},{
    "id": 2,"name": "analytics","parent": 1
},{
    "id": 3,"name": "cluster","parent": 2
},{
    "id": 4,"name": "AgglomerativeCluster","parent": 3,"size": 3938
},{
    "id": 5,"name": "CommunityStructure","size": 3812
}

如何在 Vega 中使用 D3 格式的树数据?

解决方法

我没有在 Vega 上尝试过,但是您可以使用一个小辅助函数来转换层次结构,如下所示:

data = {
"name": "Eve","children": [
    {
      "name": "Cain"
    },{
      "name": "Seth","children": [
        {
          "name": "Enos"
        },{
          "name": "Noam"
        }
      ]
    },{
      "name": "Abel"
    },{
      "name": "Awan","children": [
        {
          "name": "Enoch"
        }
      ]
    },{
      "name": "Azura"
    }
  ]
};

console.log(flatten(data));

function flatten(hierarchy) {
  let ar = [];
  let i = 0;
  
  let hierarchyWithID = d3.hierarchy(hierarchy).each(d => d.id = i++);
  
  ar = hierarchyWithID.descendants().map(d => {
    let obj = {};
    obj.name = d.data.name;
    obj.id = d.id;
    if (d.parent) { obj.parent = d.parent.id;}
    if (d.size) { obj.size = d.size;}
    return obj;
  });
  
  return ar;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

或者,一个没有 d3.js 函数的版本(分配不同的 id):

data = {
"name": "Eve",{
      "name": "Azura"
    }
  ]
};

console.log(flatten2(data));

function flatten2(obj) {
  let id = 0;
  return _flatten2(obj);

  function _flatten2(obj,ar = [],parentID = -1) {
    let node = {};
      node.id = id++;
      node.name = obj.name;
      if (parentID !== -1) { node.parent = parentID;}
      if (obj.size) { node.size = obj.size;}
      ar.push(node);

      if (obj.children) {
        for (let child of obj.children) {
          _flatten2(child,ar,node.id);
        }
      }
    return ar;
  }
}