Skip to main content

树形图和系统树图

在权阈中如何自定义树形图和图表。
Created on December 11|Last edited on January 27
本报告是作者Stacey Svetlichnaya所写的"Trees and Dendrograms"的翻译

圆形系统树图

从本地将圆形系统树图记录到权阈 (简短Colab范例).

输入格式:最小值,每个结点都需要一个编号ID。除了树根以外的大部分结点要有父ID。名称/标签为可选项。

这个例子是直接从Vega说明文档拿来的,这个图表呈现的是一个代码/变量类型的树。本报告结尾有完整Vega配置。

显示配置

点击左上方的齿轮即可调节图表设置,是交互式的。

  • 标签:显示/隐藏结点名称。
  • 半径:同心各层之间的间距。
  • 幅度:图表形成多大角度的圆,0-360度之间。
  • 旋转:圆的方位(以树根为圆心旋转)。
  • 布局:子结点之间的间距。
  • 连接:边缘如何呈现(线形、圆弧、正交)。



Full dendrogram
1


完整代码

data = [[a["id"], a["name"], a["parent"], a["size"]] for a in raw_data]
table = wandb.Table(columns=["node_id", "node_name", "parent", "size"], data=data)
fields = {"node_name" : "node_name", "node_id" : "node_id", "parent" : "parent", "size" : "size"}
dendro = wandb.plot_table(vega_spec_name="stacey/dendrogram", data_table=table, fields=fields)
wandb.log({"custom_tree" : dendro})

其中的raw_data格式为:

raw_data = [
{
    "id": 126,
    "name": "Variable",
    "parent": 67,
    "size": 1124
  },
  {
    "id": 127,
    "name": "Variance",
    "parent": 67,
    "size": 1876
  },
  {
    "id": 128,
    "name": "Xor",
    "parent": 67,
    "size": 1101
  },
  {
    "id": 129,
    "name": "scale",
    "parent": 1
  }, ...
]




树形图(常规系统树图)

还是那些样本数据,在这里呈现为常规树形图来显示概率空间(去Vega查看原图)。以上绘图代码唯一修改的就是vega_spec_name




Flat tree
1


后续步骤

自定义图表功能

  • 对显示设置满意了以后,指示如何保存为固定版本。
  • 在结点悬停时出现的有用信息,尤其是网址。
  • 根据大小/相对权值调节结点颜色/外观。
  • 并非全部结点被命名的例子(更接近传统系统树图)。
  • 泛化至显示更小的有向无环图/概率图模型/带标签结点。
  • 泛化至Python或Vega的其他输入格式(例如从原始数据计算分层聚类/树结构,如同在scipysklearn )。
  • 可以让用户更多地控制聚类分析算法。
  • 更花哨的图表类型如Sankey

要展示的数据集/研究

有用的参考资料



圆形系统树图完整Vega配置

{
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "description": "An example of a radial layout for a node-link diagram of hierarchical data.",
  "width": 720,
  "height": 720,
  "padding": 5,
  "autosize": "none",

  "signals": [
    {
      "name": "labels", "value": true,
      "bind": {"input": "checkbox"}
    },
    {
      "name": "radius", "value": 280,
      "bind": {"input": "range", "min": 20, "max": 600}
    },
    {
      "name": "extent", "value": 360,
      "bind": {"input": "range", "min": 0, "max": 360, "step": 1}
    },
    {
      "name": "rotate", "value": 0,
      "bind": {"input": "range", "min": 0, "max": 360, "step": 1}
    },
    {
      "name": "layout", "value": "tidy",
      "bind": {"input": "radio", "options": ["tidy", "cluster"]}
    },
    {
      "name": "links", "value": "line",
      "bind": {
        "input": "select",
        "options": ["line", "curve", "diagonal", "orthogonal"]
      }
    },
    { "name": "originX", "update": "width / 2" },
    { "name": "originY", "update": "height / 2" }
  ],

  "data": [
    {
      "name": "wandb",
      "transform": [
        {
          "type": "stratify",
          "key": "${field:node_id}",
          "parentKey": "${field:parent}"
        },
        {
          "type": "tree",
          "sort" : {"field": "${field:size}", "order": "descending"},                 
          "method": {"signal": "layout"},
          "size": [1, {"signal": "radius"}],
          "as": ["alpha", "radius", "depth", "children"]
        },
        {
          "type": "formula",
          "expr": "(rotate + extent * datum.alpha + 270) % 360",
          "as":   "angle"
        },
        {
          "type": "formula",
          "expr": "PI * datum.angle / 180",
          "as":   "radians"
        },
        {
          "type": "formula",
          "expr": "inrange(datum.angle, [90, 270])",
          "as":   "leftside"
        },
        {
          "type": "formula",
          "expr": "originX + datum.radius * cos(datum.radians)",
          "as":   "x"
        },
        {
          "type": "formula",
          "expr": "originY + datum.radius * sin(datum.radians)",
          "as":   "y"
        }
      ]
    },
    {
      "name": "links",
      "source": "wandb",
      "transform": [
        { "type": "treelinks" },
        {
          "type": "linkpath",
          "shape": {"signal": "links"}, "orient": "radial",
          "sourceX": "source.radians", "sourceY": "source.radius",
          "targetX": "target.radians", "targetY": "target.radius"
        }
      ]
    }
  ],

  "scales": [
    {
      "name": "color",
      "type": "linear",
      "range": {"scheme": "magma"},
      "domain": {"data": "wandb", "field": "depth"},
      "zero": true
    }
  ],

  "marks": [
    {
      "type": "path",
      "from": {"data": "links"},
      "encode": {
        "update": {
          "x": {"signal": "originX"},
          "y": {"signal": "originY"},
          "path": {"field": "path"},
          "stroke": {"value": "#ccc"}
        }
      }
    },
    {
      "type": "symbol",
      "from": {"data": "wandb"},
      "encode": {
        "enter": {
          "size": {"value": 100},
          "stroke": {"value": "#fff"}
        },
        "update": {
          "x": {"field": "x"},
          "y": {"field": "y"},
          "fill": {"scale": "color", "field": "depth"}
        }
      }
    },
    {
      "type": "text",
      "from": {"data": "wandb"},
      "encode": {
        "enter": {
          "text": {"field": "${field:node_name}"},
          "fontSize": {"value": 9},
          "baseline": {"value": "middle"}
        },
        "update": {
          "x": {"field": "x"},
          "y": {"field": "y"},
          "dx": {"signal": "(datum.leftside ? -1 : 1) * 6"},
          "angle": {"signal": "datum.leftside ? datum.angle - 180 : datum.angle"},
          "align": {"signal": "datum.leftside ? 'right' : 'left'"},
          "opacity": {"signal": "labels ? 1 : 0"}
        }
      }
    }
  ]

Iterate on AI agents and models faster. Try Weights & Biases today.