Tree¶
- class baltic.tree.Tree(treeType)¶
Represent a phylogenetic tree and provide traversal, manipulation, and plotting helpers.
Initialize an empty
baltictree.A
Treestoresbaltic.branchLike.BranchLikedescendants such asbaltic.node.Nodeandbaltic.leaf.Leafin a flatObjectslist alongside arootreference.The tree starts empty:
rootisNoneandObjectsis empty, whilecurNodeholds a placeholder node thatadd_node()andadd_leaf()attach the first branches to. Most users build trees withbaltic.baltic.make_tree()or the loaders inbaltic.iorather than constructing one directly.Parameters
- treeType{“divergence”, “time”}
Interpretation of branch lengths in the tree.
"divergence"makesheightauthoritative,"time"makesabsoluteTimeauthoritative.
Returns
None
Raises
- AssertionError
If treeType is neither
"divergence"nor"time".
Examples
>>> import baltic as bt >>> ll = bt.Tree("divergence") >>> ll.treeType 'divergence'
- add_leaf(i, name)¶
Create a new
Leafwith appropriate parent-child links; add it to the tree.Parameters
- iint
Unique index into the tree.
- namestr
Name of the new leaf.
Notes
If the tree does not have a root (i.e. a tree with no branches), then the new leaf will be set as the root of the tree.
After the new leaf is added,
self.curNodewill update to the newly created leaf. Because a leaf cannot take children, adding another branch requires reassigningself.curNodefirst.Returns
- None
The tree is modified in place.
Raises
- TypeError
If the current node of the tree to which the new leaf will be added is not itself a valid node (e.g. if
self.curNodeis aLeaf).
Examples
>>> import baltic as bt >>> ll = bt.Tree(treeType="divergence") >>> ll.add_node(1) >>> ll.add_leaf(2, "LeafA") >>> ll.root.index 1 >>> ll.curNode.index 2 >>> ll.curNode.name 'LeafA'
- add_node(i)¶
Create a new internal
Nodewith appropriate parent-child links; add it to the tree.Parameters
- iint
Unique index into the tree.
Notes
If the tree does not have a
.root(i.e. a tree with no branches), then the new node will be set as the root of the tree.After the new node is added,
self.curNodewill update to the newly created node.Returns
- None
The tree is modified in place.
Raises
- TypeError
If the current node of the tree to which the new node will be added is not itself a valid node (e.g. if
self.curNodeis aLeaf).
Examples
>>> import baltic as bt >>> ll = bt.Tree(treeType="divergence") >>> ll.add_node(1) >>> ll.add_node(2) >>> print(ll.root.index) 1 >>> print(ll.curNode.index) 2
- add_reticulation(name)¶
Create and attach a new reticulation edge below the current node.
This creates a
baltic.reticulation.Reticulation.Notes
The new reticulation’s
indexis set to name rather than to an integer, andself.curNodemoves onto it. Since a reticulation is leaf-like, the nextadd_node()oradd_leaf()would try to attach a child to it and raiseTypeError; reassignself.curNodefirst.Parameters
- namestr
Name assigned to the new reticulation object. Also used as its index.
Returns
- None
The tree is modified in place.
Examples
>>> import baltic as bt >>> ll = bt.Tree("divergence") >>> ll.add_node(1) >>> ll.add_reticulation("ret1") >>> ll.curNode.name 'ret1'
- collapse_branches(collapseIfFxn=<function Tree.<lambda>>, designatedNodes=[])¶
Collapse branches according to a determined function, creating polytomies.
Unlike most manipulation methods, this does not modify the tree: it works on a deep copy and returns that, leaving the original untouched. A collapsed node’s children are reattached to its parent and its branch length is added to each of theirs, so tree length is preserved. Collapsing is repeated until no node satisfies the condition.
The tree must have been traversed first, since node heights order the collapsing.
Parameters
- collapseIfFxncallable, optional
Function deciding whether a node should be collapsed. By default, branches with posterior support at or below 0.5 are collapsed (
lambda x: x.traits["posterior"] <= 0.5), which raisesKeyErroron a tree whose nodes carry noposteriortrait – pass an explicit function for non-BEAST trees.- designatedNodeslist[
Node], optional Explicit list of nodes to collapse instead of using collapseIfFxn.
Warning
This parameter does not currently work. Its guard compares the nodes you pass against the root of the internal deep copy, which is never the same object, so any non-empty list raises
AssertionError: Root node was designated for deletion. Use collapseIfFxn – for examplelambda n: n.index in {...}– until this is fixed.
Returns
TreeA new, collapsed tree. The original is unchanged.
Raises
- AssertionError
If the requested collapsing would remove every internal branch, or if designatedNodes is non-empty (see the warning above).
Examples
>>> import baltic as bt >>> ll = bt.make_tree("(((A:1.0,B:1.0):1.0,C:1.0):1.0,D:1.0);", treeType="divergence") >>> _ = ll.traverse_tree() >>> for node in ll.get_internal(): ... node.traits["posterior"] = 1.0 >>> ll.find_MRCA("A", "B").traits["posterior"] = 0.0 >>> collapsed = ll.collapse_branches(collapseIfFxn=lambda n: n.traits["posterior"] <= 0.5) >>> len(collapsed.get_internal()) < len(ll.get_internal()) True
- collapse_subtree_to_clade(cl, givenName, widthFunction=<function Tree.<lambda>>)¶
Replace a subtree with a collapsed
Cladeplaceholder.The subtree’s branches are removed from the tree and stored on the clade, so the operation is reversible with
restore_all_collapsed_subtrees(). The clade inherits the node’s index, length, height, absolute time and traits; the tree is re-traversed and re-sorted before returning. If the tree has atipMap, an entry is added for the new clade.Requires a traversed tree, since the clade’s width and descendant set come from the node’s
leaves.Parameters
- cl
Node Root of the subtree to collapse. Must be an internal node.
- givenNamestr
Name assigned to the collapsed clade object.
- widthFunctioncallable, optional
Function computing the visual width of the collapsed clade. By default the number of descendant tips is used.
Returns
CladeThe newly created collapsed clade. The tree itself is modified in place.
Raises
- AssertionError
If cl is not an internal node, or if collapsing it would consume the entire tree.
Examples
>>> import baltic as bt >>> ll = bt.make_tree("(((A:1.0,B:1.0):1.0,C:1.0):1.0,D:1.0);", treeType="divergence") >>> _ = ll.traverse_tree() >>> cl = ll.collapse_subtree_to_clade(ll.find_MRCA("A", "B"), "AB clade") >>> cl.name 'AB clade'
- cl
- condense_tree(cutoffs=None, protectedTips=[], widthFxn=None)¶
Collapse eligible subtrees into clade placeholders.
Each qualifying node is replaced via
collapse_subtree_to_clade()and named"collapsed clade <n>". Only the outermost qualifying nodes are collapsed, so clades are never nested. The tree is modified in place, and the collapsing is reversible withrestore_all_collapsed_subtrees().Requires a traversed tree, since eligibility is decided from each node’s
leaves.Parameters
- cutoffstuple, optional
Inclusive
(min_size, max_size)bounds, in descendant tips, for collapsible clades. By default(3, int(0.2 * number_of_tips)).Note
On a tree with fewer than 15 tips that default makes the upper bound smaller than the lower one, so nothing qualifies and the call is a silent no-op. Pass cutoffs explicitly for small trees.
- protectedTipslist, optional
Tip names that must remain outside collapsed clades. Any node whose descendants include one of these is skipped.
- widthFxncallable, optional
Function computing the visual width of each collapsed clade. By default a clade occupies as much vertical space as its tip count.
Returns
TreeThe same tree object, modified in place. The return value is a convenience for chaining, not a copy.
Examples
>>> import baltic as bt >>> ll = bt.make_tree("((((A:1.0,B:1.0):1.0,C:1.0):1.0,D:1.0):1.0,E:1.0);", treeType="divergence") >>> _ = ll.traverse_tree() >>> _ = ll.condense_tree(cutoffs=(2, 3)) >>> any(branch.__class__.__name__ == "Clade" for branch in ll.Objects) True
- count_lineages_at_time(t, timeAttr='absoluteTime', inclusionConditionFxn=<function Tree.<lambda>>)¶
Count branches spanning a given time value.
A branch is counted when
parent_time < t <= branch_time, so the interval is open at the parent’s end and closed at the branch’s own. A branch whose parent has no time assigned is skipped, which excludes the root.This is typically used after
set_absolute_time().Parameters
- tfloat
Time point at which to count extant lineages.
- timeAttrstr, default=”absoluteTime”
Branch attribute to use as the time coordinate. Pass
"height"to count lineages on a divergence tree.- inclusionConditionFxncallable, optional
Predicate selecting which branches contribute to the count. By default every spanning branch counts.
Returns
- int
Number of matching lineages.
Examples
>>> import baltic as bt >>> ll = bt.make_tree("((A:1.0,B:1.0):1.0,C:2.0);", treeType="time") >>> _ = ll.traverse_tree() >>> ll.set_absolute_time(2020.0) >>> ll.count_lineages_at_time(2019.5) 3 >>> ll.count_lineages_at_time(2018.5) 2
- explode_tree(trait=None, customFxn=None, stem=True)¶
Split a tree into subtrees at trait transitions or custom breakpoints.
A new subtree starts at the root and at every branch the split rule marks; each is extracted with
subtree(), so the results are deep copies and editing them does not affect this tree.Exactly one of trait or customFxn must be given.
Parameters
- traitstr, optional
Trait name used to define subtree boundaries when its value differs from the parent’s. Raises
KeyErrorif any branch lacks the trait.- customFxncallable, optional
Custom predicate that marks branches starting new subtrees.
- stembool, default=True
If
True, include the stem branch leading into each extracted subtree.
Returns
- list[
Tree] Extracted subtrees. A split point whose subtree contains no leaves is logged as an error and dropped, so this can be shorter than the number of branches the rule marked.
Raises
- ValueError
If both trait and customFxn are given, or neither.
Examples
>>> import baltic as bt >>> ll = bt.make_tree("(((A:1.0,B:1.0):1.0,C:1.0):1.0,D:1.0);", treeType="divergence") >>> subtrees = ll.explode_tree(customFxn=lambda k: k.is_node() and len(k.leaves) == 3, stem=False) >>> len(subtrees) 2
- find_MRCA(*descendants)¶
Find the most recent common ancestor of a list of descendant nodes.
Descendants may be passed as separate arguments, as a single list, as tip name strings, or as branch objects – but strings and branches cannot be mixed in one call.
Parameters
- *descendants
BranchLikeor str Descendant branches or tip names whose MRCA is being searched. A single list argument is unpacked. Mixing strings and branch objects raises
AttributeError.
Returns
BranchLikeor NoneThe most recent common ancestor: a
Nodein the usual case, but the branch itself when only one descendant is given, andNonewhen no descendants are given at all.
Raises
- AssertionError
If any tip name cannot be found in the tree.
Examples
>>> import baltic as bt >>> ll = bt.make_tree("(((A:1.0,B:1.0):1.0,C:1.0):1.0,D:1.0);", treeType="divergence") >>> _ = ll.traverse_tree() >>> mrca = ll.find_MRCA("A", "B") >>> sorted(mrca.leaves) ['A', 'B'] >>> ll.find_MRCA("A", "B", "C", "D") is ll.root True
- *descendants
- fix_hanging_nodes()¶
Remove internal nodes that no longer have children.
Removal repeats until no childless nodes remain, so a node orphaned by the removal of its only child is cleaned up in the same call. Hanging nodes otherwise make
traverse_tree()raiseAttributeError.This cleanup step is often needed after
subtree()orcollapse_branches().Returns
- None
The tree is modified in place. Heights are not recomputed – call
traverse_tree()afterwards if you need them current.
Examples
>>> import baltic as bt >>> ll = bt.make_tree("((A:1.0,B:1.0):1.0,C:1.0);", treeType="divergence") >>> _ = ll.traverse_tree() >>> hanging = ll.find_MRCA("A", "B") >>> hanging.children = [] >>> ll.fix_hanging_nodes() >>> hanging not in ll.Objects True
- get_all_tip_TMRCAs()¶
Compute the pairwise TMRCA matrix for all tips in a time tree.
Values are the
absoluteTimeof each pair’s common ancestor – a date on the tree’s calendar, not an elapsed duration. A tip paired with itself is0.0rather than its own date, and a pair sharing no common ancestor staysNone.Requires a traversed tree, since the descendant sets come from each node’s
leaves, and absolute dates must already be assigned withset_absolute_time().Returns
- dict[str, dict[str, float]]
Nested dictionary keyed by tip names in both directions, so
matrix[a][b]andmatrix[b][a]are both populated.
Raises
- AssertionError
If the tree’s
treeTypeis not"time".
Examples
>>> import baltic as bt >>> ll = bt.make_tree("((A:1.0,B:1.0):1.0,C:2.0);", treeType="time") >>> _ = ll.traverse_tree() >>> ll.set_absolute_time(2020.0) >>> tmrcas = ll.get_all_tip_TMRCAs() >>> tmrcas["A"]["B"] 2019.0 >>> tmrcas["A"]["A"] 0.0
- get_branches(filterFxn=<function Tree.<lambda>>, failIfNoResults=True)¶
Return branches matching a predicate.
Unlike
get_external()orget_internal(), this can return anybaltic.branchLike.BranchLikesubclass.Parameters
- filterFxncallable, optional
Predicate used to select branches. By default every branch is returned.
- failIfNoResultsbool, default=True
If
True, raise when nothing matches. IfFalse, log a warning and return an empty list instead.
Returns
- list[
BranchLike] Matching branches, in
Objectsorder.
Raises
- Exception
If no branch matches and failIfNoResults is
True.
Examples
>>> import baltic as bt >>> ll = bt.make_tree("((A:1.0,B:1.0):1.0,C:1.0);", treeType="divergence") >>> selected = ll.get_branches(lambda k: getattr(k, "length", 0) >= 1.0) >>> len(selected) >= 3 True
- get_external(filterFxn=None, onlyLeaves=True)¶
Return external branches from the tree.
The result contains
baltic.leaf.Leafobjects and, when requested, other leaf-like placeholders such asbaltic.clade.Clade.Parameters
- filterFxncallable, optional
Additional predicate applied to candidate branches. By default no additional filtering is done.
- onlyLeavesbool, default=True
If
True, return only true leaves; otherwise include all leaf-like objects such as collapsed clades and reticulations.
Returns
- list[
BranchLike] External branches satisfying the filter, in
Objectsorder.
Examples
>>> import baltic as bt >>> ll = bt.make_tree("((A:1.0,B:1.0):1.0,C:1.0);", treeType="divergence") >>> [tip.name for tip in ll.get_external(lambda k: k.name != "B")] ['A', 'C']
- get_internal(filterFxn=None)¶
Return internal nodes from the tree.
Parameters
- filterFxncallable, optional
Additional predicate applied to nodes. By default every internal node is returned.
Returns
- list[
Node] Internal nodes satisfying the filter, in
Objectsorder.
Examples
>>> import baltic as bt >>> ll = bt.make_tree("((A:1.0,B:1.0):1.0,C:1.0);", treeType="divergence") >>> len(ll.get_internal()) 2
- get_leaf(tipName)¶
Look up a single leaf by name.
Only true leaves are searched, so collapsed clades are not found; use
get_external()withonlyLeaves=Falseto include them.Parameters
- tipNamestr
Tip name to match. Matching is exact.
Returns
LeafThe matching leaf.
Raises
- AssertionError
If tipName is not a string, or if the number of matching tips is anything other than one. A missing name is an error rather than
None, and duplicate tip names are rejected the same way.
Examples
>>> import baltic as bt >>> ll = bt.make_tree("((A:1.0,B:1.0):1.0,C:1.0);", treeType="divergence") >>> ll.get_leaf("B").name 'B'
- get_parameter_list(statistic, useTraitsDict=False, filterFxn=None)¶
Collect attribute or trait values across branches.
This helper is often paired with
get_branches().Parameters
- statisticstr
Attribute name or trait key to extract.
- useTraitsDictbool, default=False
If
True, read statistic from each branch’straitsdictionary instead of as an attribute.- filterFxncallable, optional
Predicate selecting which branches to inspect. By default every branch is inspected.
Returns
- list
Extracted values. Branches that lack the attribute or trait are skipped silently, so the result can be shorter than the number of branches inspected and its entries do not line up positionally with any branch list. An unknown statistic yields an empty list rather than an error.
Examples
>>> import baltic as bt >>> ll = bt.make_tree("((A:1.0,B:2.0):1.0,C:3.0);", treeType="divergence") >>> sorted(ll.get_parameter_list("length")) [0.0, 1.0, 1.0, 2.0, 3.0]
Only the three tips carry a
name, so the list is shorter than the five branches in the tree:>>> sorted(ll.get_parameter_list("name")), len(ll.Objects) (['A', 'B', 'C'], 5)
- make_single_type()¶
Remove singleton internal nodes by merging them into their descendants.
A singleton is an internal node with exactly one child, as produced by multitype (structured-coalescent) BEAST analyses. Each is spliced out and its branch length added to the child, leaving a strictly branching tree. Requires heights, so call
traverse_tree()first.sort_branches()is called on the way out, so plotting coordinates are already refreshed when this returns.Returns
- None
The tree is modified in place.
Examples
>>> import baltic as bt >>> ll = bt.Tree("divergence") >>> ll.add_node(1) >>> ll.add_node(2) >>> ll.add_leaf(3, "A") >>> ll.curNode = ll.root >>> ll.add_leaf(4, "B") >>> _ = ll.traverse_tree() >>> ll.make_single_type() >>> all(len(node.children) != 1 for node in ll.get_internal()) True
- midpoint_root(fixSingletons=True)¶
Reroot the tree at the midpoint of the longest tip-to-tip path.
The longest path is found by rerooting on every tip in turn and recording the greatest resulting tip height, so the cost grows with the number of tips and the tree is repeatedly rerooted along the way. The tree is modified in place throughout; if this raises part-way, the tree is left rooted wherever the search had reached rather than in its original state. Work on a copy if that matters.
Inherits the restrictions of
reroot(), so it is divergence-trees only.Parameters
- fixSingletonsbool, default=True
If
True, collapse singleton nodes after rerooting. Passed through toreroot().
Returns
TreeThe same tree object, rerooted. The return value is a convenience for chaining, not a copy.
Raises
- AttributeError
If the tree’s
treeTypeis"time", viareroot().
Examples
>>> import baltic as bt >>> ll = bt.make_tree("((A:1.0,B:3.0):1.0,C:1.0);", treeType="divergence") >>> ll.sort_branches() >>> _ = ll.midpoint_root() >>> isinstance(ll.root, bt.node.Node) True
Attribution
Adapted from
Bio.Phylo.BaseTree.Tree.root_at_midpointin Biopython. The original method was implemented by Eric Talevich and its traversal was subsequently optimized by Brandon Invergo. Modified forbaltictree and branch objects.Copyright (C) 2009 Eric Talevich and the Biopython contributors.
The upstream file is distributed, at the recipient’s choice, under the Biopython License Agreement or the BSD 3-Clause License.
Source: https://github.com/biopython/biopython/blob/master/Bio/Phylo/BaseTree.py License: https://github.com/biopython/biopython/blob/master/LICENSE.rst
- plot_aligned_tip_labels(ax, xSpace=0.005, connectingLines=True, **kwargs)¶
Plot tip labels aligned to a common terminal coordinate.
Labels are placed a fixed distance past the furthest tip rather than at each tip, with optional dotted guide lines connecting the two.
Warning
Rectangular and circular layouts are supported. Passing
treeType="unrooted"warns and then raisesUnboundLocalErrorwhile building the connecting lines; passconnectingLines=Falseto use an unrooted layout.Parameters
- axmatplotlib.axes.Axes
Axes on which to draw labels.
- xSpacefloat, default=0.005
Extra horizontal offset, expressed as a fraction of tree height.
0places labels level with the furthest tip,1a full tree height beyond it.- connectingLinesbool, default=True
If
True, draw guide lines between tips and aligned labels.- **kwargsdict, optional
Additional keyword arguments forwarded to
plot_text().xCoordinateFxnandnormaliseHeightare computed here and cannot be overridden – passing either warns and discards it.
Returns
- matplotlib.axes.Axes
The input axes.
Examples
>>> import matplotlib.pyplot as plt >>> import baltic as bt >>> ll = bt.make_tree("((A:1.0,B:1.0):1.0,C:1.5);", treeType="divergence") >>> fig, ax = plt.subplots() >>> ll.plot_aligned_tip_labels(ax, xSpace=0.05) <...Axes...>
- plot_exploded_tree(ax, trait=None, customFxn=None, stem=True, subtreeSortFxn=None, targetFxn=None, xCoordinateFxn=None, colour=None, colourFxn=None, tipPoints=True, originPoint=True, verticalSpace=2, width=None, widthFxn=None, pointSize=None, pointSizeFxn=None, outline=True, outlineSize=None, outlineSizeFxn=None, outlineColour=None, outlineColourFxn=None, padNodes=None, orientation='horizontal', connectionType='baltic', **kwargs)¶
Plot subtrees extracted by trait transitions or custom split rules.
Subtrees are generated with
explode_tree(), which requires exactly one of trait or customFxn and works on deep copies, so this leaves the original tree’s structure alone.Parameters
- axmatplotlib.axes.Axes
Axes on which to draw the exploded tree view.
- traitstr, optional
Trait used to determine subtree boundaries.
- customFxncallable, optional
Custom predicate defining subtree boundaries.
- stembool, optional
If
True, include the stem branch for each extracted subtree.- subtreeSortFxncallable, optional
Function used to order subtrees vertically.
- targetFxncallable, optional
Predicate selecting branches to plot within each subtree.
- xCoordinateFxncallable, optional
X-coordinate function used for each subtree.
- colour, colourFxn, width, widthFxn, pointSize, pointSizeFxn, outline, outlineSize, outlineSizeFxn, outlineColour, outlineColourFxnoptional
Styling controls for branches and optional points.
- tipPointsbool, optional
If
True, mark subtree tips.- originPointbool, optional
If
True, mark each subtree root.- verticalSpacefloat, optional
Space inserted between consecutive subtrees.
- padNodesdict, optional
Additional spacing applied when plotting each subtree.
- orientation{“horizontal”, “vertical”}, optional
Orientation of the exploded view.
- connectionType{“baltic”, “direct”, “elbow”}, optional
Branch connection style used for each subtree.
- **kwargsdict, optional
Additional keyword arguments forwarded to subtree plotting calls.
Returns
- matplotlib.axes.Axes
The input axes.
Raises
- ValueError
If both a scalar and its
*Fxncounterpart are given for any of width, point size, outline size or colour; or, viaexplode_tree(), if both or neither of trait and customFxn is given.
Examples
>>> import matplotlib.pyplot as plt >>> import baltic as bt >>> ll = bt.make_tree("(((A:1.0,B:1.0):1.0,C:1.0):1.0,D:1.0);", treeType="divergence") >>> ll.sort_branches() >>> for branch in ll.Objects: ... branch.traits["group"] = "left" if getattr(branch, "name", "").startswith(("A", "B")) else "right" >>> fig, ax = plt.subplots() >>> ll.plot_exploded_tree(ax, trait="group", colourFxn=lambda k: "steelblue") <...Axes...>
- plot_points(ax, targetFxn=None, xCoordinateFxn=None, yCoordinateFxn=None, pointSize=None, pointSizeFxn=None, colour=None, colourFxn=None, outline=True, outlineSize=None, outlineSizeFxn=None, outlineColour=None, outlineColourFxn=None, padNodes=None, treeType='rectangular', orientation='horizontal', circStart=None, circFrac=None, inwardSpace=None, normaliseHeight=None, recomputeCoordinates=True, **kwargs)¶
Plot markers on selected branches.
This helper complements
plot_tree().Parameters
- axmatplotlib.axes.Axes
Axes on which to draw points.
- targetFxncallable, optional
Predicate selecting branches to mark.
- xCoordinateFxn, yCoordinateFxncallable, optional
Coordinate functions for point placement.
- pointSize, pointSizeFxn, colour, colourFxn, outline, outlineSize, outlineSizeFxn, outlineColour, outlineColourFxnoptional
Marker styling controls.
- padNodesdict, optional
Additional spacing used when computing coordinates.
- treeType{“rectangular”, “circular”, “unrooted”}, optional
Layout in which to place markers.
- orientation{“horizontal”, “vertical”}, optional
Orientation for rectangular layouts.
- circStart, circFrac, inwardSpace, normaliseHeightoptional
Circular-layout controls.
- recomputeCoordinatesbool, default=True
If
True, recompute coordinates before plotting. This reassigns the tree’sx/yattributes, so the call modifies the tree.- **kwargsdict, optional
Additional keyword arguments forwarded to
Axes.scatter.
Returns
- tuple[matplotlib.axes.Axes, dict]
The input axes and a dictionary mapping each plotted branch to its
(x, y)coordinates.
Examples
>>> import matplotlib.pyplot as plt >>> import baltic as bt >>> ll = bt.make_tree("((A:1.0,B:1.0):1.0,C:1.5);", treeType="divergence") >>> fig, ax = plt.subplots() >>> ax, coords = ll.plot_points(ax, targetFxn=lambda k: k.is_leaf(), pointSize=60) >>> sorted((k.name, xy) for k, xy in coords.items()) [('A', (2.0, 2.5)), ('B', (2.0, 1.5)), ('C', (1.5, 0.5))]
- plot_text(ax, targetFxn=None, xCoordinateFxn=None, xSpace=0.005, yCoordinateFxn=None, ySpace=0.0, textContentFxn=None, colour=None, colourFxn=None, treeType='rectangular', orientation='horizontal', padNodes=None, circStart=0.0, circFrac=1.0, inwardSpace=0.0, normaliseHeight=None, cladeEndAttrFxn=None, recomputeCoordinates=True, **kwargs)¶
Plot text labels on branches in rectangular, circular, or unrooted layouts.
This helper is commonly used alongside
plot_tree().Parameters
- axmatplotlib.axes.Axes
Axes on which to draw labels.
- targetFxncallable, optional
Predicate selecting branches to label.
- xCoordinateFxn, yCoordinateFxncallable, optional
Coordinate functions for label placement.
- xSpace, ySpacefloat, optional
Extra spacing offsets applied to the default coordinates.
- textContentFxncallable, optional
Function returning label text for each branch.
- colour, colourFxnoptional
Fixed colour or callable returning a colour for each branch.
- treeType{“rectangular”, “circular”, “unrooted”}, optional
Layout used for plotting labels.
- orientation{“horizontal”, “vertical”}, optional
Orientation for rectangular labels.
- padNodesdict, optional
Additional spacing to apply to selected subtrees.
- circStart, circFrac, inwardSpace, normaliseHeightoptional
Circular-layout controls.
- cladeEndAttrFxncallable, optional
Function returning the far edge of collapsed clades.
- recomputeCoordinatesbool, default=True
If
True, recompute tree coordinates before plotting. This reassigns the tree’sx/yattributes, so the call modifies the tree.- **kwargsdict, optional
Additional keyword arguments forwarded to
Axes.text.
Returns
- matplotlib.axes.Axes
The input axes.
Examples
>>> import matplotlib.pyplot as plt >>> import baltic as bt >>> ll = bt.make_tree("((A:1.0,B:1.0):1.0,C:1.5);", treeType="divergence") >>> fig, ax = plt.subplots() >>> ll.plot_text(ax, targetFxn=lambda k: k.is_leaf()) <...Axes...>
- plot_tree(ax, targetFxn=None, xCoordinateFxn=None, yCoordinateFxn=None, width=None, widthFxn=None, connectionType=None, colour=None, colourFxn=None, orientation=None, padNodes=None, treeType='rectangular', circStart=None, circFrac=None, inwardSpace=None, normaliseHeight=None, precision=None, plotClades=True, cladeColour=None, cladeEndAttrFxn=None, cladeStyle='equal', cladeShape=None, cladeBaseWidth=0.001, recomputeCoordinates=True, autoSort=True, **kwargs)¶
Plot the tree in rectangular, circular, or unrooted form.
Use
plot_text()andplot_points()to layer labels and markers onto the same geometry.Parameters
- axmatplotlib.axes.Axes
Axes on which to draw the tree.
- targetFxncallable, optional
Predicate selecting which branches to draw.
- xCoordinateFxn, yCoordinateFxncallable, optional
Coordinate functions for branch endpoints.
- width, widthFxn, colour, colourFxnoptional
Branch styling controls.
- connectionType{“baltic”, “direct”, “elbow”}, optional
Branch connection style for rooted layouts.
- orientation{“horizontal”, “vertical”}, optional
Orientation for rectangular layouts.
- padNodesdict, optional
Additional spacing applied to selected subtrees.
- treeType{“rectangular”, “circular”, “unrooted”}, optional
Layout to render.
- circStart, circFrac, inwardSpace, normaliseHeight, precisionoptional
Circular- and unrooted-layout controls.
- plotCladesbool, optional
If
True, draw collapsed clades.- cladeColour, cladeEndAttrFxn, cladeStyle, cladeShape, cladeBaseWidthoptional
Styling and geometry options for collapsed clades.
- recomputeCoordinatesbool, default=True
If
True, recompute branch coordinates before plotting.- autoSortbool, default=True
If
True, sort branches before drawing. Note this reorders the tree’s own child lists – see the note below.- **kwargsdict, optional
Additional keyword arguments forwarded to the line collection.
Returns
- matplotlib.axes.Axes
The input axes.
Examples
>>> import matplotlib.pyplot as plt >>> import baltic as bt >>> ll = bt.make_tree("((A:1.0,B:1.0):1.0,C:1.5);", treeType="divergence") >>> fig, ax = plt.subplots() >>> ll.plot_tree(ax, colourFxn=lambda k: "firebrick" if k.is_leaf() else "k") <...Axes...>
Note
Despite being a plotting call, this modifies the tree by default: with
autoSort=Trueit callssort_branches(), which reorders every node’s children, and withrecomputeCoordinates=Trueit reassigns allx/ycoordinates. PassautoSort=Falseto draw a tree in its existing order.
- project_circular_point(x, y, circStart=0.0, circFrac=1.0, inwardSpace=0.0, normaliseHeight=None)¶
Project a tree-space coordinate onto a circular tree layout.
This method applies the same radial normalization and angular projection used by
plot_tree(), making it suitable for placing annotations and other artists on circular tree axes.Parameters
- xfloat
Coordinate along the informative tree axis, usually
heightfor divergence trees orabsoluteTimefor time trees.- yfloat
Coordinate along the non-informative tree axis.
- circStartfloat, optional
Fraction of the circle at which plotting begins.
- circFracfloat, optional
Fraction of the full circle used by the layout.
- inwardSpacefloat, optional
Radial spacing applied before normalization. Negative values use the outward-facing convention of circular tree plots.
- normaliseHeightcallable, optional
Function mapping the informative coordinate to radial distance. By default, coordinates are normalized over the tree’s height or absolute-time range.
Returns
- tuple[float, float]
Cartesian coordinates in the circular tree’s data space.
Raises
- ValueError
If circFrac is not positive; if the tree has no
yspan because coordinates were never assigned; if no branch carries the coordinate attribute implied bytreeType; or if every branch shares one coordinate, leaving no radial span to normalize over.
Examples
Coordinates must already be assigned, by a plotting call or directly:
>>> import baltic as bt >>> ll = bt.make_tree("((A:1.0,B:1.0):1.0,C:1.5);", treeType="divergence") >>> ll._assign_tree_coordinates() >>> tip = ll.get_leaf("A") >>> tuple(round(value, 6) for value in ll.project_circular_point(tip.height, tip.y)) (-0.866025, 0.5)
- reduce_tree(tipsToKeep)¶
Extract the minimal subtree spanning a set of retained tips.
Every ancestor on the path from a retained tip to the root is kept, so root-to-tip distances are preserved exactly. Nodes whose other children were pruned are not collapsed, so the reduced tree generally contains singleton nodes; run
make_single_type()afterwards for a strictly branching result.Requires a traversed tree, since branches are ordered by height; call
traverse_tree()orsort_branches()first or this raisesTypeError. Duplicate entries in tipsToKeep are ignored.Parameters
- tipsToKeeplist[
BranchLike] Leaf-like branches from this tree that should remain in the reduced tree. Must be branch objects from this tree, not tip names.
Raises
- ValueError
If no tips are supplied, an input is not leaf-like, or an input does not belong to this tree.
Returns
TreeDeep-copied reduced tree containing the embedding of the retained tips.
Examples
>>> import baltic as bt >>> ll = bt.make_tree("(((A:1.0,B:1.0):1.0,C:1.0):1.0,D:1.0);", treeType="divergence") >>> ll.sort_branches() >>> reduced = ll.reduce_tree([ll.get_leaf("A"), ll.get_leaf("D")]) >>> sorted(tip.name for tip in reduced.get_external()) ['A', 'D']
Distances survive the reduction, at the cost of leaving singleton nodes behind:
>>> reduced.get_leaf("A").height == ll.get_leaf("A").height True >>> any(len(node.children) == 1 for node in reduced.get_internal()) True
- tipsToKeeplist[
- rename_tips(tipNameMap=None)¶
Rename leaf nodes using a mapping.
Renamed tips are visible through
get_external(). Only true leaves are renamed, so collapsed clades keep their names.Parameters
- tipNameMapdict, optional
Mapping from current tip name to replacement name. Every tip in the tree must appear as a key. If omitted,
self.tipMapis used.
Returns
- None
Tips are renamed in place.
Raises
- ValueError
If tipNameMap is omitted and the tree has no
tipMap.- KeyError
If a tip’s current name is missing from the mapping. Tips are renamed as they are visited, so a partial mapping leaves the tree partly renamed.
Examples
>>> import baltic as bt >>> ll = bt.make_tree("((A:1.0,B:1.0):1.0,C:1.0);", treeType="divergence") >>> ll.rename_tips({"A": "sample_1", "B": "sample_2", "C": "sample_3"}) >>> sorted(tip.name for tip in ll.get_external()) ['sample_1', 'sample_2', 'sample_3']
- reroot(branch=None, branchFrac=0.5, fixSingletons=True)¶
Reroot the tree on a branch or branch midpoint.
Only divergence trees can be rerooted: moving the root would invalidate the calibration of a time tree, so one raises
AttributeErrorinstead. Total tree length is preserved, and this is checked before returning.The tree is modified in place. If the current root carries a non-zero branch length, it is silently reset to
0.0(with a warning logged) since a root stub is a plotting convenience rather than real divergence.Parameters
- branch
BranchLike, optional Branch on which the new root should be placed. If omitted, midpoint rooting is used via
midpoint_root(). Passing the current root logs a warning and returns the tree unchanged.- branchFracfloat, default=0.5
Where along
branchto place the new root, as a fraction of its length measured from the branch’s parent end.0.0puts the root at the top of the branch,1.0at the branch itself.- fixSingletonsbool, default=True
If
True, runmake_single_type()afterwards to splice out the singleton node the old root becomes.
Returns
TreeThe same tree object, rerooted. The return value is a convenience for chaining, not a copy.
Raises
- AttributeError
If the tree’s
treeTypeis"time".- AssertionError
If total tree length changed during rerooting, which would indicate a branch-length bookkeeping error.
Examples
>>> import baltic as bt >>> ll = bt.make_tree("((A:1.0,B:1.0):1.0,C:2.0);", treeType="divergence") >>> ll.sort_branches() >>> branch = ll.get_leaf("C") >>> _ = ll.reroot(branch=branch, branchFrac=0.5) >>> ll.root is not None True
Attribution
Adapted from
Bio.Phylo.BaseTree.Tree.root_with_outgroupin Biopython, originally implemented by Eric Talevich. Modified forbalticbranch objects, parent references, singleton removal, and branch-fraction placement.Copyright (C) 2009 Eric Talevich and the Biopython contributors.
The upstream file is distributed, at the recipient’s choice, under the Biopython License Agreement or the BSD 3-Clause License.
Source: https://github.com/biopython/biopython/blob/master/Bio/Phylo/BaseTree.py License: https://github.com/biopython/biopython/blob/master/LICENSE.rst
- branch
- rescale(factor)¶
Multiply all branch lengths by a constant factor.
Heights are recomputed immediately via
traverse_tree().Parameters
- factorfloat
Scaling factor applied to every branch length.
Returns
- None
Branches are modified in place.
Examples
>>> import baltic as bt >>> ll = bt.make_tree("(A:1.0,B:2.0);", treeType="divergence") >>> ll.rescale(2.0) >>> sorted(ll.get_parameter_list("length")) [0.0, 2.0, 4.0]
- restore_all_collapsed_subtrees()¶
Restore every previously collapsed clade back into its original subtree.
This reverses
collapse_subtree_to_clade(). All clades are restored, and the loop repeats until none remain, so clades nested inside a restored subtree are expanded too. AnytipMapentries created for the clades are removed, and the tree is re-traversed before returning.Returns
- None
The tree is modified in place.
Examples
>>> import baltic as bt >>> ll = bt.make_tree("(((A:1.0,B:1.0):1.0,C:1.0):1.0,D:1.0);", treeType="divergence") >>> _ = ll.traverse_tree() >>> _ = ll.collapse_subtree_to_clade(ll.find_MRCA("A", "B"), "AB clade") >>> ll.restore_all_collapsed_subtrees() >>> any(branch.name == "AB clade" for branch in ll.get_external(onlyLeaves=False)) False
- root_by_regression(stat: str = 'r^2', forcePositive: bool = True, nJobs: int | None = None, baseRefineIters: int = 20, refineItersPerTip: int = 10, maxRefineIters: int = 400, returnRefinedDates: bool = True)¶
Reroot the tree by continuous root-to-tip regression.
For each represented branch, the optimal root position is solved analytically rather than sampled on a grid. Tips with date ranges are alternately projected into their ranges and refitted until convergence.
The tree is rerooted in place, and tips whose dates were uncertain have their
absoluteTimeoverwritten with the inferred value. Requires a divergence tree whose tips carry dates, as produced by loading withabsoluteTime=True.Progress is written to standard output through a dedicated handler on the
baltic.tree.root_by_regressionlogger, independent of the root logger’s configuration.Parameters
- stat{“r^2”, “correlation”, “sum of squares”}, default=”r^2”
Regression statistic to optimize.
"sum of squares"is minimized; the other statistics are maximized.- forcePositivebool, default=True
If true, exclude root positions with a negative regression slope.
- nJobsint or None, optional
Number of worker threads used to evaluate branches.
Noneor a non-positive value uses the available CPU count.- baseRefineItersint, default=20
Base number of ranged-date refinement iterations.
- refineItersPerTipint, default=10
Additional ranged-date iterations allowed per uncertain tip.
- maxRefineItersint, default=400
Upper limit on ranged-date refinement iterations.
- returnRefinedDatesbool, default=True
If true, return
(tree, refined_dates). Otherwise return the tree alone. Note the default is the tuple.
Returns
Treeor tupleThe same tree object, rerooted, and – when returnRefinedDates is true – a dict of inferred dates for the tips that had ranges. The dict is empty when every tip date was already exact. A tree with fewer than three tips is returned unchanged, with a warning logged.
Raises
- ValueError
If stat is not one of the three supported statistics, if the tree is not a divergence tree, or if the refinement iteration limits are not positive.
Warns
- RuntimeWarning
If ranged-date refinement hits its iteration limit without converging.
Notes
Exact dates require one closed-form solve per branch. Date ranges use iterative projection. The previous implementation remains available as
root_by_regression_legacy(), which differs in several respects – see its docstring before switching.Examples
Progress lines go to standard output, so this example captures them to keep the doctest readable.
>>> import io >>> import baltic as bt >>> from contextlib import redirect_stdout >>> handle = io.StringIO("((A|2020-01-01:0.1,B|2020-06-01:0.2):0.3,C|2021-01-01:0.4);") >>> ll = bt.io.load_newick(handle, treeType="divergence", absoluteTime=True, variableDate=True) >>> with redirect_stdout(io.StringIO()): ... rooted, inferred = ll.root_by_regression(nJobs=1) >>> rooted is ll True >>> inferred {}
- root_by_regression_legacy(stat: str = 'r^2', forcePositive: bool = True, nJobs: int | None = None, baseRefineIters: int = 20, refineItersPerTip: int = 10, maxRefineIters: int = 400, returnRefinedDates: bool = True)¶
Reroot using the preserved legacy root-to-tip regression search.
Superseded by
root_by_regression(), which solves each branch in closed form instead of searching, runs in threads, and validates its arguments more consistently. Prefer that method for new code; this one is kept so earlier results can be reproduced.Every non-root branch is evaluated as a candidate root, each in a worker process. The tree is rerooted in place and uncertain tips have their
absoluteTimeoverwritten with the best-fitting inferred value.Warning
Because the work is dispatched with
concurrent.futures.ProcessPoolExecutor, on platforms that spawn rather than fork (macOS and Windows) the calling code must be guarded byif __name__ == "__main__":. Without it the pool dies withBrokenProcessPool, and it generally cannot be called straight from a notebook or an interactive session. This applies even withnJobs=1.root_by_regression()uses threads and has no such restriction.Parameters
- stat{“r^2”, “correlation”, “sum of squares”}, default=”r^2”
Which regression stat to optimize.
- forcePositivebool, default=True
Forbid date inference to allow negative branch lengths.
- nJobsint or None, optional
Number of parallel worker processes to use for the search.
Noneor a non-positive value uses the available CPU count.- baseRefineItersint, default=20
Minimum number of Monte Carlo iterations.
- refineItersPerTipint, default=10
Additional Monte Carlo iterations per tip.
- maxRefineItersint, default=400
Maximum number of Monte Carlo iterations.
- returnRefinedDatesbool, default=True
True if the best-fitting inferred dates should be returned.
Returns
Treeor tupleThe same tree object, rerooted, and – when returnRefinedDates is true – a dict mapping tip names to the best-fitting dates. A tree with no candidate branches is returned bare, without the dict, regardless of returnRefinedDates.
Raises
- AssertionError
If stat is not one of the three supported statistics, or if no tip carries an
absoluteTimeRange(load the tree withabsoluteTime=True). Note thatroot_by_regression()raisesValueErrorfor the equivalent argument error.- ValueError
If only some tips carry an
absoluteTimeRange; the range of every tip is differenced, so a mix of set and unset ranges fails.
Examples
>>> import io >>> import baltic as bt >>> handle = io.StringIO("((A|2020-01-01:0.1,B|2020-06-01:0.2):0.3,C|2021-01-01:0.4);") >>> ll = bt.io.load_newick(handle, treeType="divergence", absoluteTime=True, variableDate=True) >>> rooted, inferred_dates = ll.root_by_regression_legacy(nJobs=1) >>> isinstance(inferred_dates, dict) True
The final two steps are skipped rather than run: the process pool requires a
__main__guard, which a doctest cannot provide. Run them from a script shaped like this instead:if __name__ == "__main__": rooted, inferred_dates = ll.root_by_regression_legacy(nJobs=1)
- set_absolute_time(mostRecentSamplingDate, justLeaves=False)¶
Assign absolute times to branches from their heights.
Each branch’s
absoluteTimebecomesmostRecentSamplingDate - treeHeight + height, so heights must already be set: calltraverse_tree()first, or this raisesTypeErroron the unset heights. The tree’smostRecentattribute is set to the latest assigned date. Branches are modified in place.These dates are later consumed by
get_all_tip_TMRCAs()andcount_lineages_at_time().Parameters
- mostRecentSamplingDatefloat
Absolute date corresponding to the most recent sampled tip, usually a decimal year from
baltic.bt_utils.calendar_to_decimal_date().- justLeavesbool, default=False
If
True, only assign absolute times to leaves, leaving internal nodes with whateverabsoluteTimethey already had.
Returns
- None
Branches are modified in place.
Examples
>>> import baltic as bt >>> ll = bt.make_tree("((A:1.0,B:2.0):1.0,C:3.0);", treeType="time") >>> _ = ll.traverse_tree() >>> ll.set_absolute_time(2020.0) >>> round(ll.get_leaf("C").absoluteTime, 1) 2020.0 >>> round(ll.root.absoluteTime, 1), ll.mostRecent (2017.0, 2020.0)
- sort_branches(descending=True, sortFxn=None, operationFxn=None)¶
Reorder child lists for internal nodes and refresh plotting coordinates.
Coordinate updates are handled by
_assign_tree_coordinates().Parameters
- descendingbool, default=True
Controls the direction of the default sort. Ignored when sortFxn or operationFxn is given.
- sortFxncallable, optional
Key function used to sort each node’s children. By default children are ordered by type, then descendant count, then branch length.
- operationFxncallable, optional
Callable that receives and returns each node’s child list directly, for reorderings a key function cannot express (such as reversing).
Returns
- None
Child lists and plotting coordinates are updated in place.
Raises
- Exception
If both sortFxn and operationFxn are given.
Examples
>>> import baltic as bt >>> ll = bt.make_tree("((A:1.0,B:1.0):1.0,C:1.0);", treeType="divergence") >>> ll.sort_branches(descending=False, operationFxn=lambda kids: list(reversed(kids))) >>> ll.root.children[0].is_leaf() True
- subtree(startingNode=None, traverseCondition=None, stem=True)¶
Generate a new subtree starting from a given root node according to a certain condition.
Parameters
- startingNode
BranchLike, optional The node from which the new subtree will descend.
By default, the root of the tree is used.
- traverseConditioncallable, optional
Function defining the conditional inclusion descendant nodes.
By convention, the function should take a single
BranchLikeobject as input and return a boolean value indicating whether to traverse that branch. For example, to include all branches with length greater than0.5:traverseCondition = lambda k: k.length > 0.5.By default, all branches are included. Supplying one also prunes children that were not traversed and runs
fix_hanging_nodes()on the result.- stembool, default=True
Include the stem branch leading into startingNode. When
False, the new root’slengthis set to0.0. Has no effect beyond that when startingNode is already the root.
Returns
Treeor NoneA new tree whose branches are deep copies, so editing it never affects the original.
Noneis returned, with an error logged, when the traversal collects no leaves – which is what an over-restrictive traverseCondition produces.
Examples
>>> import baltic as bt >>> ll = bt.make_tree("(((A:1.0,B:1.0):1.0,C:1.0):1.0,D:1.0);", treeType="divergence") >>> _ = ll.traverse_tree() >>> node = ll.find_MRCA("A", "B", "C") >>> sub = ll.subtree(startingNode=node, stem=False) >>> sorted(tip.name for tip in sub.get_external()) ['A', 'B', 'C']
The copy is independent of the tree it came from:
>>> sub.get_leaf("A").name = "renamed" >>> sorted(tip.name for tip in ll.get_external()) ['A', 'B', 'C', 'D']
- startingNode
- to_auspice_json(traits=None, mostRecentDate=None)¶
Convert the tree to an Auspice v2 JSON structure.
Branch dictionaries are constructed with
baltic.bt_utils.branch_to_json().Only traits Auspice can colour by are carried into the metadata: those ending
_95%_HPD(continuous),.set.prob(categorical) or_median, plusposterior. Other traits are dropped from the colourings.Note
This modifies the tree. Every internal node gains a
node_idxtrait holding its Auspice node name, andtraverse_tree()is run to establish the ordering.Parameters
- traitsiterable, optional
Trait names to export. If omitted, every trait found on the tree is considered. When passing an explicit list, include the
_95%_HPDand.set.probcompanions of any trait whose uncertainty should survive.- mostRecentDatefloat, optional
Most recent sampling date for time trees. Must be a
float– anintsuch as2024is rejected. For a time tree it defaults to the tree’smostRecent; supplying it for a divergence tree logs a warning.
Returns
- dict
Auspice JSON payload with
version,metaandtreekeys.
Raises
- AssertionError
If mostRecentDate is not a float, or if it was omitted for a time tree whose
mostRecenthas not been set byset_absolute_time().
Examples
>>> import baltic as bt >>> ll = bt.make_tree("((A:1.0,B:1.0):1.0,C:1.0);", treeType="time") >>> ll.sort_branches() >>> ll.set_absolute_time(2024.0) >>> data = ll.to_auspice_json(mostRecentDate=2024.0) >>> sorted(data.keys()) ['meta', 'tree', 'version']
- to_string(curNode=None, traits=None, nexus=False, stringFragment=None, traverseCondition=None, rename=None, quoteCharacter="'", json=False)¶
Serialize the tree to Newick- or NEXUS-like text.
Annotations are written as BEAST-style
[&key=value]comments. Tip names are always quoted, and branch lengths are written with fifteen decimal places.Parameters
- curNode
BranchLike, optional Current node during recursive serialization. Callers normally leave this unset; see the note on the return value below.
- traitsiterable, optional
Trait names to include in branch comments. By default every trait found anywhere on the tree is exported; pass an empty list for a plain Newick string.
- nexusbool, default=False
If
True, wrap the result in a simple NEXUS tree block.- stringFragmentlist, optional
Internal accumulator used during recursion.
- traverseConditioncallable, optional
Predicate selecting which descendants to serialize. By default every descendant is written.
- renamedict, optional
Optional mapping from existing tip names to exported names. Must contain every tip in the tree.
- quoteCharacterstr, default=”’”
Quote character used around tip names.
- jsonbool, default=False
Compatibility flag. Its only effect is to forbid combining it with nexus.
Returns
- str or None
Serialized tree string. The string is assembled and returned only when recursion reaches the tree’s root, so calling this with an explicit curNode other than the root returns
None. Usesubtree()first to serialize part of a tree.
Raises
- AssertionError
If rename is not a dict or omits a tip name; if traverseCondition leaves a node with no traversable children; or if nexus and json are both true.
Examples
>>> import baltic as bt >>> ll = bt.make_tree("((A:1.0,B:1.0):1.0,C:1.0);", treeType="divergence") >>> ll.to_string().endswith(";") True >>> ll.to_string(traits=[]) "(('A':1.000000000000000,'B':1.000000000000000):1.000000000000000,'C':1.000000000000000):0.000000000000000;" >>> ll.to_string(nexus=True).startswith("#NEXUS") True
- curNode
- traverse_tree(curNode=None, includeCondition=None, traverseCondition=None, collect=None)¶
Traverse the tree recursively while updating heights and descendant sets.
This is the method that recomputes tree state, so call it after any structural edit. As it walks it sets each branch’s
height, fills each node’sleavesset with the names of its descendant tips, sets each node’schildHeight, and updates the tree’streeHeight. When started from the root with neither condition given, the existingheight,leavesandchildHeightvalues are cleared first; supplying either condition skips that reset, so a filtered traversal refines existing state rather than rebuilding it.Parameters
- curNode
BranchLike, optional Node at which to start traversal. By default the root is used.
- includeConditioncallable, optional
Predicate deciding whether a visited branch should be collected. By default only leaf-like branches are collected, so a bare
traverse_tree()returns the tips rather than every branch.- traverseConditioncallable, optional
Predicate deciding whether a child branch should be traversed. By default every child is traversed.
- collectlist, optional
Existing collection list to append to. Used by the recursive calls; callers normally leave this unset.
Returns
- list[
BranchLike] Branches satisfying
includeCondition, in traversal order.
Raises
- AttributeError
If a node without children is encountered. Repair such a tree with
fix_hanging_nodes().
Examples
>>> import baltic as bt >>> ll = bt.make_tree("((A:1.0,B:1.0):1.0,C:1.0);", treeType="divergence") >>> leaves = ll.traverse_tree(includeCondition=lambda k: k.is_leaf()) >>> [tip.name for tip in leaves] ['A', 'B', 'C']
Heights and descendant sets are a side effect of the walk:
>>> ll.root.childHeight, ll.treeHeight (2.0, 2.0) >>> sorted(ll.root.leaves) ['A', 'B', 'C']
- curNode
- treeStats()¶
Print a short textual summary of tree statistics.
This is a print-oriented wrapper around
_calculate_tree_stats(); usetreeStatsDict()to get the same numbers as data. Because the statistics are recomputed, this callstraverse_tree()and therefore refreshes branch heights as a side effect.Returns
- None
The summary is written to standard output.
Examples
>>> import baltic as bt >>> import io >>> from contextlib import redirect_stdout >>> ll = bt.make_tree("(A:1.0,B:2.0);", treeType="divergence") >>> buf = io.StringIO() >>> with redirect_stdout(buf): ... ll.treeStats() >>> "Tree height" in buf.getvalue() True
- treeStatsDict()¶
Return summary statistics describing the current tree.
This exposes the dictionary returned by
_calculate_tree_stats(), the same numberstreeStats()prints. Because the statistics are recomputed, this callstraverse_tree()and therefore refreshes branch heights as a side effect.Returns
- dict
Dictionary of tree height and length, the
strictlyBifurcating,multitypeTree,singletonTreeandhasTraitstopology flags, and counts of objects, nodes and leaves.
Examples
>>> import baltic as bt >>> ll = bt.make_tree("(A:1.0,B:2.0);", treeType="divergence") >>> sorted(ll.treeStatsDict()) ['hasTraits', 'multitypeTree', 'numLeaves', 'numNodes', 'numObjects', 'singletonTree', 'strictlyBifurcating', 'treeHeight', 'treeLength']