Module lrng.lrng
Expand source code
from numbers import Number
from copy import copy, deepcopy
class LabeledRange:
'''
A helper class for keeping track of the start / stop of a given class in a
sequence
'''
def __init__(self, name:str, start:int, stop:int):
'''
Arguments:
name (str): name of the class
start (int): the index at which the class starts
stop (int): the index at which the class stops
'''
self.name = name
self.start = int(start)
self.stop = int(stop)
'''
Various conversions from LabeledRange to pythonic types
'''
def as_list(self):
return [self.name, self.start, self.stop]
def as_str_list(self):
return [str(e) for e in self.as_list()]
def as_tuple(self):
return tuple(self.as_list())
def as_dict(self):
return dict(zip(['name', 'start', 'stop'], self.as_list()))
def as_txt(self, delim='\t', newline='\n', newline_q=True):
return delim.join(self.as_str_list()) + (newline if newline_q else '')
def as_csv(self, newline='\n', newline_q=True):
return self.as_txt(',', newline, newline_q)
def as_tsv(self, newline='\n', newline_q=True):
return self.as_txt('\t', newline, newline_q)
def __hash__(self):
return hash(self.as_tuple())
def __repr__(self):
return '{}{}'.format(self.__class__.__name__, self.as_tuple())
def __str__(self):
return self.__repr__()
def __len__(self):
return self.stop - self.start
def __iter__(self):
return (e for e in self.as_list())
def __eq__(self, other):
if not isinstance(other, LabeledRange):
return False
return (self.name == other.name) and \
(self.start == other.start) and \
(self.stop == other.stop)
def __ne__(self, other):
return not self.__eq__(other)
def __contains__(self, other):
'''
Arguments:
other (LabeledRange / int): If other is a LabeledRange, only true
if other is bounded by self. If other is a number, true if
self.start <= other <= self.stop
Returns:
results (bool)
'''
if isinstance(other, Number):
return self.start <= other <= self.stop
if not isinstance(other, LabeledRange):
return False
if not other.same_q(self):
return False
return other.start in self and other.stop in self
def same_q(self, other):
'''Whether or not other is of the same class'''
if not isinstance(other, LabeledRange):
return False
return self.name == other.name
def min(self, other):
'''Minimum value betwen two ranges'''
if not self.same_q(other):
return min([self.start, self.stop])
return min([self.start, self.stop, other.start, other.stop])
def max(self, other):
'''Maximum value betwen two ranges'''
if not self.same_q(other):
return max([self.start, self.stop])
return max([self.start, self.stop, other.start, other.stop])
def overlap_q(self, other):
'''Whether or not two ranges overlap'''
if not self.same_q(other):
return False
return any([
other.start in self, other.stop in self,
self.start in other, self.stop in other
])
def __add__(self, other):
'''
Attempt to combine two ranges together.
'''
if not isinstance(other, LabeledRange):
raise ValueError('{} is not a LabeledRange'.format(other))
if not self.overlap_q(other):
return LabeledRanges([deepcopy(self), deepcopy(other)])
else:
return LabeledRange(self.name, self.min(other), self.max(other))
def __iadd__(self, other):
'''Combine two ranges updating this instance'''
if self.overlap_q(other):
self.start = self.min(other)
self.stop = self.max(other)
return self
class LabeledRanges:
def __init__(self, ranges:list=[]):
self.ranges = ranges
def classes(self):
return set([rng.name for rng in self])
def as_list(self):
return [rng.as_list() for rng in self]
def as_tuple(self):
return tuple([rng.as_tuple() for rng in self])
@property
def ranges(self):
return self._ranges
@ranges.setter
def ranges(self, ranges):
rngs = []
for rng in ranges:
if isinstance(rng, LabeledRange):
rngs.append(rng)
else:
rngs.append(LabeledRange(*rng))
self._ranges = list(set(rngs))
@ranges.deleter
def ranges(self):
del self._ranges
def __iter__(self):
return (rng for rng in self.ranges)
def __getitem__(self, key):
return self.ranges[key]
def __str__(self):
return self.__repr__()
def __repr__(self):
s = '{}('.format(self.__class__.__name__)
if len(self.ranges) == 0:
return s + ')'
else:
s += '\n'
for i, rng in enumerate(self.ranges):
s += '\t' + repr(rng) + '\n'
s += ')'
return s
def __eq__(self, other):
if isinstance(other, LabeledRanges):
return all([rng in other for rng in self.ranges]) and \
all([rng in self for rng in other.ranges])
return False
def __ne__(self, other):
return not self.__eq__(other)
def __contains__(self, other):
'''Test if range type is in self or if range is in ranges'''
if isinstance(other, str):
# return any([rng.name === other for rng in self])
for rng in self:
if rng.name == other:
return True
return False
if isinstance(other, LabeledRange):
# return any([other in rng for rng in self])
for rng in self:
if other in rng:
return True
return False
if isinstance(other, LabeledRanges):
# return all([self.__contains__(rng) for rng in other])
for rng in other:
if not self.__contains__(rng):
return False
return True
return False
def overlap_q(self, other):
'''Whether range overlaps with self'''
for rng in self.ranges:
if rng.overlap_q(other):
return True
return False
def append(self, other):
'''Add range to self'''
# Append a range
if isinstance(other, LabeledRange):
found_q = False
for rng in self:
if rng.overlap_q(other):
found_q = True
rng += other
if not found_q:
self.ranges.append(other)
# Map each range to the above block
if isinstance(other, LabeledRanges):
for rng in other:
self.append(other)
return self
def __give__(self, other):
'''Add other to self'''
if isinstance(other, LabeledRange):
self.append(other)
if isinstance(other, LabeledRanges):
for rng in other:
self.append(rng)
return self.simplify()
def simplify(self):
'''Remove duplicate ranges'''
for rng in self:
self.append(rng)
self.ranges = list(set(self.ranges))
return self
def __add__(self, other):
cp = deepcopy(self)
cp.__give__(other)
return cp
def __iadd__(self, other):
self.__give__(other)
return self
def __radd__(self, other):
if not isinstance(other, LabeledRange) or not isinstance(other, LabeledRanges):
return self
self.__iadd__(other)
return self
Classes
class LabeledRange (name: str, start: int, stop: int)
-
A helper class for keeping track of the start / stop of a given class in a sequence
Arguments
name (str): name of the class start (int): the index at which the class starts stop (int): the index at which the class stops
Expand source code
class LabeledRange: ''' A helper class for keeping track of the start / stop of a given class in a sequence ''' def __init__(self, name:str, start:int, stop:int): ''' Arguments: name (str): name of the class start (int): the index at which the class starts stop (int): the index at which the class stops ''' self.name = name self.start = int(start) self.stop = int(stop) ''' Various conversions from LabeledRange to pythonic types ''' def as_list(self): return [self.name, self.start, self.stop] def as_str_list(self): return [str(e) for e in self.as_list()] def as_tuple(self): return tuple(self.as_list()) def as_dict(self): return dict(zip(['name', 'start', 'stop'], self.as_list())) def as_txt(self, delim='\t', newline='\n', newline_q=True): return delim.join(self.as_str_list()) + (newline if newline_q else '') def as_csv(self, newline='\n', newline_q=True): return self.as_txt(',', newline, newline_q) def as_tsv(self, newline='\n', newline_q=True): return self.as_txt('\t', newline, newline_q) def __hash__(self): return hash(self.as_tuple()) def __repr__(self): return '{}{}'.format(self.__class__.__name__, self.as_tuple()) def __str__(self): return self.__repr__() def __len__(self): return self.stop - self.start def __iter__(self): return (e for e in self.as_list()) def __eq__(self, other): if not isinstance(other, LabeledRange): return False return (self.name == other.name) and \ (self.start == other.start) and \ (self.stop == other.stop) def __ne__(self, other): return not self.__eq__(other) def __contains__(self, other): ''' Arguments: other (LabeledRange / int): If other is a LabeledRange, only true if other is bounded by self. If other is a number, true if self.start <= other <= self.stop Returns: results (bool) ''' if isinstance(other, Number): return self.start <= other <= self.stop if not isinstance(other, LabeledRange): return False if not other.same_q(self): return False return other.start in self and other.stop in self def same_q(self, other): '''Whether or not other is of the same class''' if not isinstance(other, LabeledRange): return False return self.name == other.name def min(self, other): '''Minimum value betwen two ranges''' if not self.same_q(other): return min([self.start, self.stop]) return min([self.start, self.stop, other.start, other.stop]) def max(self, other): '''Maximum value betwen two ranges''' if not self.same_q(other): return max([self.start, self.stop]) return max([self.start, self.stop, other.start, other.stop]) def overlap_q(self, other): '''Whether or not two ranges overlap''' if not self.same_q(other): return False return any([ other.start in self, other.stop in self, self.start in other, self.stop in other ]) def __add__(self, other): ''' Attempt to combine two ranges together. ''' if not isinstance(other, LabeledRange): raise ValueError('{} is not a LabeledRange'.format(other)) if not self.overlap_q(other): return LabeledRanges([deepcopy(self), deepcopy(other)]) else: return LabeledRange(self.name, self.min(other), self.max(other)) def __iadd__(self, other): '''Combine two ranges updating this instance''' if self.overlap_q(other): self.start = self.min(other) self.stop = self.max(other) return self
Methods
def as_csv(self, newline='\n', newline_q=True)
-
Expand source code
def as_csv(self, newline='\n', newline_q=True): return self.as_txt(',', newline, newline_q)
def as_dict(self)
-
Expand source code
def as_dict(self): return dict(zip(['name', 'start', 'stop'], self.as_list()))
def as_list(self)
-
Expand source code
def as_list(self): return [self.name, self.start, self.stop]
def as_str_list(self)
-
Expand source code
def as_str_list(self): return [str(e) for e in self.as_list()]
def as_tsv(self, newline='\n', newline_q=True)
-
Expand source code
def as_tsv(self, newline='\n', newline_q=True): return self.as_txt('\t', newline, newline_q)
def as_tuple(self)
-
Expand source code
def as_tuple(self): return tuple(self.as_list())
def as_txt(self, delim='\t', newline='\n', newline_q=True)
-
Expand source code
def as_txt(self, delim='\t', newline='\n', newline_q=True): return delim.join(self.as_str_list()) + (newline if newline_q else '')
def max(self, other)
-
Maximum value betwen two ranges
Expand source code
def max(self, other): '''Maximum value betwen two ranges''' if not self.same_q(other): return max([self.start, self.stop]) return max([self.start, self.stop, other.start, other.stop])
def min(self, other)
-
Minimum value betwen two ranges
Expand source code
def min(self, other): '''Minimum value betwen two ranges''' if not self.same_q(other): return min([self.start, self.stop]) return min([self.start, self.stop, other.start, other.stop])
def overlap_q(self, other)
-
Whether or not two ranges overlap
Expand source code
def overlap_q(self, other): '''Whether or not two ranges overlap''' if not self.same_q(other): return False return any([ other.start in self, other.stop in self, self.start in other, self.stop in other ])
def same_q(self, other)
-
Whether or not other is of the same class
Expand source code
def same_q(self, other): '''Whether or not other is of the same class''' if not isinstance(other, LabeledRange): return False return self.name == other.name
class LabeledRanges (ranges: list = [])
-
Expand source code
class LabeledRanges: def __init__(self, ranges:list=[]): self.ranges = ranges def classes(self): return set([rng.name for rng in self]) def as_list(self): return [rng.as_list() for rng in self] def as_tuple(self): return tuple([rng.as_tuple() for rng in self]) @property def ranges(self): return self._ranges @ranges.setter def ranges(self, ranges): rngs = [] for rng in ranges: if isinstance(rng, LabeledRange): rngs.append(rng) else: rngs.append(LabeledRange(*rng)) self._ranges = list(set(rngs)) @ranges.deleter def ranges(self): del self._ranges def __iter__(self): return (rng for rng in self.ranges) def __getitem__(self, key): return self.ranges[key] def __str__(self): return self.__repr__() def __repr__(self): s = '{}('.format(self.__class__.__name__) if len(self.ranges) == 0: return s + ')' else: s += '\n' for i, rng in enumerate(self.ranges): s += '\t' + repr(rng) + '\n' s += ')' return s def __eq__(self, other): if isinstance(other, LabeledRanges): return all([rng in other for rng in self.ranges]) and \ all([rng in self for rng in other.ranges]) return False def __ne__(self, other): return not self.__eq__(other) def __contains__(self, other): '''Test if range type is in self or if range is in ranges''' if isinstance(other, str): # return any([rng.name === other for rng in self]) for rng in self: if rng.name == other: return True return False if isinstance(other, LabeledRange): # return any([other in rng for rng in self]) for rng in self: if other in rng: return True return False if isinstance(other, LabeledRanges): # return all([self.__contains__(rng) for rng in other]) for rng in other: if not self.__contains__(rng): return False return True return False def overlap_q(self, other): '''Whether range overlaps with self''' for rng in self.ranges: if rng.overlap_q(other): return True return False def append(self, other): '''Add range to self''' # Append a range if isinstance(other, LabeledRange): found_q = False for rng in self: if rng.overlap_q(other): found_q = True rng += other if not found_q: self.ranges.append(other) # Map each range to the above block if isinstance(other, LabeledRanges): for rng in other: self.append(other) return self def __give__(self, other): '''Add other to self''' if isinstance(other, LabeledRange): self.append(other) if isinstance(other, LabeledRanges): for rng in other: self.append(rng) return self.simplify() def simplify(self): '''Remove duplicate ranges''' for rng in self: self.append(rng) self.ranges = list(set(self.ranges)) return self def __add__(self, other): cp = deepcopy(self) cp.__give__(other) return cp def __iadd__(self, other): self.__give__(other) return self def __radd__(self, other): if not isinstance(other, LabeledRange) or not isinstance(other, LabeledRanges): return self self.__iadd__(other) return self
Instance variables
var ranges
-
Expand source code
@property def ranges(self): return self._ranges
Methods
def append(self, other)
-
Add range to self
Expand source code
def append(self, other): '''Add range to self''' # Append a range if isinstance(other, LabeledRange): found_q = False for rng in self: if rng.overlap_q(other): found_q = True rng += other if not found_q: self.ranges.append(other) # Map each range to the above block if isinstance(other, LabeledRanges): for rng in other: self.append(other) return self
def as_list(self)
-
Expand source code
def as_list(self): return [rng.as_list() for rng in self]
def as_tuple(self)
-
Expand source code
def as_tuple(self): return tuple([rng.as_tuple() for rng in self])
def classes(self)
-
Expand source code
def classes(self): return set([rng.name for rng in self])
def overlap_q(self, other)
-
Whether range overlaps with self
Expand source code
def overlap_q(self, other): '''Whether range overlaps with self''' for rng in self.ranges: if rng.overlap_q(other): return True return False
def simplify(self)
-
Remove duplicate ranges
Expand source code
def simplify(self): '''Remove duplicate ranges''' for rng in self: self.append(rng) self.ranges = list(set(self.ranges)) return self