遍历对象数组以生成 d3 桑基图数据

Posted

技术标签:

【中文标题】遍历对象数组以生成 d3 桑基图数据【英文标题】:Traverse Array of objects to generate d3 Sankey Chart data 【发布时间】:2019-06-30 21:43:41 【问题描述】:

必须将此输入(tree-like 结构)格式化为特定格式才能绘制d3 sankey diagram chart

let unformattedJson = [
  
    "key": "a1",
    "value": 30,
    "buckets": [
      
        "key": "a2",
        "value": 10 
      ,
      
        "key": "b2",
        "value": 20 
      
    ]
  ,
  
    "key": "b1",
    "value": 70,
    "buckets": [
      
        "key": "b2",
        "value": 40 
      ,
      
        "key": "c2",
        "value": 30 
      
    ]
  
]

我需要生成的预期输出是:


  "nodes": [
    "nodeId":0,"name":"a1",
    "nodeId":1,"name":"a2",
    "nodeId":2,"name":"b2",
    "nodeId":3,"name":"b1",
    "nodeId":4,"name":"c2"
  ],
  "links": [
    "source":0,"target":1,"value":10,
    "source":0,"target":2,"value":20,
    "source":3,"target":2,"value":40,
    "source":3,"target":4,"value":30
  ]

我的解决方法。我做了两个函数来计算节点和链接。对于节点,我创建了一个递归函数来获取所有唯一键,并为每个键分配了一个id。我还做了另一个函数来获取键之间的所有关系。

let makeNodeObj = function(orObj, index)
    let obj = ;
    obj.nodeId = index;
    obj.name = orObj;
    return obj;


var getUniqueKeys = (old, arr)=>
  let toRet = old;
  arr.forEach((data,index)=>
    if(toRet.indexOf(data.key)<0) //remove duplicates
      toRet.push(data.key);
    
    if(data.buckets !== undefined && data.buckets.length>0)
        getUniqueKeys(toRet, data.buckets);
    
  );
  return toRet;


let uniqueKeys = getUniqueKeys([],unformattedJson);

let nodes = uniqueKeys.map((data,index)=>
  return makeNodeObj(data,index);
);

let getNodeId = function(nodes, key)
  let node = nodes.find((data)=>
    return data.name == key
  );
  return node.nodeId;


let links = [];
unformattedJson.map((data)=>
  let sourceId = getNodeId(nodes, data.key);
  if(data.buckets.length>0)
    data.buckets.map((data2)=>
      let targetId = getNodeId(nodes,data2.key);
      let linkObj = ;
      linkObj.source = sourceId;
      linkObj.target = targetId;
      linkObj.value = data2.value;
      links.push(linkObj);
    )
  
);

console.log(
  nodes, links
);

我的解决方案只有在只有一个级别深的存储桶时才有效。如何为孩子内的多个嵌套桶实现这一点?

【问题讨论】:

【参考方案1】:

我采用递归方法来生成预期的输出。 key 和他生成的id 之间的关联与Map 保持一致。该方法使用Deep First Search算法遍历树的思想。

let unformattedJson = [
  
    "key": "a1",
    "value": 30,
    "buckets": [
      
        "key": "a2",
        "value": 10,
        "buckets": [
          "key": "a3", "value": 99
        ]
      ,
      "key": "b2", "value": 20
    ]
  ,
  
    "key": "b1",
    "value": 70,
    "buckets": [
      "key": "b2", "value": 40,
      "key": "c2", "value": 30
    ]
  
];

const getData = (input, visited=new Map(), parent, nodes=[], links=[]) =>

    input.forEach(x =>
    
        // Add node into the node list, if not visited previosuly.

        if (!visited.has(x.key))
        
            let currId = nodes.length;
            nodes.push(nodeId: currId, name: x.key);
            visited.set(x.key, currId);
        

        // If a parent node exists, add relation into the links list.

        if (parent)
        
            // Note, we use the "Map" to get the ids.

            links.push(
                source: visited.get(parent.key),
                target: visited.get(x.key),
                value: x.value
            );
        

        // Traverse (if required) to the next level of deep.

        if (x.buckets)
            getData(x.buckets, visited, x, nodes, links)
    );

    return nodes: nodes, links: links;


console.log(getData(unformattedJson));

【讨论】:

以上是关于遍历对象数组以生成 d3 桑基图数据的主要内容,如果未能解决你的问题,请参考以下文章

可视化应用实战案例:绘制交互式+pdf+png等多格式桑基图

Python 绘制惊艳的桑基图

origin2017的桑基图在哪

R-无序的定类数据分析:列联表、热力图、和弦图、桑基图和统计检验

如何遍历 ReactTable 中的对象数组以显示数据?

循环遍历多维数组以生成新数据并在节点 js / javascript 中追加新的键值