Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat : add reciprocal monophyly function to Tree class #2974

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions python/tskit/trees.py
Original file line number Diff line number Diff line change
Expand Up @@ -1492,6 +1492,41 @@
"""
return bool(self._ll_tree.is_descendant(u, v))

def is_recip_monophyletic(self, pop_groups = []):
"""
Returns True if the tree is reciprocally monophyletic, as defined Standford's
definition : https://xtrees.stanford.edu/reciprocal-monophyly. This function
relies on the following logic : if each population's MRCA's children are the
same as the population's leaves, all populations are reciprocally monophyletic.
The :math:`pop_groups` parameter allows population groupings, for cases where
two separate populations in the tree file must be analysed as a single one.

:param list[list(int)] pop_groups: two-dimensional list of population groupings
:return: True if :math:`self` is a reciprocally monophyletic
:rtype: bool
"""
import pandas as pd

Check warning on line 1508 in python/tskit/trees.py

View check run for this annotation

Codecov / codecov/patch

python/tskit/trees.py#L1508

Added line #L1508 was not covered by tests

leaves_by_pop = pd.DataFrame(columns=['nodes']) # This will be the list of leaves per population

Check warning on line 1510 in python/tskit/trees.py

View check run for this annotation

Codecov / codecov/patch

python/tskit/trees.py#L1510

Added line #L1510 was not covered by tests
for root in self.roots :
for leaf in self.leaves() :
current_pop = self.get_population(leaf)

Check warning on line 1513 in python/tskit/trees.py

View check run for this annotation

Codecov / codecov/patch

python/tskit/trees.py#L1513

Added line #L1513 was not covered by tests
if current_pop not in leaves_by_pop.index :
leaves_by_pop.loc[current_pop] = set() # Create a new entry for the population

Check warning on line 1515 in python/tskit/trees.py

View check run for this annotation

Codecov / codecov/patch

python/tskit/trees.py#L1515

Added line #L1515 was not covered by tests
# Set type is important here because it is unordered, so when comparing the list of nodes, order isn't important
leaves_by_pop.loc[current_pop, 'nodes'].add(leaf)

Check warning on line 1517 in python/tskit/trees.py

View check run for this annotation

Codecov / codecov/patch

python/tskit/trees.py#L1517

Added line #L1517 was not covered by tests

# Allows for population grouping if necessary
for pop_group in pop_groups :
leaves_by_pop.loc[pop_group[0], 'nodes'] = leaves_by_pop.loc[pop_group[0], 'nodes'].union(leaves_by_pop.loc[pop_group[1], 'nodes'])
leaves_by_pop.pop(pop_group[1], inplace=True)

Check warning on line 1522 in python/tskit/trees.py

View check run for this annotation

Codecov / codecov/patch

python/tskit/trees.py#L1521-L1522

Added lines #L1521 - L1522 were not covered by tests

leaves_by_pop['mrca'] = leaves_by_pop['nodes'].apply(lambda x: self.mrca(*x)) # Identify the MRCA of the population's leaves
leaves_by_pop['toplevelChildren'] = leaves_by_pop['mrca'].apply(lambda x: set(self.leaves(x))) # Identify the MRCA's children

# If each population's MRCA's children are the same as the population's leaves, all populations are reciprocally monophyletic
return (leaves_by_pop['nodes'] == leaves_by_pop['toplevelChildren']).all()

Check warning on line 1528 in python/tskit/trees.py

View check run for this annotation

Codecov / codecov/patch

python/tskit/trees.py#L1528

Added line #L1528 was not covered by tests

@property
def num_nodes(self):
"""
Expand Down
Loading