0
# Tree Manipulation
1
2
TreeSwift provides extensive tree modification operations including rerooting, subtree extraction, node contraction, polytomy resolution, and branch manipulation. Operations support both in-place modifications and copy-based operations for flexible tree editing workflows.
3
4
## Capabilities
5
6
### Tree Rerooting
7
8
Change the root of the tree to a different location, optionally specifying the distance along the target branch.
9
10
```python { .api }
11
def reroot(self, node: Node, length: float = None, branch_support: bool = False) -> None:
12
"""
13
Reroot tree at specified node and distance.
14
15
Parameters:
16
- node (Node): Node where tree should be rerooted
17
- length (float): Distance along target edge for new root (None for midpoint)
18
- branch_support (bool): Whether to handle branch support values during rerooting
19
"""
20
```
21
22
Usage examples:
23
24
```python
25
import treeswift
26
27
# Load a tree
28
tree = treeswift.read_tree_newick("((A:0.1,B:0.2):0.3,(C:0.4,D:0.5):0.6);")
29
print(f"Original: {tree.newick()}")
30
31
# Find a leaf to reroot on
32
for node in tree.traverse_leaves():
33
if node.get_label() == "A":
34
# Reroot at leaf A
35
tree.reroot(node)
36
print(f"Rerooted at A: {tree.newick()}")
37
break
38
39
# Reroot at specific distance along an edge
40
tree = treeswift.read_tree_newick("((A:0.1,B:0.2):0.3,(C:0.4,D:0.5):0.6);")
41
for node in tree.traverse_leaves():
42
if node.get_label() == "B":
43
# Reroot at distance 0.05 along edge leading to B
44
tree.reroot(node, length=0.05)
45
print(f"Rerooted at 0.05 from B: {tree.newick()}")
46
break
47
```
48
49
### Tree Extraction
50
51
Extract subtrees or create new trees with specific taxa.
52
53
```python { .api }
54
def extract_subtree(self, node: Node) -> Tree:
55
"""
56
Extract subtree rooted at specified node.
57
58
Parameters:
59
- node (Node): Root node of subtree to extract
60
61
Returns:
62
- Tree: New tree containing subtree rooted at node
63
"""
64
65
def extract_tree_with(self, labels: set, suppress_unifurcations: bool = True) -> Tree:
66
"""
67
Extract tree containing only leaves with specified labels.
68
69
Parameters:
70
- labels (set): Set of leaf labels to keep
71
- suppress_unifurcations (bool): Remove nodes with single child
72
73
Returns:
74
- Tree: New tree containing only specified leaves
75
"""
76
77
def extract_tree_without(self, labels: set, suppress_unifurcations: bool = True) -> Tree:
78
"""
79
Extract tree excluding leaves with specified labels.
80
81
Parameters:
82
- labels (set): Set of leaf labels to exclude
83
- suppress_unifurcations (bool): Remove nodes with single child
84
85
Returns:
86
- Tree: New tree without specified leaves
87
"""
88
```
89
90
Usage examples:
91
92
```python
93
import treeswift
94
95
tree = treeswift.read_tree_newick("(((A:0.1,B:0.2):0.3,C:0.4):0.5,((D:0.6,E:0.7):0.8,F:0.9):1.0);")
96
97
# Extract subtree rooted at internal node
98
for node in tree.traverse_internal():
99
if node.num_children() == 2:
100
subtree = tree.extract_subtree(node)
101
print(f"Subtree: {subtree.newick()}")
102
break
103
104
# Keep only specific taxa
105
keep_taxa = {"A", "B", "C"}
106
reduced_tree = tree.extract_tree_with(keep_taxa)
107
print(f"Tree with A,B,C: {reduced_tree.newick()}")
108
109
# Remove specific taxa
110
remove_taxa = {"A", "B"}
111
filtered_tree = tree.extract_tree_without(remove_taxa)
112
print(f"Tree without A,B: {filtered_tree.newick()}")
113
114
# Keep structure (don't suppress unifurcations)
115
keep_structure = tree.extract_tree_with({"A", "C"}, suppress_unifurcations=False)
116
print(f"With structure: {keep_structure.newick()}")
117
```
118
119
### Branch and Node Manipulation
120
121
Modify tree structure by contracting, collapsing, or removing nodes and branches.
122
123
```python { .api }
124
def contract_low_support(self, threshold: float, terminal: bool = False, internal: bool = True) -> None:
125
"""
126
Contract nodes with support values below threshold.
127
128
Parameters:
129
- threshold (float): Support value threshold for contraction
130
- terminal (bool): Contract terminal branches below threshold
131
- internal (bool): Contract internal branches below threshold
132
"""
133
134
def collapse_short_branches(self, threshold: float) -> None:
135
"""
136
Collapse internal branches with length <= threshold.
137
138
Parameters:
139
- threshold (float): Length threshold for branch collapse
140
"""
141
142
def suppress_unifurcations(self) -> None:
143
"""Remove nodes with single child by connecting child directly to parent."""
144
145
def resolve_polytomies(self) -> None:
146
"""Arbitrarily resolve polytomies using zero-length edges."""
147
```
148
149
Usage examples:
150
151
```python
152
import treeswift
153
154
# Contract low support branches
155
tree = treeswift.read_tree_newick("((A:0.1,B:0.2)0.3:0.05,(C:0.4,D:0.5)0.8:0.15);")
156
tree.contract_low_support(0.5) # Contract nodes with support < 0.5
157
print(f"After support filter: {tree.newick()}")
158
159
# Collapse short branches
160
tree = treeswift.read_tree_newick("((A:0.001,B:0.2):0.05,(C:0.4,D:0.002):0.15);")
161
tree.collapse_short_branches(0.01) # Collapse branches <= 0.01
162
print(f"After collapsing short branches: {tree.newick()}")
163
164
# Resolve polytomies
165
tree = treeswift.read_tree_newick("(A,B,C,D);") # 4-way polytomy
166
print(f"Original polytomy: {tree.newick()}")
167
tree.resolve_polytomies()
168
print(f"Resolved: {tree.newick()}")
169
170
# Suppress unifurcations (remove single-child nodes)
171
tree = treeswift.read_tree_newick("(((A)));") # Unnecessary internal nodes
172
tree.suppress_unifurcations()
173
print(f"After suppression: {tree.newick()}")
174
```
175
176
### Edge Length Manipulation
177
178
Modify branch lengths and scale the entire tree.
179
180
```python { .api }
181
def scale_edges(self, multiplier: float) -> None:
182
"""
183
Multiply all edge lengths by multiplier.
184
185
Parameters:
186
- multiplier (float): Scaling factor for all edge lengths
187
"""
188
```
189
190
Usage examples:
191
192
```python
193
import treeswift
194
195
# Scale tree by factor
196
tree = treeswift.read_tree_newick("((A:0.1,B:0.2):0.05,(C:0.3,D:0.4):0.15);")
197
print(f"Original: {tree.newick()}")
198
199
# Double all branch lengths
200
tree_copy = tree.__copy__()
201
tree_copy.scale_edges(2.0)
202
print(f"Scaled by 2.0: {tree_copy.newick()}")
203
204
# Convert to units of 1000 (e.g., from substitutions per site to per 1000 sites)
205
tree_copy = tree.__copy__()
206
tree_copy.scale_edges(1000)
207
print(f"Scaled by 1000: {tree_copy.newick()}")
208
```
209
210
### Tree Structure Modification
211
212
Modify the fundamental structure of the tree including rooting and tree organization.
213
214
```python { .api }
215
def deroot(self) -> None:
216
"""Contract edge at root to create trifurcation (unroot tree)."""
217
218
def ladderize(self, ascending: bool = True) -> None:
219
"""
220
Ladderize tree by sorting children by number of descendants.
221
222
Parameters:
223
- ascending (bool): Sort in ascending order of descendant count
224
"""
225
226
def order(self, mode: str, ascending: bool = True) -> None:
227
"""
228
Order children of nodes based on various criteria.
229
230
Parameters:
231
- mode (str): Ordering criterion ('edge_length', 'label', 'num_descendants')
232
- ascending (bool): Sort in ascending order
233
"""
234
```
235
236
Usage examples:
237
238
```python
239
import treeswift
240
241
# Deroot a tree
242
tree = treeswift.read_tree_newick("((A,B),(C,D));")
243
print(f"Rooted: {tree.newick()}, is_rooted: {tree.is_rooted}")
244
tree.deroot()
245
print(f"Unrooted: {tree.newick()}, is_rooted: {tree.is_rooted}")
246
247
# Ladderize tree (smaller clades first)
248
tree = treeswift.read_tree_newick("(((A,B),C),((D,E,F),(G,H)));")
249
print(f"Original: {tree.newick()}")
250
tree.ladderize(ascending=True)
251
print(f"Ladderized (ascending): {tree.newick()}")
252
253
# Order by edge length
254
tree = treeswift.read_tree_newick("((A:0.5,B:0.1):0.2,(C:0.3,D:0.8):0.4);")
255
print(f"Original: {tree.newick()}")
256
tree.order("edge_length", ascending=True)
257
print(f"Ordered by edge length: {tree.newick()}")
258
```
259
260
### Node Label and Metadata Management
261
262
Rename nodes and manage node metadata.
263
264
```python { .api }
265
def rename_nodes(self, renaming_map: dict) -> None:
266
"""
267
Rename nodes using mapping from old to new labels.
268
269
Parameters:
270
- renaming_map (dict): Dictionary mapping old labels to new labels
271
"""
272
273
def condense(self) -> None:
274
"""Merge siblings with same label, keeping larger edge length."""
275
```
276
277
Usage examples:
278
279
```python
280
import treeswift
281
282
# Rename nodes
283
tree = treeswift.read_tree_newick("((species_A:0.1,species_B:0.2):0.05,(species_C:0.3,species_D:0.4):0.15);")
284
rename_map = {
285
"species_A": "A",
286
"species_B": "B",
287
"species_C": "C",
288
"species_D": "D"
289
}
290
tree.rename_nodes(rename_map)
291
print(f"Renamed: {tree.newick()}")
292
293
# Condense duplicate labels
294
tree = treeswift.read_tree_newick("((A:0.1,A:0.2):0.05,(B:0.3,B:0.1):0.15);")
295
print(f"Before condense: {tree.newick()}")
296
tree.condense() # Merge duplicate labels, keep longer edge
297
print(f"After condense: {tree.newick()}")
298
```
299
300
### Root Edge Handling
301
302
Special handling for trees with edge lengths at the root.
303
304
```python { .api }
305
def drop_edge_length_at_root(self, label: str = 'OLDROOT') -> None:
306
"""
307
Convert root edge length to child node.
308
309
Parameters:
310
- label (str): Label for new node created from root edge
311
"""
312
```
313
314
Usage examples:
315
316
```python
317
import treeswift
318
319
# Handle root edge length
320
# Note: This requires a tree with edge length at root
321
tree = treeswift.Tree()
322
root = tree.root
323
root.edge_length = 0.1 # Root has edge length
324
325
child1 = treeswift.Node(label="A", edge_length=0.2)
326
child2 = treeswift.Node(label="B", edge_length=0.3)
327
root.add_child(child1)
328
root.add_child(child2)
329
330
print(f"Before: {tree.newick()}")
331
tree.drop_edge_length_at_root("OUTGROUP")
332
print(f"After dropping root edge: {tree.newick()}")
333
```