Preserve root branch length in Phylo.TreeMixin.prune

This commit is contained in:
Michael M
2024-01-22 20:12:06 -06:00
committed by Peter Cock
parent 1605da01a7
commit d5459bb627
5 changed files with 39 additions and 17 deletions

View File

@ -660,8 +660,8 @@ class TreeMixin:
def prune(self, target=None, **kwargs):
"""Prunes a terminal clade from the tree.
If taxon is from a bifurcation, the connecting node will be collapsed
and its branch length added to remaining terminal node. This might be no
If the taxon is from a bifurcation, the connecting node will be collapsed
and its branch length added to remaining terminal node. This might no
longer be a meaningful value.
:returns: parent clade of the pruned target
@ -679,17 +679,14 @@ class TreeMixin:
parent.clades.remove(path[-1])
if len(parent) == 1:
# We deleted a branch from a bifurcation
if parent == self.root:
# If we're at the root, move the root upwards
# NB: This loses the length of the original branch
newroot = parent.clades[0]
newroot.branch_length = None
parent = self.root = newroot
else:
# If we're not at the root, collapse this parent
child = parent.clades[0]
if child.branch_length is not None:
child.branch_length += parent.branch_length or 0.0
if len(path) == 1:
# If we're at the root, move the root upwards
parent = self.root = child
else:
# If we're not at the root, collapse this parent
if len(path) < 3:
grandparent = self.root
else:

View File

@ -352,7 +352,7 @@ function:
>>> Phylo.write(tree1, "tree1.nwk", "newick")
1
>>> Phylo.write(trees, "other_trees.xml", "phyloxml") # write the remaining trees
12
13
Convert files between any of the supported formats with the ``convert``
function:
@ -364,7 +364,7 @@ function:
>>> Phylo.convert("tree1.nwk", "newick", "tree1.xml", "nexml")
1
>>> Phylo.convert("other_trees.xml", "phyloxml", "other_trees.nex", "nexus")
12
13
To use strings as input or output instead of actual files, use
``StringIO`` as you would with SeqIO and AlignIO:
@ -681,7 +681,7 @@ tree intact, make a complete copy of the tree first, using Pythons
sort clades deepest-to-shallowest.
``prune``
Prunes a terminal clade from the tree. If taxon is from a
Prunes a terminal clade from the tree. If the taxon is from a
bifurcation, the connecting node will be collapsed and its branch
length added to remaining terminal node. This might no longer be a
meaningful value.

View File

@ -408,6 +408,26 @@
</clade>
</clade>
</phylogeny>
<phylogeny rooted="true">
<clade>
<branch_length>0.04</branch_length>
<clade>
<branch_length>0.06</branch_length>
<clade>
<name>A</name>
<branch_length>0.102</branch_length>
</clade>
<clade>
<name>B</name>
<branch_length>0.23</branch_length>
</clade>
</clade>
<clade>
<name>C</name>
<branch_length>0.4</branch_length>
</clade>
</clade>
</phylogeny>
<align:alignment xmlns:align="http://example.org/align">
<seq name="A">acgtcgcggcccgtggaagtcctctcct</seq>
<seq name="B">aggtcgcggcctgtggaagtcctctcct</seq>

View File

@ -150,7 +150,7 @@ class IOTests(unittest.TestCase):
trees = Phylo.parse("PhyloXML/phyloxml_examples.xml", "phyloxml")
with tempfile.NamedTemporaryFile(mode="w") as out_handle:
count = Phylo.write(trees, out_handle, "phyloxml")
self.assertEqual(13, count)
self.assertEqual(14, count)
def test_convert_phyloxml_filename(self):
"""Write phyloxml to a given filename."""
@ -162,7 +162,7 @@ class IOTests(unittest.TestCase):
count = Phylo.write(trees, tmp_filename, "phyloxml")
finally:
os.remove(tmp_filename)
self.assertEqual(13, count)
self.assertEqual(14, count)
def test_int_labels(self):
"""Read newick formatted tree with numeric labels."""
@ -471,6 +471,7 @@ class MixinTests(unittest.TestCase):
tree = self.phylogenies[1]
parent = tree.prune(name="C")
self.assertEqual(parent, tree.root)
self.assertEqual(tree.root.branch_length, 0.06)
self.assertEqual(len(parent.clades), 2)
for clade, name, blen in zip(parent, "AB", (0.102, 0.23)):
self.assertTrue(clade.is_terminal())
@ -478,6 +479,10 @@ class MixinTests(unittest.TestCase):
self.assertAlmostEqual(clade.branch_length, blen)
self.assertEqual(len(tree.get_terminals()), 2)
self.assertEqual(len(tree.get_nonterminals()), 1)
# Taxon just below root
tree = self.phylogenies[13]
parent = tree.prune(name="C")
self.assertEqual(tree.root.branch_length, 0.1)
def test_split(self):
"""TreeMixin: split() method."""

View File

@ -96,13 +96,13 @@ class ParseTests(unittest.TestCase):
test_read_apaf = _test_read_factory(EX_APAF, (1, 0))
test_read_bcl2 = _test_read_factory(EX_BCL2, (1, 0))
test_read_made = _test_read_factory(EX_MADE, (6, 0))
test_read_phylo = _test_read_factory(EX_PHYLO, (13, 1))
test_read_phylo = _test_read_factory(EX_PHYLO, (14, 1))
test_read_dollo = _test_read_factory(EX_DOLLO, (1, 0))
test_parse_apaf = _test_parse_factory(EX_APAF, 1)
test_parse_bcl2 = _test_parse_factory(EX_BCL2, 1)
test_parse_made = _test_parse_factory(EX_MADE, 6)
test_parse_phylo = _test_parse_factory(EX_PHYLO, 13)
test_parse_phylo = _test_parse_factory(EX_PHYLO, 14)
test_parse_dollo = _test_parse_factory(EX_DOLLO, 1)
# lvl-2 clades, sub-clade counts, lvl-3 clades