Range Collections

While the Range class may be the engine of Ranger, it really gets its traction from the various classes in the Ranger.src.Collections module. These include RangeSet, RangeMap, and RangeBucketMap. These allow the user to work with multiple ranges along with mappings of Ranges to objects.

Using the RangeSet

The Ranger.src.Collections.RangeSet.RangeSet class is a collection of unique, disjoint Ranges of the same type. When a new Range is added to an existing RangeSet, it is checked for a connection to any existing Ranges already in the RangeSet. If it is not connected, the new Range is added on its own. If it is connected, the new Range coalesces with all connected Ranges. This is shown below with integers

>>> from Ranger import RangeSet
>>> from Ranger import Range
>>> intRangeSet = RangeSet()
>>> intRangeSet.add(Range.closed(1,5))
>>> intRangeSet
RangeSet([1 , 5])
>>> intRangeSet.add(Range.closedOpen(8,10))
>>> intRangeSet
RangeSet([1 , 5], [8 , 10))
>>> intRangeSet.add(Range.openClosed(5,7))
>>> intRangeSet
RangeSet([1 , 7], [8 , 10))
>>> intRangeSet.add(Range.closed(5,9))
>>> intRangeSet
RangeSet([1 , 10))
>>> intRangeSet.add(Range.atLeast(6))
>>> intRangeSet
RangeSet([1 , ))

The RangeSet then has multiple methods for performing set operations or determining whether any overlap exists with query Ranges. Continuing where we left off...

>>> intRangeSet.contains(Range.closed(-1,4))
False
>>> intRangeSet.overlaps(Range.closed(-1,4))
True
>>> otherRangeSet = RangeSet()
>>> otherRangeSet.add(Range.closed(5,10))
>>> otherRangeSet.add(Range.closed(12,15))
>>> otherRangeSet
RangeSet([5 , 10], [12 , 15])
>>> intRangeSet.difference(otherRangeSet)
RangeSet([1 , 5), (10 , 12), (15 , ))])
>>> otherRangeSet2 = RangeSet()
>>> otherRangeSet2.add(Range.closed(-1,6))
>>> otherRangeSet2
RangeSet([-1 , 6])
>>> otherRangeSet2.intersection(otherRangeSet)
RangeSet([5 , 6])
>>> otherRangeSet2.union(otherRangeSet)
RangeSet([-1 , 10], [12 , 15])

Using the RangeMap

The Ranger.src.Collections.RangeMap class acts like a traditional hashmap, using disjoint Range objects as keys that can map to single objects. One important thing to note when using a RangeMap is that a given section’s value will be replaced if a new key/value pair is added over that position.

>>> from Ranger import RangeMap
>>> from Ranger import Range
>>> theMap = RangeMap()
>>> theMap[Range.closed(2,5)] = 'a'
>>> theMap
{[2 , 5] : a}
>>> theMap[Range.closed(8,10)] = 'b'
>>> theMap
{[2 , 5] : a, [8 , 10] : b}
>>> theMap[Range.closed(0,3)] = 'c'
>>> theMap
{[0 , 3] : c, (3 , 5] : a, [8 , 10] : b})}
>>> theMap[Range.closed(4,4)] = 'b'
>>> theMap
{[0 , 3] : c, (3 , 4) : a, [4 , 4] : b, (4 , 5] : a, [8 , 10] : b})}
>>> theMap[3]
set(['c'])
>>> theMap[4]
set(['b'])
>>> theMap[Range.closed(4,20)]
set(['a', 'b'])
>>> theMap[Range.closed(4,20)]
set(['a', 'b'])
>>> del theMap[Range.closed(4,20)]
>>> theMap
{[0 , 3] : c, (3 , 4) : a}

Using the RangeBucketMap

The RangeMap is all well and good if you want sections in your Range domain to map only to a single item. For instance, it could be used to form the basis of a calendar that only supports a single event for any instance in time. However, oftentimes more than one mapping is desired for any particular Range. For instance, a particular chromosomal region could map to mutiple mRNAs, or a calendar could keep track of what multiple users are doing at any given time. The RangeBucketMap accomplishes this feat.

The Ranger.src.Collections.RangeBucketMap class maps disjoint Ranges to one or more hashable objects. When a new key/value pair is added to a particular Range, that value is kept along with any pre-existing values in the Range.

>>> from Ranger import Range
>>> from Ranger import RangeBucketMap
>>> buckets = RangeBucketMap()
>>> buckets[Range.closed(3,5)] = 'a'
>>> buckets
{[3 , 5] : set(['a'])}
>>> buckets[Range.closed(8,10)] = 'b'
>>> buckets
{[3 , 5] : set(['a']), [8 , 10] : set(['b'])}
>>> buckets[Range.closed(1,6)] = 'c'
>>> buckets
{[1 , 3) : set(['c']), [3 , 5] : set(['a', 'c']),
(5 , 6] : set(['c']), [8 , 10] : set(['b'])}
>>> buckets[Range.closed(4,100)]
set(['a', 'c', 'b'])
>>> del buckets[Range.closed(4,8)]
>>> buckets
{[1 , 3) : set(['c']), [3 , 4) : set(['a', 'c']), (8 , 10] : set(['b'])}]}

Table Of Contents

Previous topic

Introducing the Range

Next topic

Ranger package

This Page