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);
    

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容