or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.mdnode-operations.mdtree-analysis.mdtree-io.mdtree-manipulation.mdtree-traversal.mdvisualization.md

tree-manipulation.mddocs/

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

```