Introduction to the usage of Python treelib library to create a multi-fork tree

Introduction to the usage of Python treelib library to create a multi-fork tree

The treelib library is a third-party library for Python. This library implements some common methods related to polytrees.

One, install treelib

pip install treelib

In the treelib library, two classes Tree and Node are implemented, which are used to create polytrees and create nodes, respectively.

Second, create a multi-tree and add nodes

1. Create a polytree

# coding=utf-8
from treelib import Tree, Node


tree = Tree()
tree.show()
print(tree.identifier)

operation result:

Tree is empty

2f9fa5c8-e7aa-11ea-9b8b-b886873e4844

The Tree class is used to instantiate a multi-branch tree. There are four initialization parameters (tree=None, deep=False, node_class=None, identifier=None), all of which have default values. tree means to copy an existing tree and pass in a Tree object. deep indicates whether to deep copy when copying another tree. node_class represents the node class, generally does not need to be used, it can be ignored. Identifier represents the id of the tree. A unique id value is assigned by default during initialization. You can also manually specify an id to ensure that it is unique. Once the tree is created, the id cannot be modified.

2. Add nodes to the polytree

tree.create_node(tag='Node-5', identifier='node-5', data=5)
tree.create_node(tag='Node-10', identifier='node-10', parent='node-5', data=10)
tree.create_node('Node-15', 'node-15', 'node-10', 15)
tree.show()

node = Node(data=50)
tree.add_node(node, parent='node-5')
node_a = Node(tag='Node-A', identifier='node-A', data='A')
tree.add_node(node_a, parent='node-5')
tree.show()

operation result:

Node-5
└── Node-10
    └── Node-15

Node-5
├── Node-10
│   └── Node-15
├── Node-A
└── ddeb9b02-e7ab-11ea-b2ee-b886873e4844

create_node(tag=None, identifier=None, parent=None, data=None): Create a node and add it directly to the tree. tag represents the label of the node. The label of the node is displayed when the structure of the tree is printed on the console. The value can be specified. If the value is not specified, it will be equal to id by default. Identifier represents the id of the node, a unique id is assigned by default, or a unique id can be specified. It should be noted here that the id is unique and cannot be repeated. The label can be repeated but it is best not to repeat it. parent represents the parent node of the node. The root node may not be specified. However, a tree can only have one root node. If there is already a root node in the tree, an error will be reported if the parent is empty. data represents the data stored in the node, which can be of various data types.

add_node(node, parent=None): Add a node to the tree. This method needs to use the Node class to create the node first, the first parameter is passed into the node, and the second parameter is the same as the create_node() method.

Three, Node creates nodes and methods in the Node class

1. Create Node

The Node class is used to create nodes. There are four initialization parameters (tag=None, identifier=None, expanded=True, data=None), all with default values. The tag, identifier and data are the same as the previous create_node() method. Expanded means the scalability of the node, which will be used in the Tree, you can leave it alone, just keep the default.

The Node class to create a node is generally used in conjunction with add_node() in the Tree class.

2. The attributes and methods of the node

print(node)
print('node id: ', node.identifier)
print('node tag:', node.tag)
print('node data:', node.data)

print('node is leaf: ', node.is_leaf())
print('node is root: ', node.is_root())

operation result:

Node(tag=719f3842-e7af-11ea-8caa-b886873e4844, identifier=719f3842-e7af-11ea-8caa-b886873e4844, data=50)
node id:  719f3842-e7af-11ea-8caa-b886873e4844
node tag: 719f3842-e7af-11ea-8caa-b886873e4844
node data: 50
node is leaf:  True
node is root:  False

If you print the node directly, the node will be printed out as a class object. The three attributes of the node's tag, identifier, and data can be called separately. It can be seen that when no value is specified, the tag is equal to the identifier, which is a unique value assigned by the system.

is_leaf(tree_id=None): Returns whether the position of the node in the tree is a leaf node.

is_root(tree_id=None): Returns whether the position of the node in the tree is the root node.

There are some other methods in the Node class, which are mainly used to process the pointer of the node. Generally, they are not called directly, so I won't introduce them here.

Fourth, the method introduction in Tree

1. Return the number of nodes in the polytree

tree.show()
print('tree len: ', len(tree))
print('tree size:', tree.size())
tree.create_node(tag='Node-20', identifier='node-20', parent='node-10', data=20)
tree.create_node(tag='Node-30', identifier='node-30', parent='node-15', data=30)
print('tree size:', tree.size())

