Trees
A compas.datastructures.Tree
is a data structure that can be used to
represent hierarchical relationships between data. It starts with a single root
node, and each node can have a number of children nodes. Each node can
store arbitrary attributes.
Note
Please refer to the API for a complete overview of all functionality:
Tree Construction
Trees can be constructed in a number of ways: * by adding nodes to a tree instance, specifiying parent node, * by adding nodes directly to another node instance that belongs to a tree, or * directly loaded from a serilised json file.
Adding To Tree
>>> from compas.datastructures import Tree
>>> from compas.datastructures import TreeNode
>>> tree = Tree()
>>> root_node = TreeNode('root')
>>> tree.add(root_node)
>>> branch_node = TreeNode('branch')
>>> tree.add(branch_node, parent=root_node)
>>> leaf_node1 = TreeNode('leaf1')
>>> tree.add(leaf_node1, parent=branch_node)
>>> leaf_node2 = TreeNode('leaf2')
>>> tree.add(leaf_node2, parent=branch_node)
>>> tree.print_hierarchy()
└── <TreeNode root>
└── <TreeNode branch>
├── <TreeNode leaf1>
└── <TreeNode leaf2>
Adding To Node
>>> from compas.datastructures import Tree
>>> from compas.datastructures import TreeNode
>>> tree = Tree()
>>> root_node = TreeNode('root')
>>> tree.add(root_node)
>>> branch_node = TreeNode('branch')
>>> root_node.add(branch_node)
>>> leaf_node1 = TreeNode('leaf1')
>>> branch_node.add(leaf_node1)
>>> leaf_node2 = TreeNode('leaf2')
>>> branch_node.add(leaf_node2)
└── <TreeNode root>
└── <TreeNode branch>
├── <TreeNode leaf1>
└── <TreeNode leaf2>
Loading From File
>>> from compas.data import json_load
>>> tree = json_load('tree.json')
>>> tree.print_hierarchy()
└── <TreeNode root>
└── <TreeNode branch>
├── <TreeNode leaf1>
└── <TreeNode leaf2>
Remove Node
>>> tree.remove(leaf_node2) # Or branch_node.remove(leaf_node2)
>>> tree.print_hierarchy()
└── <TreeNode root>
└── <TreeNode branch>
└── <TreeNode leaf1>
Tree Traversal
The tree can be traversed in a number of ways, including depth-first and breadth-first. For depth-first traversal, there are additional options for pre-order and post-order.
Depth-First
The algorithm starts at the root node and explores as far as possible along each branch before backtracking. The traversal can be done in pre-orderor post-order. In pre-order, the parent node is visited before its children. In post-order, the parent node is visited after its children.
>>> from compas.datastructures import Tree
>>> from compas.datastructures import TreeNode
>>> tree = Tree()
>>> root_node = TreeNode('root')
>>> tree.add(root_node)
>>> branch_node1 = TreeNode('branch1')
>>> root_node.add(branch_node1)
>>> leaf_node1 = TreeNode('leaf1')
>>> branch_node1.add(leaf_node1)
>>> leaf_node2 = TreeNode('leaf2')
>>> branch_node1.add(leaf_node2)
>>> branch_node2 = TreeNode('branch2')
>>> root_node.add(branch_node2)
>>> leaf_node3 = TreeNode('leaf3')
>>> branch_node2.add(leaf_node3)
>>> leaf_node4 = TreeNode('leaf4')
>>> branch_node2.add(leaf_node4)
>>> tree.print_hierarchy()
└── <TreeNode root>
├── <TreeNode branch1>
│ ├── <TreeNode leaf1>
│ └── <TreeNode leaf2>
└── <TreeNode branch2>
├── <TreeNode leaf3>
└── <TreeNode leaf4>
>>> for node in tree.traverse(strategy='depthfirst', order='preorder'):
... print(node)
<TreeNode root>
<TreeNode branch1>
<TreeNode leaf1>
<TreeNode leaf2>
<TreeNode branch2>
<TreeNode leaf3>
<TreeNode leaf4>
>>> for node in tree.traverse(strategy='depthfirst', order='postorder'):
... print(node)
<TreeNode leaf1>
<TreeNode leaf2>
<TreeNode branch1>
<TreeNode leaf3>
<TreeNode leaf4>
<TreeNode branch2>
<TreeNode root>
Breadth-First
The algorithm starts at the root node and explores the neighbour nodes first, before moving to the next level neighbours.
>>> for node in tree.traverse(strategy='breadthfirst'):
... print(node)
<TreeNode root>
<TreeNode branch1>
<TreeNode branch2>
<TreeNode leaf1>
<TreeNode leaf2>
<TreeNode leaf3>
<TreeNode leaf4>
Node Attributes
>>> nodes = tree.get_nodes_by_name('branch1')
>>> nodes
[<TreeNode branch1>]
>>> node = tree.get_node_by_name('branch1')
>>> node
<TreeNode branch1>
>>> node.is_root
False
>>> node.is_leaf
False
>>> node.is_branch
True
>>> node.parent
<TreeNode root>
>>> node.children
[<TreeNode leaf1>, <TreeNode leaf2>]
>>> leaf1 = node.children[0]
>>> list(leaf1.ancestors)
[<TreeNode branch1>, <TreeNode root>]
>>> root = leaf1.ancestors[-1]
>>> list(root.descendants)
[<TreeNode branch1>, <TreeNode leaf1>, <TreeNode leaf2>, <TreeNode branch2>, <TreeNode leaf3>, <TreeNode leaf4>]