Skip to content

Commit eda66fa

Browse files
authored
Merge pull request #786 from hyanwong/interval-tuple
Use namedtuple for interval
2 parents 29c2853 + 0b0e262 commit eda66fa

File tree

2 files changed

+24
-13
lines changed

2 files changed

+24
-13
lines changed

python/tests/test_highlevel.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -645,8 +645,8 @@ def verify_edge_diffs(self, ts):
645645
children[edge.parent].add(edge.child)
646646
while tree.interval[1] <= left:
647647
tree = next(trees)
648-
self.assertTrue(left >= tree.interval[0])
649-
self.assertTrue(right <= tree.interval[1])
648+
self.assertTrue(left >= tree.interval.left)
649+
self.assertTrue(right <= tree.interval.right)
650650
for u in tree.nodes():
651651
if tree.is_internal(u):
652652
self.assertIn(u, children)
@@ -2362,6 +2362,14 @@ def test_seek(self):
23622362
for bad_position in [-1, L, L + 1, -L]:
23632363
self.assertRaises(ValueError, tree.seek, bad_position)
23642364

2365+
def test_interval(self):
2366+
ts = msprime.simulate(10)
2367+
tree = ts.first()
2368+
self.assertEqual(tree.interval[0], 0)
2369+
self.assertEqual(tree.interval.left, 0)
2370+
self.assertEqual(tree.interval[1], 1)
2371+
self.assertEqual(tree.interval.right, 1)
2372+
23652373
def verify_empty_tree(self, tree):
23662374
ts = tree.tree_sequence
23672375
self.assertEqual(tree.index, -1)

python/tskit/trees.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
"CoalescenceRecord", ["left", "right", "node", "children", "time", "population"]
5959
)
6060

61+
Interval = collections.namedtuple("Interval", ["left", "right"])
62+
6163

6264
# TODO this interface is rubbish. Should have much better printing options.
6365
# TODO we should be use __slots__ here probably.
@@ -810,8 +812,8 @@ def seek(self, position):
810812
"""
811813
Sets the state to represent the tree that covers the specified
812814
position in the parent tree sequence. After a successful return
813-
of this method we have ``tree.interval[0]`` <= ``position``
814-
< ``tree.interval[1]``.
815+
of this method we have ``tree.interval.left`` <= ``position``
816+
< ``tree.interval.right``.
815817
816818
:param float position: The position along the sequence length to
817819
seek to.
@@ -824,7 +826,7 @@ def seek(self, position):
824826
# No point in complicating the current implementation by trying
825827
# to seek from the correct direction.
826828
self.first()
827-
while self.interval[1] <= position:
829+
while self.interval.right <= position:
828830
self.next()
829831

830832
def rank(self):
@@ -1336,12 +1338,13 @@ def interval(self):
13361338
tree therefore applies to all genomic locations :math:`x` such that
13371339
:math:`l \\leq x < r`.
13381340
1339-
:return: A tuple (l, r) representing the left-most (inclusive)
1341+
:return: A named tuple (l, r) representing the left-most (inclusive)
13401342
and right-most (exclusive) coordinates of the genomic region
1341-
covered by this tree.
1343+
covered by this tree. The coordinates can be accessed by index
1344+
(``0`` or ``1``) or equivalently by name (``.left`` or ``.right``)
13421345
:rtype: tuple
13431346
"""
1344-
return self._ll_tree.get_left(), self._ll_tree.get_right()
1347+
return Interval(self._ll_tree.get_left(), self._ll_tree.get_right())
13451348

13461349
def get_length(self):
13471350
# Deprecated alias for self.span
@@ -3493,7 +3496,7 @@ def edge_diffs(self):
34933496
for interval, edge_tuples_out, edge_tuples_in in iterator:
34943497
edges_out = [Edge(*(e + (metadata_decoder,))) for e in edge_tuples_out]
34953498
edges_in = [Edge(*(e + (metadata_decoder,))) for e in edge_tuples_in]
3496-
yield interval, edges_out, edges_in
3499+
yield Interval(*interval), edges_out, edges_in
34973500

34983501
def sites(self):
34993502
"""
@@ -3554,7 +3557,7 @@ def breakpoints(self, as_array=False):
35543557
Returns the breakpoints along the chromosome, including the two extreme points
35553558
0 and L. This is equivalent to
35563559
3557-
>>> iter([0] + [t.interval[1] for t in self.trees()])
3560+
>>> iter([0] + [t.interval.right for t in self.trees()])
35583561
35593562
By default we return an iterator over the breakpoints as Python float objects;
35603563
if ``as_array`` is True we return them as a numpy array.
@@ -3576,7 +3579,7 @@ def breakpoints(self, as_array=False):
35763579
def at(self, position):
35773580
"""
35783581
Returns the tree covering the specified genomic location. The returned tree
3579-
will have ``tree.interval[0]`` <= ``position`` < ``tree.interval[1]``.
3582+
will have ``tree.interval.left`` <= ``position`` < ``tree.interval.right``.
35803583
See also :meth:`Tree.seek`.
35813584
35823585
:return: A new instance of :class:`Tree` positioned to cover the specified
@@ -4383,8 +4386,8 @@ def to_nexus(self, precision=14):
43834386

43844387
s += "BEGIN TREES;\n"
43854388
for tree in self.trees():
4386-
start_interval = "{0:.{1}f}".format(tree.interval[0], precision)
4387-
end_interval = "{0:.{1}f}".format(tree.interval[1], precision)
4389+
start_interval = "{0:.{1}f}".format(tree.interval.left, precision)
4390+
end_interval = "{0:.{1}f}".format(tree.interval.right, precision)
43884391
newick = tree.newick(precision, node_labels=node_labels)
43894392
s += f"\tTREE tree{start_interval}_{end_interval} = {newick}\n"
43904393

0 commit comments

Comments
 (0)