Tutorial

Creating the root of the tree

A tree is represented by a Node. First, let’s create the root:

from littletree import Node

tree = Node({"population": 7.909}, identifier="World")

All nodes have the following attributes:

Attribute

Description

identifier

Identifier of the node. Must be unique among siblings.

parent

Parent node. None for the root.

children

Child nodes.

data

Dictionary for storing arbitrary data.


Adding nodes

You can add child nodes via assignment:

tree["Asia"] = Node({"population": 4.694})
tree["Africa"] = Node({"population": 1.393})

Or by providing children or a parent in the constructor:

# Adding children during creation
tree = Node(
    {"population": 7.909},
    identifier="World",
    children={
        "Asia": Node({"population": 4.694}),
        "Africa": Node({"population": 1.393})
    }
)

# Assigning a parent
Node({"population": 1.029}, identifier="America", parent=tree)

Restrictions

  • A node cannot have multiple parents:

tree1 = Node()
tree2 = Node()
child = Node()

tree1["child"] = child  # OK
tree2["child"] = child  # Raises DuplicateParentError

Use detach() or copy() to reuse nodes:

tree2["child"] = child.detach()  # Move node
tree2["child"] = child.copy()    # Copy node
  • Sibling identifiers must be unique:

tree = Node()
child1 = Node(identifier="name", parent=tree)
child2 = Node(identifier="name", parent=tree)  # DuplicateChildError
child2 = Node(identifier="different_name", parent=tree)  # OK
  • Cycles are not allowed:

tree = Node()
tree["child"] = tree  # LoopError

Bulk adding

update() allows adding multiple children efficiently:

tree.update({
    "Asia": Node({"population": 4.694}),
    "Africa": Node({"population": 1.393}),
})

You can copy or detach nodes from another tree:

tree1 = Node(identifier="tree1")
tree2 = Node(identifier="tree2")

tree1["Asia"] = Node({"population": 4.694})

# Copy node from tree1 to tree2
tree2.update([tree1["Asia"]], mode="copy")

# Move node from tree1 to tree2
tree2.update([tree1["Asia"]], mode="detach")

Iteration

Common iterators:

Iterator

Description

iter(tree.children)

Iterate over children

iter(tree.path)

Iterate from root to self

iter(tree.nodes)

Iterate over all nodes

iter(tree.ancestors)

Iterate over ancestors

iter(tree.descendants)

Iterate over descendants

iter(tree.siblings)

Iterate over siblings

tree.iter_together(tree2)

Iterate over multiple trees together

iter(node1.to(node2))

Iterate from one node to another

The nodes can be iterated in a specific order. Along with each node, an item is also yielded. You can access item.depth to get the node’s depth in the tree, and item.index to get its position among siblings at that level.

for node, item in tree.nodes.preorder():
    ...

for node, item in tree.nodes.postorder():
    ...

for node, item in tree.nodes.levelorder():
    ...

Optional keep argument allows filtering nodes and their descendants:

# Limit iteration to depth 2
for node, item in tree.nodes.preorder(keep=MaxDepth(2)):
    ...

Path operations

Get the path to a node:

str(tree["Europe"].path)  # "/World/Europe"

Access nodes using paths:

lisbon = tree["Europe"]["Portugal"]["Lisbon"]  # Bracket style
lisbon = tree.path(["Europe", "Portugal", "Lisbon"])
lisbon = tree.path("Europe/Portugal/Lisbon")

Create nodes along a path if they don’t exist:

lisbon = tree.path.create(["Europe", "Portugal", "Lisbon"])
lisbon = tree.path.create("Europe/Portugal/Lisbon")

Search using glob patterns:

lisbon_nodes = tree.path.glob("**/Lisbon")

Miscellaneous tree operations

Height and breadth of the tree:

height = tree.levels.count() - 1
breadth = tree.leaves.count()

Depth of a node:

depth = node.ancestors.count()

Path from one node to another:

madrid = tree.path.create("Europe/Spain/Madrid")
madrid_to_lisbon = list(madrid.to(lisbon))

Distance between nodes (edges):

distance = madrid.to(lisbon).edges.count()

Lowest common ancestor:

europe = madrid.to(lisbon).lca

Exporting and serialization

Function

Description

to_string(), show()

Pretty print the tree

plot()

Plot with matplotlib

to_dot(), to_mermaid(), to_latex()

Export to dot, mermaid, or LaTeX

to_image(), to_pillow()

Requires Graphviz or Mermaid

from_dict() / to_dict()

Convert to/from JSON-like formats

from_rows() / to_rows()

Convert to/from path lists

from_relations() / to_relations()

Convert to/from parent-child lists

from_newick() / to_newick()

Convert to/from Newick format

from_networkx() / to_networkx()

Convert to/from networkx graphs

Example using Newick format:

family_newick = "((George, Charlotte, Louis)William,(Archie, Lilibet)Harry)'Charles III'[&&NHX:born=1948]"
family_tree = Node.from_newick(family_newick)
family_tree.show(style="round-arrow")
family_tree.to_pillow().show()  # Requires Graphviz and Pillow
family_tree.plot()  # Requires matplotlib
Charles III
{'born': '1948'}
├→ William
│  ├→ George
│  ├→ Charlotte
│  ╰→ Louis
╰→ Harry
   ├→ Archie
   ╰→ Lilibet

royals