D3.js 层次结构

层次结构(Hierarchies)

分析或可视化数据时的一种常用技术是将数据组织成。(分组)

可以将层次结构视为树状结构,其中根项目(或“节点”)拆分为顶级组。每个顶级组拆分为二级组,依此类推。最顶层的项目(或节点)称为根节点。最底部的项目称为叶子叶子节点

有几种方法可以可视化分层数据,包括树图压缩圆图旭日图

  • 从数据数组创建层次结构:使用 D3 的.rollup函数按任何分类属性对数据进行分组。第一个参数是要分组的数组;第二个参数是一个reduce函数,这是一个接受值数组并输出单个值的函数;其余参数是指定要分组的属性的函数。可以使用.get检查返回的map对象
function sumWorldwideGross(group) {
  return d3.sum(group, function(d) {
    return d.Worldwide_Gross;
  });
}

let groups = d3.rollup(data,
                       sumWorldwideGross,
                       function(d) { return d.Distributor; },
                       function(d) { return d.Genre; }
                      );

// Get Sony Pictures
groups.get('Sony Pictures');  // {"Comedy" => 22498520, "Action" => 315268353, "Drama" => 93905424}

// Get Drama within Sony Pictures
groups.get('Sony Pictures').get('Drama');  // 93905424
  • d3.层次结构:通过调用d3.hierarchy并传入生成的map对象来创建的d3.rollup
function sumWorldwideGross(group) {
  return d3.sum(group, function(d) {
    return d.Worldwide_Gross;
  });
}

let groups = d3.rollup(data,
                       sumWorldwideGross,
                       function(d) { return d.Distributor; },
                       function(d) { return d.Genre; }
                      );

let root = d3.hierarchy(groups);

d3.hierarchy输出的是一个嵌套对象,类似于:

{
  data: [undefined, Map(3)],
  children: [
    {
      data: ["Sony Pictures", Map(3)],
      children: [...],
      depth: 1,
      height: 1,
      parent: {...} // this item's parent node
    }.
    {
      data: ["Walt Disney Pictures", Map(2)],
      children: [...],
      depth: 1,
      height: 1,
      parent: {...} // this item's parent node
    }.
    {
      data: ["Warner Bros.", Map(3)],
      children: [...],
      depth: 1,
      height: 1,
      parent: {...} // this item's parent node
    }
  ],
  depth: 0,
  height: 2,
  parent: null
}

层次结构中的每个项目(或节点)都有属性:data, children, depth, height and parent.

data是传入的关联项目中的map或对象。通常,不需要访问该值,因为层次结构通过其childrenvalue属性使该数据可用。

children是一个包含节点子节点的数组。

depthheight指示层次结构中节点的深度和高度。

