CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-treeswift

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.

Overview
Eval results
Files

tree-manipulation.mddocs/

Tree Manipulation

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.

Capabilities

Tree Rerooting

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()}")
        break

Tree Extraction

Extract 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()}")

Branch and Node Manipulation

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()}")

Edge Length Manipulation

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()}")

Tree Structure Modification

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()}")

Node Label and Metadata Management

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()}")

Root Edge Handling

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

docs

index.md

node-operations.md

tree-analysis.md

tree-io.md

tree-manipulation.md

tree-traversal.md

visualization.md

tile.json