operation result:

Node-5
├── 6e75a77a-e92d-11ea-abfe-b886873e4844
├── Node-10
│   └── Node-15
└── Node-A

tree len:  5
tree size: 5
tree size: 7

show(): Display the output of the multi-fork tree in a tree structure. You can pass in relevant parameters to limit the scope of the display.

size(level=None): Returns the number of nodes in the multi-tree. By default, all nodes in the multi-tree are returned, which is the same as the len() method. If the number of levels is specified, only the number of nodes in that level will be returned.

2. Return the nodes in the polytree

tree.show()
print(tree.all_nodes())
for node in tree.all_nodes_itr():
    print(node)
for id in tree.expand_tree():
    print(id)

operation result:

Node-5
├── Node-10
│   ├── Node-15
│   │   └── Node-30
│   └── Node-20
├── Node-A
└── fcb7619a-e931-11ea-8946-b886873e4844

[Node(tag=Node-5, identifier=node-5, data=5), Node(tag=Node-10, identifier=node-10, data=10), Node(tag=Node-15, identifier=node-15, data=15), Node(tag=fcb7619a-e931-11ea-8946-b886873e4844, identifier=fcb7619a-e931-11ea-8946-b886873e4844, data=50), Node(tag=Node-A, identifier=node-A, data=A), Node(tag=Node-20, identifier=node-20, data=20), Node(tag=Node-30, identifier=node-30, data=30)]
Node(tag=Node-5, identifier=node-5, data=5)
Node(tag=Node-10, identifier=node-10, data=10)
Node(tag=Node-15, identifier=node-15, data=15)
Node(tag=fcb7619a-e931-11ea-8946-b886873e4844, identifier=fcb7619a-e931-11ea-8946-b886873e4844, data=50)
Node(tag=Node-A, identifier=node-A, data=A)
Node(tag=Node-20, identifier=node-20, data=20)
Node(tag=Node-30, identifier=node-30, data=30)
node-5
node-10
node-15
node-30
node-20
node-A
fcb7619a-e931-11ea-8946-b886873e4844

all_nodes(): Returns all nodes in the multi-fork tree, the return result is a list of node objects, and the order of the nodes is the order in which they are added to the tree.

all_nodes_itr(): Returns all nodes in the polytree, the return result is an iterator, and the order of the nodes is the order in which they are added to the tree.

expand_tree(): Returns the id of all nodes in the multi-branch tree, the return result is a generator, and the order is in the order of depth-first traversal. You can pass in parameters such as filter conditions to change the returned generator.

3. Node relationship in polytree

print('node-5 children:', tree.children('node-5'))
print('node-10 branch:', tree.is_branch('node-10'))
print('node-20 siblings:', tree.siblings('node-20'))
print('node-30 parent:', tree.parent('node-30'))
print('node-15 ancestor: ', tree.ancestor('node-15'))
print(tree.is_ancestor('node-15', 'node-30'))
for node in tree.rsearch('node-30'):
    print(node)

operation result:

node-5 children: [Node(tag=Node-10, identifier=node-10, data=10), Node(tag=2fbce8a8-e933-11ea-9304-b886873e4844, identifier=2fbce8a8-e933-11ea-9304-b886873e4844, data=50), Node(tag=Node-A, identifier=node-A, data=A)]
node-10 branch: ['node-15', 'node-20']
node-20 siblings: [Node(tag=Node-15, identifier=node-15, data=15)]
node-30 parent: Node(tag=Node-15, identifier=node-15, data=15)
node-15 ancestor:  node-10
True
node-30
node-15
node-10
node-5

children(nid): Pass in the node id, return all the child nodes of the node, the return result is a node list, if there is no child node, return an empty list.

is_branch(nid): Pass in node id, return all child node id of node, return result is a list, if there is no child node, return empty list.

siblings(nid): Pass in the node id, return all sibling nodes of the node, the return result is a node list, if there is no sibling node, return an empty list.

parent(nid): Pass in the node id and return the parent node of the node. If the root node is passed in, it returns None.

ancestor(nid, level=None): Pass in the node id, return an ancestor node of the node, if the level is specified, return the ancestor node of the level layer, if the level is not specified, return the parent node, if the specified level is greater than the number of levels of the node , An error is reported.

is_ancestor(ancestor, grandchild): Pass in two node ids, determine whether ancestor is an ancestor of grandchild, and return a boolean value.

