TreeSwift: Fast tree module for Python 2 and 3 - A Python library for parsing, manipulating, and iterating over rooted tree structures with emphasis on performance and speed.
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.
Change the root of the tree to a different location, optionally specifying the distance along the target branch.
def reroot(self, node: Node, length: float = None, branch_support: bool = False) -> None:
"""
Reroot tree at specified node and distance.
Parameters:
- node (Node): Node where tree should be rerooted
- length (float): Distance along target edge for new root (None for midpoint)
- branch_support (bool): Whether to handle branch support values during rerooting
"""Usage examples:
import treeswift
# Load a tree
tree = treeswift.read_tree_newick("((A:0.1,B:0.2):0.3,(C:0.4,D:0.5):0.6);")
print(f"Original: {tree.newick()}")
# Find a leaf to reroot on
for node in tree.traverse_leaves():
if node.get_label() == "A":
# Reroot at leaf A
tree.reroot(node)
print(f"Rerooted at A: {tree.newick()}")
break
# Reroot at specific distance along an edge
tree = treeswift.read_tree_newick("((A:0.1,B:0.2):0.3,(C:0.4,D:0.5):0.6);")
for node in tree.traverse_leaves():
if node.get_label() == "B":
# Reroot at distance 0.05 along edge leading to B
tree.reroot(node, length=0.05)
print(f"Rerooted at 0.05 from B: {tree.newick()}")
breakExtract subtrees or create new trees with specific taxa.
def extract_subtree(self, node: Node) -> Tree:
"""
Extract subtree rooted at specified node.
Parameters:
- node (Node): Root node of subtree to extract
Returns:
- Tree: New tree containing subtree rooted at node
"""
def extract_tree_with(self, labels: set, suppress_unifurcations: bool = True) -> Tree:
"""
Extract tree containing only leaves with specified labels.
Parameters:
- labels (set): Set of leaf labels to keep
- suppress_unifurcations (bool): Remove nodes with single child
Returns:
- Tree: New tree containing only specified leaves
"""
def extract_tree_without(self, labels: set, suppress_unifurcations: bool = True) -> Tree:
"""
Extract tree excluding leaves with specified labels.
Parameters:
- labels (set): Set of leaf labels to exclude
- suppress_unifurcations (bool): Remove nodes with single child
Returns:
- Tree: New tree without specified leaves
"""Usage examples:
import treeswift
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);")
# Extract subtree rooted at internal node
for node in tree.traverse_internal():
if node.num_children() == 2:
subtree = tree.extract_subtree(node)
print(f"Subtree: {subtree.newick()}")
break
# Keep only specific taxa
keep_taxa = {"A", "B", "C"}
reduced_tree = tree.extract_tree_with(keep_taxa)
print(f"Tree with A,B,C: {reduced_tree.newick()}")
# Remove specific taxa
remove_taxa = {"A", "B"}
filtered_tree = tree.extract_tree_without(remove_taxa)
print(f"Tree without A,B: {filtered_tree.newick()}")
# Keep structure (don't suppress unifurcations)
keep_structure = tree.extract_tree_with({"A", "C"}, suppress_unifurcations=False)
print(f"With structure: {keep_structure.newick()}")Modify tree structure by contracting, collapsing, or removing nodes and branches.
def contract_low_support(self, threshold: float, terminal: bool = False, internal: bool = True) -> None:
"""
Contract nodes with support values below threshold.
Parameters:
- threshold (float): Support value threshold for contraction
- terminal (bool): Contract terminal branches below threshold
- internal (bool): Contract internal branches below threshold
"""
def collapse_short_branches(self, threshold: float) -> None:
"""
Collapse internal branches with length <= threshold.
Parameters:
- threshold (float): Length threshold for branch collapse
"""
def suppress_unifurcations(self) -> None:
"""Remove nodes with single child by connecting child directly to parent."""
def resolve_polytomies(self) -> None:
"""Arbitrarily resolve polytomies using zero-length edges."""Usage examples:
import treeswift
# Contract low support branches
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);")
tree.contract_low_support(0.5) # Contract nodes with support < 0.5
print(f"After support filter: {tree.newick()}")
# Collapse short branches
tree = treeswift.read_tree_newick("((A:0.001,B:0.2):0.05,(C:0.4,D:0.002):0.15);")
tree.collapse_short_branches(0.01) # Collapse branches <= 0.01
print(f"After collapsing short branches: {tree.newick()}")
# Resolve polytomies
tree = treeswift.read_tree_newick("(A,B,C,D);") # 4-way polytomy
print(f"Original polytomy: {tree.newick()}")
tree.resolve_polytomies()
print(f"Resolved: {tree.newick()}")
# Suppress unifurcations (remove single-child nodes)
tree = treeswift.read_tree_newick("(((A)));") # Unnecessary internal nodes
tree.suppress_unifurcations()
print(f"After suppression: {tree.newick()}")Modify branch lengths and scale the entire tree.
def scale_edges(self, multiplier: float) -> None:
"""
Multiply all edge lengths by multiplier.
Parameters:
- multiplier (float): Scaling factor for all edge lengths
"""Usage examples:
import treeswift
# Scale tree by factor
tree = treeswift.read_tree_newick("((A:0.1,B:0.2):0.05,(C:0.3,D:0.4):0.15);")
print(f"Original: {tree.newick()}")
# Double all branch lengths
tree_copy = tree.__copy__()
tree_copy.scale_edges(2.0)
print(f"Scaled by 2.0: {tree_copy.newick()}")
# Convert to units of 1000 (e.g., from substitutions per site to per 1000 sites)
tree_copy = tree.__copy__()
tree_copy.scale_edges(1000)
print(f"Scaled by 1000: {tree_copy.newick()}")Modify the fundamental structure of the tree including rooting and tree organization.
def deroot(self) -> None:
"""Contract edge at root to create trifurcation (unroot tree)."""
def ladderize(self, ascending: bool = True) -> None:
"""
Ladderize tree by sorting children by number of descendants.
Parameters:
- ascending (bool): Sort in ascending order of descendant count
"""
def order(self, mode: str, ascending: bool = True) -> None:
"""
Order children of nodes based on various criteria.
Parameters:
- mode (str): Ordering criterion ('edge_length', 'label', 'num_descendants')
- ascending (bool): Sort in ascending order
"""Usage examples:
import treeswift
# Deroot a tree
tree = treeswift.read_tree_newick("((A,B),(C,D));")
print(f"Rooted: {tree.newick()}, is_rooted: {tree.is_rooted}")
tree.deroot()
print(f"Unrooted: {tree.newick()}, is_rooted: {tree.is_rooted}")
# Ladderize tree (smaller clades first)
tree = treeswift.read_tree_newick("(((A,B),C),((D,E,F),(G,H)));")
print(f"Original: {tree.newick()}")
tree.ladderize(ascending=True)
print(f"Ladderized (ascending): {tree.newick()}")
# Order by edge length
tree = treeswift.read_tree_newick("((A:0.5,B:0.1):0.2,(C:0.3,D:0.8):0.4);")
print(f"Original: {tree.newick()}")
tree.order("edge_length", ascending=True)
print(f"Ordered by edge length: {tree.newick()}")Rename nodes and manage node metadata.
def rename_nodes(self, renaming_map: dict) -> None:
"""
Rename nodes using mapping from old to new labels.
Parameters:
- renaming_map (dict): Dictionary mapping old labels to new labels
"""
def condense(self) -> None:
"""Merge siblings with same label, keeping larger edge length."""Usage examples:
import treeswift
# Rename nodes
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);")
rename_map = {
"species_A": "A",
"species_B": "B",
"species_C": "C",
"species_D": "D"
}
tree.rename_nodes(rename_map)
print(f"Renamed: {tree.newick()}")
# Condense duplicate labels
tree = treeswift.read_tree_newick("((A:0.1,A:0.2):0.05,(B:0.3,B:0.1):0.15);")
print(f"Before condense: {tree.newick()}")
tree.condense() # Merge duplicate labels, keep longer edge
print(f"After condense: {tree.newick()}")Special handling for trees with edge lengths at the root.
def drop_edge_length_at_root(self, label: str = 'OLDROOT') -> None:
"""
Convert root edge length to child node.
Parameters:
- label (str): Label for new node created from root edge
"""Usage examples:
import treeswift
# Handle root edge length
# Note: This requires a tree with edge length at root
tree = treeswift.Tree()
root = tree.root
root.edge_length = 0.1 # Root has edge length
child1 = treeswift.Node(label="A", edge_length=0.2)
child2 = treeswift.Node(label="B", edge_length=0.3)
root.add_child(child1)
root.add_child(child2)
print(f"Before: {tree.newick()}")
tree.drop_edge_length_at_root("OUTGROUP")
print(f"After dropping root edge: {tree.newick()}")Install with Tessl CLI
npx tessl i tessl/pypi-treeswift