Skip to main content

How To Customize Trees and Dendrograms in Weights & Biases

In this article, we take a look at how to customize charts of trees and graphs in Weights & Biases.
Created on December 1|Last edited on November 28

Circular Dendrogram

If you want to log a circular dendrogram natively to W&B, follow this super quick Colab example.
Input format: at minimum, each node needs an id. Most nodes except the root should have parent ids. Names/labels are optional.
This example is taken directly from the Vega documentation, and this chart renders a code/variable type tree. See the end of this report for the full Vega spec.

Display Configuration

Click on the gear icon in the top right to adjust the chart settings interactively.
  • labels: show/hide node names
  • radius: spacing between concentric layers
  • extent: 0-360 degrees of the circle in which to render the chart
  • rotate: circle orientation (pivot chart around the root)
  • layout: visual spacing of child nodes
  • links: how the edges are rendered (lines, curves, orthogonal)

Full dendrogram
1


Full Code

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})
where raw_data has the form:
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
}, ...
]




Tree (Regular Dendrogram)

Here the same sample data is rendered as a regular tree to show the space of possibilities (see the original in Vega).
The only change to the plotting code above requires changing the vega_spec_name.

Flat tree
1


Next Steps

Custom Chart Features

  • show how to save a fixed version once someone is happy with the display settings
  • include useful information on node hover, esp URLs
  • adjust node color/appearance based on size/relative weight
  • examples where not all nodes are named (closer to a traditional dendrogram)
  • generalize to displaying smaller DAGs/PGMs/labeled nodes
  • generalize to other input formats in Python or Vega (e.g. compute hierarchical clustering/tree structure from raw data, as in scipy or sklearn
  • allow the user more control over the clustering algorithm

Datasets/Research to Showcase

Useful References



Circular Dendrogram Full Vega Spec

{
"$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.