parent引用节点的父节点。

  • 可视化层次结构

    • 树形布局:首先使用创建树布局函数d3.tree();可以命令配置树的大小.size;然后,可以调用treeLayout,传入定义的层次结构对象root
    var treeLayout = d3.tree();
    
    treeLayout.size([400, 200]);
    
    treeLayout(root);
    

    这将在root的每个节点上写入xy值。

    绘制节点:

    1. 用于root.descendants()获取所有节点的数组
    2. 将此数组加入圆(或任何其他类型的 SVG 元素)
    3. 使用xy定位圆圈

    要绘制链接:

    1. 用于root.links()获取所有链接的数组
    2. 将数组连接到行(或路径)元素
    3. 使用xy链接的sourcetarget属性来定位线
    // Nodes
    d3.select('svg g.nodes')
      .selectAll('circle.node')
      .data(root.descendants())
      .join('circle')
      .classed('node', true)
      .attr('cx', function(d) {return d.x;})
      .attr('cy', function(d) {return d.y;})
      .attr('r', 4);
    
    // Links
    d3.select('svg g.links')
      .selectAll('line.link')
      .data(root.links())
      .join('line')
      .classed('link', true)
      .attr('x1', function(d) {return d.source.x;})
      .attr('y1', function(d) {return d.source.y;})
      .attr('x2', function(d) {return d.target.x;})
      .attr('y2', function(d) {return d.target.y;});
    
    • 集群布局:与tree布局非常相似,主要区别在于所有叶节点都放置在相同的深度
    var clusterLayout = d3.cluster()
      .size([400, 200]);
    
    var root = d3.hierarchy(data);
    
    clusterLayout(root);
    
    • 树状图布局:用于直观地表示每个项目都有关联值的层次结构。

      通过调用创建树图布局函数d3.treemap() ;可以配置布局;在将此布局应用于层次结构之前,必须在层次结构上运行.sum()。调用treemapLayout,传入root之前定义的层次结构对象.

    var treemapLayout = d3.treemap();
    
    treemapLayout
      .size([400, 200])
      .paddingOuter(10);
    
    root.sum(function(d) {
      return d.value;
    });
    
    treemapLayout(root);
    

    树形图布局函数向每个节点添加 4 个属性x0x1y0y1。它们指定树形图中每个矩形的尺寸。

    d3.select('svg g')
      .selectAll('rect')
      .data(root.descendants())
      .join('rect')
      .attr('x', function(d) { return d.x0; })
      .attr('y', function(d) { return d.y0; })
      .attr('width', function(d) { return d.x1 - d.x0; })
      .attr('height', function(d) { return d.y1 - d.y0; })
    

    如果想在每个矩形中添加标签,可以将g元素加入数组并添加recttext元素到每个g

    var nodes = d3.select('svg g')
      .selectAll('g')
      .data(rootNode.descendants())
      .join('g')
      .attr('transform', function(d) {return 'translate(' + [d.x0, d.y0] + ')'})
    
    nodes
      .append('rect')
      .attr('width', function(d) { return d.x1 - d.x0; })
      .attr('height', function(d) { return d.y1 - d.y0; })
    
    nodes
      .append('text')
      .attr('dx', 4)
      .attr('dy', 14)
      .text(function(d) {
        return d.data.name;
      })
    

    treemap可以通过多种方式配置布局:

    1. 可以使用设置节点子节点周围的填充.paddingOuter
    2. 兄弟节点之间的填充可以使用.paddingInner
    3. 可以使用同时设置外部和内部填充.padding
    4. 外部填充也可以使用,``.paddingTop.paddingBottom.paddingLeft.paddingRight`进行微调

    树形图有不止一种排列矩形的策略。D3 有一些内置的:treemapBinary, treemapDice,treemapSlicetreemapSliceDicetreemapSquarify

    • 包布局:类似于树布局,但用圆圈表示节点。
    var packLayout = d3.pack();
    
    packLayout.size([300, 300]);
    
    rootNode.sum(function(d) {
      return d.value;
    });
    
    packLayout(rootNode);
    
    d3.select('svg g')
      .selectAll('circle')
      .data(rootNode.descendants())
      .join('circle')
      .attr('cx', function(d) { return d.x; })
      .attr('cy', function(d) { return d.y; })
      .attr('r', function(d) { return d.r; })
    

    可以通过g为每个后代创建元素来添加标签:

    var nodes = d3.select('svg g')
      .selectAll('g')
      .data(rootNode.descendants())
      .join('g')
      .attr('transform', function(d) {return 'translate(' + [d.x, d.y] + ')'})
    
    nodes
      .append('circle')
      .attr('r', function(d) { return d.r; })
    
    nodes
      .append('text')
      .attr('dy', 4)
      .text(function(d) {
        return d.children === undefined ? d.data.name : '';
      })
    
    

    可以使用以下方式配置每个圆圈周围的填充

    packLayout.padding(10)
    
    • 分区布局:将一个矩形空间细分为层,每个层代表层次结构中的一个层。对于层中的每个节点,每一层进一步细分
    var partitionLayout = d3.partition();
    
    partitionLayout.size([400, 200]);
    
    rootNode.sum(function(d) {
      return d.value;
    });
    
    partitionLayout(rootNode);
    
    d3.select('svg g')
      .selectAll('rect')
      .data(rootNode.descendants())
      .join('rect')
      .attr('x', function(d) { return d.x0; })
      .attr('y', function(d) { return d.y0; })
      .attr('width', function(d) { return d.x1 - d.x0; })
      .attr('height', function(d) { return d.y1 - d.y0; });
    
    partitionLayout.padding(2);
    

    如果您想更改分区布局的方向,以便图层从左到右运行,您可以在定义元素时交换x0x1和交换:y0y1

     .attr('x', function(d) { return d.y0; })
      .attr('y', function(d) { return d.x0; })
      .attr('width', function(d) { return d.y1 - d.y0; })
      .attr('height', function(d) { return d.x1 - d.x0; });
    

    还可以将x尺寸映射到旋转角度和y半径以创建旭日图:

    var data = {
      "name": "A1",
      "children": [
          {
              "name": "B1",
              "children": [
                  {
                      "name": "C1",
                      "value": 100
                  },
                  {
                      "name": "C2",
                      "value": 300
                  },
                  {
                      "name": "C3",
                      "value": 200
                  }
              ]
          },
          {
              "name": "B2",
              "value": 200
          }
      ]
    };
    
    var radius = 150;
    
    var partitionLayout = d3.partition()
      .size([2 * Math.PI, radius]);
    
    var arcGenerator = d3.arc()
      .startAngle(function(d) { return d.x0; })
      .endAngle(function(d) { return d.x1; })
      .innerRadius(function(d) { return d.y0; })
      .outerRadius(function(d) { return d.y1; });
    
    var rootNode = d3.hierarchy(data)
    
    rootNode.sum(function(d) {
      return d.value;
    });
    
    partitionLayout(rootNode);
    
    d3.select('svg g')
      .selectAll('path')
      .data(rootNode.descendants())
      .join('path')
      .attr('d', arcGenerator);
    
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。

推荐阅读更多精彩内容