rsearch(nid, filter=None): Pass in the node id, traverse all the node id on the path from the node to the root node, including the node and the root node, and the return result is a generator. You can pass in filter conditions to filter the results.

4. The depth and leaf nodes of a polytree

print('tree depth:', tree.depth())
print('node-20 depth:', tree.depth(node='node-20'))
print('node-20 level:', tree.level('node-20'))
print('tree leaves:', tree.leaves())
print(tree.paths_to_leaves())

operation result:

tree depth: 3
node-20 depth: 2
node-20 level: 2
tree leaves: [Node(tag=7e421cb4-e935-11ea-8e6d-b886873e4844, identifier=7e421cb4-e935-11ea-8e6d-b886873e4844, data=50), Node(tag=Node-A, identifier=node-A, data=A), Node(tag=Node-20, identifier=node-20, data=20), Node(tag=Node-30, identifier=node-30, data=30)]
[['node-5', '7e421cb4-e935-11ea-8e6d-b886873e4844'], ['node-5', 'node-A'], ['node-5', 'node-10', 'node-20'], ['node-5', 'node-10', 'node-15', 'node-30']]

depth(node=None): Returns the height of the node, the height of the root node is 0, increasing sequentially. If no node is specified, the height of the tree is returned.

level(nid, filter=None): Returns the height of the node. The results of level() and depth() are the same, but the way of passing parameters is different.

leaves(nid=None): Returns all the leaf nodes of the multi-tree, and the result is a list of nodes. When the node id is not specified, all leaf nodes of the entire tree are returned by default. When the node id is specified, all leaf nodes of the subtree with the specified node as the root node are returned.

paths_to_leaves(): Returns the id of all nodes on the path from the root node to each leaf node. The result of each leaf node is a list, and the results of all leaf nodes form a list. So the end result is a nested list of lists.

5. Determine whether the node is in the polytree and get the node

print('node-10 is in tree:', tree.contains('node-10'))
print('node-100 is in tree:', tree.contains('node-100'))
print(tree.get_node('node-10'))
print(tree.get_node('node-100'))
tree.update_node('node-20', data=200)
print('data of node-20:', tree.get_node('node-20').data)

operation result:

node-10 is in tree: True
node-100 is in tree: False
Node(tag=Node-10, identifier=node-10, data=10)
None
data of node-20: 200

contains(nid): Pass in the node id, determine whether the node is in the tree, and return a boolean value.

get_node(nid): Pass in the node id, get the node from the tree, return the node object, and return None if the passed node id is not in the tree.

update_node(nid, **attrs): Pass in the node id, modify the attribute value of the node, which parameter needs to be modified is passed in as a keyword parameter, and 0 or more attributes can be passed in.

6. Adjust the node relationship in the polytree

tree.show()
tree.link_past_node('node-10')
tree.show()
tree.move_node('node-30', 'node-20')
tree.show()

operation result:

Node-5
├── 488e66b6-e939-11ea-aa02-b886873e4844
├── Node-10
│   ├── Node-15
│   │   └── Node-30
│   └── Node-20
└── Node-A

Node-5
├── 488e66b6-e939-11ea-aa02-b886873e4844
├── Node-15
│   └── Node-30
├── Node-20
└── Node-A

Node-5
├── 488e66b6-e939-11ea-aa02-b886873e4844
├── Node-15
├── Node-20
│   └── Node-30
└── Node-A

link_past_node(nid): Pass in the node id, link all child nodes of the node to its parent node, which is equivalent to deleting the node from the tree. If the node id is not in the tree, an error is reported.

move_node(source, destination): Pass in two node ids, move the source node into a child node of the destination node. If the node id is not in the tree, an error is reported.

7. Multitree merge and subtree copy

tree2 = Tree()
tree2.create_node(tag='Node-7', identifier='node-7', data=7)
tree2.create_node(tag='Node-17', identifier='node-17', parent='node-7', data=17)
tree2.show()
tree.paste('node-20', tree2)
tree.show()

tree3 = Tree()
tree3.create_node(tag='Node-8', identifier='node-8', data=8)
tree3.create_node(tag='Node-18', identifier='node-18', parent='node-8', data=18)
tree3.show()
tree.merge('node-A', tree3)
tree.show()

print(tree.subtree('node-20'))

operation result:

Node-7
└── Node-17

Node-5
├── 4f603236-e93a-11ea-8577-b886873e4844
├── Node-15
├── Node-20
│   ├── Node-30
│   └── Node-7
│       └── Node-17
└── Node-A

