0
# Node Operations
1
2
TreeSwift Node objects represent individual tree nodes with parent-child relationships, labels, and edge lengths. Node operations provide fine-grained control over tree structure and enable custom tree construction and manipulation algorithms.
3
4
## Capabilities
5
6
### Node Creation and Properties
7
8
Create and inspect individual nodes with their basic properties.
9
10
```python { .api }
11
class Node:
12
"""Individual tree node with parent-child relationships."""
13
14
def __init__(self, label: object = None, edge_length: float = None) -> None:
15
"""
16
Create a new Node.
17
18
Parameters:
19
- label (object): Node label (any object, typically string)
20
- edge_length (float): Length of edge leading to this node
21
"""
22
23
def __str__(self) -> str:
24
"""String representation of node (returns label as string)."""
25
26
def __copy__(self) -> Node:
27
"""Create a copy of this node."""
28
29
def __lt__(self, other: Node) -> bool:
30
"""Less than comparison based on labels."""
31
```
32
33
Usage examples:
34
35
```python
36
import treeswift
37
38
# Create nodes
39
root = treeswift.Node()
40
leaf_a = treeswift.Node(label="A", edge_length=0.1)
41
leaf_b = treeswift.Node(label="B", edge_length=0.2)
42
43
print(f"Root: {root}") # Empty string (no label)
44
print(f"Leaf A: {leaf_a}") # "A"
45
print(f"Leaf B: {leaf_b}") # "B"
46
47
# Copy nodes
48
leaf_a_copy = leaf_a.__copy__()
49
print(f"Original: {leaf_a.get_label()}, Copy: {leaf_a_copy.get_label()}")
50
51
# Compare nodes
52
nodes = [treeswift.Node(label="C"), treeswift.Node(label="A"), treeswift.Node(label="B")]
53
sorted_nodes = sorted(nodes)
54
print(f"Sorted labels: {[str(n) for n in sorted_nodes]}")
55
```
56
57
### Parent-Child Relationships
58
59
Manage the hierarchical structure of nodes through parent-child relationships.
60
61
```python { .api }
62
def add_child(self, child: Node) -> None:
63
"""
64
Add child to this node.
65
66
Parameters:
67
- child (Node): Child node to add
68
"""
69
70
def remove_child(self, child: Node) -> None:
71
"""
72
Remove child from this node.
73
74
Parameters:
75
- child (Node): Child node to remove
76
77
Raises:
78
- RuntimeError: If child is not found
79
"""
80
81
def child_nodes(self) -> list[Node]:
82
"""
83
Get list of child nodes.
84
85
Returns:
86
- list[Node]: Copy of children list
87
"""
88
89
def get_parent(self) -> Node | None:
90
"""
91
Get parent node.
92
93
Returns:
94
- Node | None: Parent node (None if root)
95
"""
96
97
def set_parent(self, parent: Node) -> None:
98
"""
99
Set parent node (use carefully to avoid breaking tree structure).
100
101
Parameters:
102
- parent (Node): New parent node
103
"""
104
```
105
106
Usage examples:
107
108
```python
109
import treeswift
110
111
# Build tree structure manually
112
root = treeswift.Node()
113
internal = treeswift.Node(edge_length=0.1)
114
leaf_a = treeswift.Node(label="A", edge_length=0.2)
115
leaf_b = treeswift.Node(label="B", edge_length=0.3)
116
117
# Construct tree
118
root.add_child(internal)
119
internal.add_child(leaf_a)
120
internal.add_child(leaf_b)
121
122
# Inspect relationships
123
print(f"Root children: {len(root.child_nodes())}")
124
print(f"Internal node parent is root: {internal.get_parent() == root}")
125
print(f"Leaf A parent: {leaf_a.get_parent() == internal}")
126
127
# Modify structure
128
leaf_c = treeswift.Node(label="C", edge_length=0.4)
129
internal.add_child(leaf_c)
130
print(f"Internal node now has {len(internal.child_nodes())} children")
131
132
# Remove child
133
internal.remove_child(leaf_c)
134
print(f"After removal: {len(internal.child_nodes())} children")
135
```
136
137
### Node Properties and Status
138
139
Query node properties and structural status within the tree.
140
141
```python { .api }
142
def is_leaf(self) -> bool:
143
"""
144
Check if node is a leaf (no children).
145
146
Returns:
147
- bool: True if leaf, False otherwise
148
"""
149
150
def is_root(self) -> bool:
151
"""
152
Check if node is root (no parent).
153
154
Returns:
155
- bool: True if root, False otherwise
156
"""
157
158
def num_children(self) -> int:
159
"""
160
Get number of children.
161
162
Returns:
163
- int: Number of child nodes
164
"""
165
166
def num_nodes(self, leaves: bool = True, internal: bool = True) -> int:
167
"""
168
Count nodes in subtree rooted at this node.
169
170
Parameters:
171
- leaves (bool): Include leaf nodes in count
172
- internal (bool): Include internal nodes in count
173
174
Returns:
175
- int: Number of selected nodes in subtree
176
"""
177
```
178
179
Usage examples:
180
181
```python
182
import treeswift
183
184
# Create tree structure
185
tree = treeswift.read_tree_newick("((A,B),(C,D));")
186
187
# Check node properties
188
for node in tree.traverse_preorder():
189
label = node.get_label() or "internal"
190
status = []
191
192
if node.is_root():
193
status.append("root")
194
if node.is_leaf():
195
status.append("leaf")
196
197
print(f"{label}: {', '.join(status) if status else 'internal'}")
198
print(f" Children: {node.num_children()}")
199
200
if not node.is_leaf():
201
subtree_size = node.num_nodes()
202
leaf_count = node.num_nodes(internal=False)
203
internal_count = node.num_nodes(leaves=False)
204
print(f" Subtree: {subtree_size} total, {leaf_count} leaves, {internal_count} internal")
205
```
206
207
### Label Management
208
209
Get and set node labels with flexible typing support.
210
211
```python { .api }
212
def get_label(self) -> object:
213
"""
214
Get node label.
215
216
Returns:
217
- object: Node label (any type, None if not set)
218
"""
219
220
def set_label(self, label: object) -> None:
221
"""
222
Set node label.
223
224
Parameters:
225
- label (object): New label (any type)
226
"""
227
```
228
229
Usage examples:
230
231
```python
232
import treeswift
233
234
# Work with different label types
235
node1 = treeswift.Node()
236
node2 = treeswift.Node()
237
node3 = treeswift.Node()
238
239
# String labels
240
node1.set_label("Species_A")
241
print(f"String label: {node1.get_label()}")
242
243
# Numeric labels
244
node2.set_label(42)
245
print(f"Numeric label: {node2.get_label()}")
246
247
# Complex object labels
248
node3.set_label({"species": "Homo sapiens", "confidence": 0.95})
249
print(f"Dict label: {node3.get_label()}")
250
251
# None labels
252
node4 = treeswift.Node()
253
print(f"No label: {node4.get_label()}")
254
print(f"String representation: '{node4}'")
255
```
256
257
### Edge Length Management
258
259
Manage branch lengths associated with nodes.
260
261
```python { .api }
262
def get_edge_length(self) -> float | None:
263
"""
264
Get edge length leading to this node.
265
266
Returns:
267
- float | None: Edge length (None if not set)
268
"""
269
270
def set_edge_length(self, length: float) -> None:
271
"""
272
Set edge length leading to this node.
273
274
Parameters:
275
- length (float): Edge length (must be convertible to float)
276
277
Raises:
278
- TypeError: If length cannot be converted to float
279
"""
280
```
281
282
Usage examples:
283
284
```python
285
import treeswift
286
287
# Edge length operations
288
node = treeswift.Node(label="A")
289
290
# Initially no edge length
291
print(f"Initial edge length: {node.get_edge_length()}")
292
293
# Set edge length
294
node.set_edge_length(0.123)
295
print(f"Set edge length: {node.get_edge_length()}")
296
297
# Update edge length
298
node.set_edge_length(0.456)
299
print(f"Updated edge length: {node.get_edge_length()}")
300
301
# Edge length type conversion
302
node.set_edge_length("0.789") # String converted to float
303
print(f"From string: {node.get_edge_length()}")
304
305
node.set_edge_length(1) # Integer converted to float
306
print(f"From integer: {node.get_edge_length()}")
307
308
# Error handling
309
try:
310
node.set_edge_length("invalid")
311
except TypeError as e:
312
print(f"Error: {e}")
313
```
314
315
### Node Structural Operations
316
317
Modify node structure and resolve local tree issues.
318
319
```python { .api }
320
def contract(self) -> None:
321
"""Contract this node by connecting children directly to parent."""
322
323
def resolve_polytomies(self) -> None:
324
"""Resolve polytomies below this node using zero-length edges."""
325
```
326
327
Usage examples:
328
329
```python
330
import treeswift
331
332
# Contract node example
333
tree = treeswift.read_tree_newick("(((A,B),C),D);")
334
print(f"Before contraction: {tree.newick()}")
335
336
# Find internal node to contract
337
for node in tree.traverse_internal():
338
if node.num_children() == 1: # Unifurcation
339
node.contract() # Remove this node
340
break
341
342
print(f"After contraction: {tree.newick()}")
343
344
# Resolve polytomies at node level
345
tree = treeswift.Tree()
346
root = tree.root
347
for label in ["A", "B", "C", "D"]:
348
child = treeswift.Node(label=label, edge_length=0.1)
349
root.add_child(child)
350
351
print(f"Polytomy: {tree.newick()}")
352
root.resolve_polytomies() # Resolve only below this node
353
print(f"Resolved: {tree.newick()}")
354
```
355
356
### Node Output Formatting
357
358
Generate string representations of subtrees rooted at nodes.
359
360
```python { .api }
361
def newick(self) -> str:
362
"""
363
Generate Newick string for subtree rooted at this node.
364
365
Returns:
366
- str: Newick representation of subtree
367
"""
368
```
369
370
Usage examples:
371
372
```python
373
import treeswift
374
375
# Generate subtree Newick strings
376
tree = treeswift.read_tree_newick("((A:0.1,B:0.2):0.3,(C:0.4,D:0.5):0.6);")
377
378
print(f"Full tree: {tree.newick()}")
379
380
# Get Newick for each subtree
381
for node in tree.traverse_internal():
382
if node != tree.root:
383
subtree_newick = node.newick()
384
print(f"Subtree with {node.num_children()} children: {subtree_newick}")
385
386
# Single node Newick
387
for leaf in tree.traverse_leaves():
388
leaf_newick = leaf.newick()
389
print(f"Leaf {leaf.get_label()}: {leaf_newick}")
390
```
391
392
### Node Attributes Access
393
394
Direct access to node attributes (use with caution).
395
396
```python { .api }
397
# Node attributes (direct access)
398
children: list[Node] # List of child nodes
399
parent: Node | None # Parent node (None for root)
400
label: object # Node label
401
edge_length: float | None # Edge length
402
```
403
404
Usage examples:
405
406
```python
407
import treeswift
408
409
# Direct attribute access (advanced usage)
410
node = treeswift.Node(label="test", edge_length=0.5)
411
412
# Access attributes directly
413
print(f"Label: {node.label}")
414
print(f"Edge length: {node.edge_length}")
415
print(f"Children list: {node.children}")
416
print(f"Parent: {node.parent}")
417
418
# Modify attributes directly (be careful!)
419
node.label = "modified_label"
420
node.edge_length = 1.0
421
422
# Add to tree
423
tree = treeswift.Tree()
424
tree.root.children.append(node) # Direct list manipulation
425
node.parent = tree.root # Set parent reference
426
427
print(f"Tree: {tree.newick()}")
428
```