Node-8
└── Node-18

Node-5
├── 4f603236-e93a-11ea-8577-b886873e4844
├── Node-15
├── Node-20
│   ├── Node-30
│   └── Node-7
│       └── Node-17
└── Node-A
    └── Node-18

Node-20
├── Node-30
└── Node-7
    └── Node-17

paste(nid, new_tree, deep=False): Pass in the node id and a new tree, paste the entire new tree into a subtree of the specified node, and the root node of the new tree becomes a child node of the specified node. If the node is not in the tree, an error is reported.

merge(nid, new_tree, deep=False): Pass in the node id and a new tree, merge the new tree with the specified node, after the merge, the root node of the new tree is not retained, and the subtrees of the root node in the new tree all become the specified The subtree of the node. If the node is not in the tree, an error is reported.

subtree(nid, identifier=None): Pass in the node id, and copy the subtree with this node as the root node. If the node is not in the tree, an error is reported.

8. Convert the polytree to a dictionary and save it to a file

print(tree.to_dict())
print(tree.to_json())
tree.to_graphviz()
tree.save2file('demo_tree.tree')

operation result:

{'Node-5': {'children': ['Node-15', {'Node-20': {'children': ['Node-30', {'Node-7': {'children': ['Node-17']}}]}}, {'Node-A': {'children': ['Node-18']}}, 'e1f2ba34-e93b-11ea-a5f9-b886873e4844']}}
{"Node-5": {"children": ["Node-15", {"Node-20": {"children": ["Node-30", {"Node-7": {"children": ["Node-17"]}}]}}, {"Node-A": {"children": ["Node-18"]}}, "e1f2ba34-e93b-11ea-a5f9-b886873e4844"]}}
digraph tree {
	"node-5" [label="Node-5", shape=circle]
	"node-15" [label="Node-15", shape=circle]
	"node-20" [label="Node-20", shape=circle]
	"node-A" [label="Node-A", shape=circle]
	"e1f2ba34-e93b-11ea-a5f9-b886873e4844" [label="e1f2ba34-e93b-11ea-a5f9-b886873e4844", shape=circle]
	"node-30" [label="Node-30", shape=circle]
	"node-7" [label="Node-7", shape=circle]
	"node-18" [label="Node-18", shape=circle]
	"node-17" [label="Node-17", shape=circle]
	"node-5" -> "e1f2ba34-e93b-11ea-a5f9-b886873e4844"
	"node-5" -> "node-A"
	"node-5" -> "node-15"
	"node-5" -> "node-20"
	"node-20" -> "node-30"
	"node-20" -> "node-7"
	"node-A" -> "node-18"
	"node-7" -> "node-17"
}

to_dict(): Convert the tree into a dictionary structure, the sibling nodes are expressed as a parallel relationship by a list, and the parent and child nodes are expressed as a key-value relationship by a dictionary. Finally, the conversion result is returned.

to_json(): Convert the tree to json, the data structure is the same as to_dict(), but the format is different. Finally, the conversion result is returned.

to_graphviz(): Convert the tree into the structure of a visual graph, no return value.

save2file(filename): Save the tree to a specified file. After running, it will generate a filename file under the current path, and write the structure diagram of the tree in the file. When the file with the same name exists, it will be added and written without overwriting.

These four methods have many keyword parameters, which can be specified to change the result of the conversion.

9. Multitree delete subtree

tree.show()
print('remover node: ', tree.remove_node('node-7'))
tree.show()
print(tree.remove_subtree('node-20'))
tree.show()

operation result:

Node-5
├── 9e0bce40-e93d-11ea-99e7-b886873e4844
├── Node-15
├── Node-20
│   ├── Node-30
│   └── Node-7
│       └── Node-17
└── Node-A
    └── Node-18

remover node:  2
Node-5
├── 9e0bce40-e93d-11ea-99e7-b886873e4844
├── Node-15
├── Node-20
│   └── Node-30
└── Node-A
    └── Node-18

Node-20
└── Node-30

Node-5
├── 9e0bce40-e93d-11ea-99e7-b886873e4844
├── Node-15
└── Node-A
    └── Node-18

remove_node(identifier): Pass in the node id, delete the node as the subtree of the root node, and return the number of deleted nodes.

remove_subtree(nid, identifier=None): Pass in the node id, delete the node as the subtree of the root node, and return the deleted subtree.

 

 

Guess you like

Origin blog.csdn.net/weixin_43790276/article/details/108248298