Public functions
get_name
Return the identifier name of the tier.
View Source
def get_name(self):
"""Return the identifier name of the tier."""
return self.__name
get_ctrl_vocab
Return the controlled vocabulary of the tier.
View Source
def get_ctrl_vocab(self):
"""Return the controlled vocabulary of the tier."""
return self.__ctrl_vocab
get_media
Return the media of the tier.
View Source
def get_media(self):
"""Return the media of the tier."""
return self.__media
get_parent
Return the parent of the tier.
View Source
def get_parent(self):
"""Return the parent of the tier."""
return self.__parent
set_name
Set the name of the tier.
If no name is given, an GUID is randomly assigned.
Important: An empty string is accepted.
Parameters
- name: (str) The identifier name or None.
Returns
View Source
def set_name(self, name=None):
"""Set the name of the tier.
If no name is given, an GUID is randomly assigned.
Important: An empty string is accepted.
:param name: (str) The identifier name or None.
:returns: the formatted name
"""
if name is None:
if self.get_id() == '':
self.gen_id()
name = self.get_id()
su = sppasUnicode(name)
self.__name = su.to_strip()
return self.__name
set_ctrl_vocab
Set a controlled vocabulary to this tier.
Parameters
- ctrl_vocab: (sppasCtrlVocab or None)
Raises
AnnDataTypeError, CtrlVocabContainsError
View Source
def set_ctrl_vocab(self, ctrl_vocab=None):
"""Set a controlled vocabulary to this tier.
:param ctrl_vocab: (sppasCtrlVocab or None)
:raises: AnnDataTypeError, CtrlVocabContainsError
"""
if ctrl_vocab is not None:
if isinstance(ctrl_vocab, sppasCtrlVocab) is False:
raise AnnDataTypeError(ctrl_vocab, 'sppasCtrlVocab')
for annotation in self.__ann:
for label in annotation.get_labels():
annotation.validate_label(label)
if self.__parent is not None:
try:
self.__parent.add_ctrl_vocab(ctrl_vocab)
except TrsAddError:
pass
self.__ctrl_vocab = ctrl_vocab
set_media
Set a media to the tier.
Parameters
Raises
AnnDataTypeError
View Source
def set_media(self, media):
"""Set a media to the tier.
:param media: (sppasMedia)
:raises: AnnDataTypeError
"""
if media is not None:
if isinstance(media, sppasMedia) is False:
raise AnnDataTypeError(media, 'sppasMedia')
if self.__parent is not None:
try:
self.__parent.add_media(media)
except TrsAddError:
pass
self.__media = media
set_parent
Set the parent of the tier.
Parameters
- parent: (sppasTranscription)
View Source
def set_parent(self, parent):
"""Set the parent of the tier.
:param parent: (sppasTranscription)
"""
self.__parent = parent
if parent is not None:
if self.__media is not None:
try:
self.__parent.add_media(self.__media)
except TrsAddError:
pass
if self.__ctrl_vocab is not None:
try:
self.__parent.add_ctrl_vocab(self.__ctrl_vocab)
except TrsAddError:
pass
copy
Return a deep copy of the tier.
Returns
- (sppasTier) including the 'id'.
View Source
def copy(self):
"""Return a deep copy of the tier.
:return: (sppasTier) including the 'id'.
"""
new_tier = sppasTier(self.__name)
my_parent = self.get_parent()
self.set_parent(None)
new_tier.set_parent(None)
new_tier.set_ctrl_vocab(self.__ctrl_vocab)
new_tier.set_media(self.__media)
for a in self.__ann:
new_ann = a.copy()
new_tier.add(new_ann)
for key in self.get_meta_keys():
new_tier.set_meta(key, self.get_meta(key))
new_tier.set_parent(my_parent)
self.__parent = my_parent
return new_tier
create_annotation
Create and add a new annotation into the tier.
Parameters
- location: (sppasLocation) the location(s) where the annotation happens
- labels: (sppasLabel, list) the label(s) to stamp this annot.
Returns
View Source
def create_annotation(self, location, labels=None):
"""Create and add a new annotation into the tier.
:param location: (sppasLocation) the location(s) where the annotation happens
:param labels: (sppasLabel, list) the label(s) to stamp this annot.
:returns: sppasAnnotation
"""
ann = sppasAnnotation(location, labels)
self.add(ann)
return ann
create_annotation_before
Create and add a new annotation in the hole before idx.
Parameters
- idx: (int) Index of an existing annotation
Returns
Raises
AnnDataTypeError, AnnDataIndexError
View Source
def create_annotation_before(self, idx):
"""Create and add a new annotation in the hole before idx.
:param idx: (int) Index of an existing annotation
:returns: sppasAnnotation
:raises AnnDataTypeError, AnnDataIndexError
"""
if self.is_point():
raise AnnDataTypeError(self.get_name(), 'Interval, Disjoint')
try:
ann = self.__ann[idx]
except IndexError:
raise AnnDataIndexError(idx)
if idx == 0:
if self.is_int():
begin = sppasPoint(0)
else:
begin = sppasPoint(0.0)
else:
prev_ann = self.__ann[idx - 1]
begin = prev_ann.get_highest_localization()
end = ann.get_lowest_localization()
new_ann = self.create_annotation(sppasLocation(sppasInterval(begin, end)))
return new_ann
create_annotation_after
Create and add a new annotation in the hole after idx.
Parameters
- idx: (int) Index of an existing annotation
Returns
Raises
AnnDataTypeError, AnnDataIndexError
View Source
def create_annotation_after(self, idx):
"""Create and add a new annotation in the hole after idx.
:param idx: (int) Index of an existing annotation
:returns: sppasAnnotation
:raises AnnDataTypeError, AnnDataIndexError
"""
if self.is_point():
raise AnnDataTypeError(self.get_name(), 'Interval, Disjoint')
if idx + 1 == len(self.__ann):
raise AnnDataIndexError(idx + 1)
try:
ann = self.__ann[idx]
next_ann = self.__ann[idx + 1]
except IndexError:
raise AnnDataIndexError(idx)
begin = ann.get_highest_localization()
end = next_ann.get_lowest_localization()
new_ann = self.create_annotation(sppasLocation(sppasInterval(begin, end)))
return new_ann
is_empty
Return True if the tier does not contain annotations.
View Source
def is_empty(self):
"""Return True if the tier does not contain annotations."""
return len(self.__ann) == 0
append
Append the given annotation at the end of the tier.
Assign this tier as parent to the annotation.
Parameters
- annotation: (sppasAnnotation)
Raises
AnnDataTypeError, CtrlVocabContainsError, HierarchyContainsError, HierarchyTypeError, TierAppendError
View Source
def append(self, annotation):
"""Append the given annotation at the end of the tier.
Assign this tier as parent to the annotation.
:param annotation: (sppasAnnotation)
:raises: AnnDataTypeError, CtrlVocabContainsError, HierarchyContainsError, HierarchyTypeError, TierAppendError
"""
self.validate_annotation(annotation)
if len(self.__ann) > 0:
end = self.__ann[-1].get_highest_localization()
new = annotation.get_lowest_localization()
if annotation.location_is_point() and end == new:
raise TierAppendError(end, new)
if end > new:
raise TierAppendError(end, new)
self.__ann.append(annotation)
add
Add an annotation to the tier in sorted order.
Assign this tier as parent to the annotation.
Parameters
- annotation: (sppasAnnotation)
Raises
AnnDataTypeError, CtrlVocabContainsError, HierarchyContainsError, HierarchyTypeError
Returns
- the index of the annotation in the tier
View Source
def add(self, annotation):
"""Add an annotation to the tier in sorted order.
Assign this tier as parent to the annotation.
:param annotation: (sppasAnnotation)
:raises: AnnDataTypeError, CtrlVocabContainsError, HierarchyContainsError, HierarchyTypeError
:returns: the index of the annotation in the tier
"""
self.validate_annotation(annotation)
try:
self.append(annotation)
except Exception:
if annotation.location_is_point():
index = self.index(annotation.get_lowest_localization())
if index != -1:
if self.__ann[index].get_lowest_localization().get_midpoint() == annotation.get_lowest_localization().get_midpoint():
raise TierAddError(index)
self.__ann.insert(index + 1, annotation)
return index + 1
else:
index = self.near(annotation.get_lowest_localization(), direction=-1)
self.__ann.insert(index, annotation)
return index
else:
index = self.mindex(annotation.get_lowest_localization(), bound=0)
while index + 1 < len(self.__ann) and self.__ann[index + 1].get_lowest_localization() < annotation.get_lowest_localization():
index += 1
while index + 1 < len(self.__ann) and self.__ann[index + 1].get_lowest_localization() == annotation.get_lowest_localization() and (self.__ann[index + 1].get_highest_localization() < annotation.get_highest_localization()):
index += 1
if self.__ann[index].get_location() == annotation.get_location():
raise TierAddError(index)
if index + 1 < len(self.__ann):
if self.__ann[index + 1].get_location() == annotation.get_location():
raise TierAddError(index + 1)
self.__ann.insert(index + 1, annotation)
return index + 1
return len(self.__ann) - 1
remove
Remove annotation intervals between begin and end.
Parameters
- begin: (sppasPoint)
- end: (sppasPoint)
- overlaps: (bool)
Returns
- the number of removed annotations
Raises
HierarchyContainsError
View Source
def remove(self, begin, end, overlaps=False):
"""Remove annotation intervals between begin and end.
:param begin: (sppasPoint)
:param end: (sppasPoint)
:param overlaps: (bool)
:returns: the number of removed annotations
:raises: HierarchyContainsError
"""
if end < begin:
raise IntervalBoundsError(begin, end)
annotations = self.find(begin, end, overlaps)
copied_anns = list()
for a in reversed(annotations):
copied_anns.append(a.copy())
self.__ann.remove(a)
if self.__parent is not None:
try:
self.validate()
except:
for a in copied_anns:
self.add(a)
return len(copied_anns)
pop
Remove the annotation at the given position in the tier.
If no index is specified, pop() removes
and returns the last annotation in the tier.
Parameters
- index: (int) Index of the annotation to remove.
Raises
HierarchyContainsError
View Source
def pop(self, index=-1):
"""Remove the annotation at the given position in the tier.
If no index is specified, pop() removes
and returns the last annotation in the tier.
:param index: (int) Index of the annotation to remove.
:raises: HierarchyContainsError
"""
try:
ann = self.__ann[index]
copied_ann = ann.copy()
except IndexError:
raise AnnDataIndexError(index)
self.__ann.pop(index)
if self.__parent is not None:
try:
self.validate()
except:
self.__ann.insert(index, copied_ann)
raise
remove_unlabelled
Remove annotations without labels.
Do not remove an annotation if it invalidates the hierarchy.
Returns
- the number of removed annotations
View Source
def remove_unlabelled(self):
"""Remove annotations without labels.
Do not remove an annotation if it invalidates the hierarchy.
:returns: the number of removed annotations
"""
nb = 0
for a in reversed(self.__ann):
if a.is_labelled() is False:
try:
self.__ann.remove(a)
nb += 1
except:
pass
return nb
split
Split annotation at the given index into 2 annotations.
Example with ann=[1.0;2.0] at index 1:
- tier.split(1): creates ann1=[1.0;1.5] and ann2=[1.5;2.0]
- tier.split(1, 1.2): creates ann1=[1.0;1.2] and ann2=[1.2;2.0]
- tier.split(1, 1.2, 1.5): creates ann1=[1.0;1.2] and ann2=[1.5;2.0]
Parameters
- idx: (int) Index of the annotation to split.
- from_time: (int) Time value to split from
- to_time: (int) Time value to end the cut part
Returns
- newly created annotation at index idx+1
View Source
def split(self, idx, from_time=None, to_time=None):
"""Split annotation at the given index into 2 annotations.
Example with ann=[1.0;2.0] at index 1:
- tier.split(1): creates ann1=[1.0;1.5] and ann2=[1.5;2.0]
- tier.split(1, 1.2): creates ann1=[1.0;1.2] and ann2=[1.2;2.0]
- tier.split(1, 1.2, 1.5): creates ann1=[1.0;1.2] and ann2=[1.5;2.0]
:param idx: (int) Index of the annotation to split.
:param from_time: (int) Time value to split from
:param to_time: (int) Time value to end the cut part
:return: newly created annotation at index idx+1
"""
if self.is_point() is True:
raise AnnDataTypeError(self.get_name(), 'Interval, Disjoint')
try:
ann = self.__ann[idx]
except IndexError:
raise AnnDataIndexError(idx)
localization = ann.get_location().get_best()
end = localization.get_end().copy()
if from_time is not None:
if isinstance(from_time, sppasPoint) is False:
from_time = sppasPoint(from_time)
if ann.inside_localization(from_time) is False:
raise IntervalRangeException(from_time, localization.get_begin(), end)
if to_time is not None:
if isinstance(to_time, sppasPoint) is False:
to_time = sppasPoint(to_time)
if ann.inside_localization(to_time) is False:
raise IntervalRangeException(to_time, localization.get_begin(), end)
if from_time is None:
cut_point = localization.middle()
else:
cut_point = from_time
try:
localization.set_end(cut_point)
self.validate()
except:
localization.set_end(end)
raise
if to_time is not None:
cut_point = to_time
new_ann = self.create_annotation(sppasLocation(sppasInterval(cut_point.copy(), end)))
return new_ann
merge
Merge the annotation at given index with next or previous one.
if direction > 0:
annidx: [beginidx, endidx, labelsidx]
nextann: [beginn, endn, labelsn]
result: [beginidx, endn, labelsidx + labelsn]
if direction < 0:
prevann: [beginp, endp, labelsp]
annidx: [beginidx, endidx, labelsidx]
result: [beginp, endidx, labelsp + labelsidx]
Parameters
- idx: (int) Index of the annotation in the list
- direction: (int) Positive for next, Negative for previous
Returns
- (bool) False if direction does not match with index
Raises
Exception if merged annotation can't be deleted of the tier
View Source
def merge(self, idx, direction):
"""Merge the annotation at given index with next or previous one.
if direction > 0:
ann_idx: [begin_idx, end_idx, labels_idx]
next_ann: [begin_n, end_n, labels_n]
result: [begin_idx, end_n, labels_idx + labels_n]
if direction < 0:
prev_ann: [begin_p, end_p, labels_p]
ann_idx: [begin_idx, end_idx, labels_idx]
result: [begin_p, end_idx, labels_p + labels_idx]
:param idx: (int) Index of the annotation in the list
:param direction: (int) Positive for next, Negative for previous
:return: (bool) False if direction does not match with index
:raise: Exception if merged annotation can't be deleted of the tier
"""
if self.is_point() is True:
raise AnnDataTypeError(self.get_name(), 'Interval, Disjoint')
try:
ann = self.__ann[idx]
except IndexError:
raise AnnDataIndexError(idx)
if direction > 0:
merge_idx = idx + 1
if merge_idx == len(self.__ann):
return False
else:
if idx == 0:
return False
merge_idx = idx - 1
merge_ann = self.__ann[merge_idx]
merge_labels = [l.copy() for l in merge_ann.get_labels()]
merge_loc = merge_ann.get_location().get_best()
localization = ann.get_location().get_best()
copied_loc = localization.copy()
labels = self.__ann[idx].get_labels()
try:
if direction > 0:
localization.set_end(merge_loc.get_end())
new_labels = labels + merge_labels
else:
localization.set_begin(merge_loc.get_begin())
new_labels = merge_labels + labels
self.validate()
ann.set_labels(new_labels)
self.pop(merge_idx)
except:
ann.set_best_localization(copied_loc)
ann.set_labels(labels)
raise
return True
get_all_points
Return the list of all points of the tier.
View Source
def get_all_points(self):
"""Return the list of all points of the tier."""
if len(self.__ann) == 0:
return []
points = list()
for ann in self.__ann:
points.extend(ann.get_all_points())
return points
get_first_point
Return the first point of the first annotation.
View Source
def get_first_point(self):
"""Return the first point of the first annotation."""
if len(self.__ann) == 0:
return None
return self.__ann[0].get_lowest_localization()
get_last_point
Return the last point of the last location.
View Source
def get_last_point(self):
"""Return the last point of the last location."""
if len(self.__ann) == 0:
return None
return self.__ann[-1].get_highest_localization()
has_point
Return True if the tier contains a given point.
Parameters
- point: (sppasPoint) The point to find in the tier.
Returns
View Source
def has_point(self, point):
"""Return True if the tier contains a given point.
:param point: (sppasPoint) The point to find in the tier.
:returns: (bool)
"""
if isinstance(point, sppasPoint) is False:
raise AnnDataTypeError(point, 'sppasPoint')
return point in self.get_all_points()
has_location
Return True if the tier has the given location.
to be tested.
Parameters
View Source
def has_location(self, location):
"""Return True if the tier has the given location.
to be tested.
"""
begin = location.get_lowest_localization()
end = location.get_highest_localization()
anns = self.find(begin, end, overlaps=False)
for a in anns:
if a.get_location() == location:
return True
return False
is_disjoint
Return True if the tier is made of disjoint localizations.
View Source
def is_disjoint(self):
"""Return True if the tier is made of disjoint localizations."""
if len(self.__ann) == 0:
return False
return self.__ann[0].get_location().is_disjoint()
is_interval
Return True if the tier is made of interval localizations.
View Source
def is_interval(self):
"""Return True if the tier is made of interval localizations."""
if len(self.__ann) == 0:
return False
return self.__ann[0].get_location().is_interval()
is_point
Return True if the tier is made of point localizations.
View Source
def is_point(self):
"""Return True if the tier is made of point localizations."""
if len(self.__ann) == 0:
return False
return self.__ann[0].get_location().is_point()
get_midpoint_intervals
Return midpoint values of all the intervals.
View Source
def get_midpoint_intervals(self):
"""Return midpoint values of all the intervals."""
units = list()
if self.is_point() is False:
for i in range(len(self)):
b = self.__ann[i].get_lowest_localization().get_midpoint()
e = self.__ann[i].get_highest_localization().get_midpoint()
units.append((b, e))
return units
get_midpoint_points
Return midpoint values of all the points.
View Source
def get_midpoint_points(self):
"""Return midpoint values of all the points."""
units = list()
if self.is_point() is True:
for i in range(len(self)):
m = self.__ann[i].get_lowest_localization().get_midpoint()
units.append(m)
return units
set_radius
Fix a radius value to all points of the tier.
Parameters
- radius: (int, float) New radius value
Raises
AnnDataTypeError, AnnDataNegValueError
View Source
def set_radius(self, radius):
"""Fix a radius value to all points of the tier.
:param radius: (int, float) New radius value
:raise: AnnDataTypeError, AnnDataNegValueError
"""
for ann in self.__ann:
ann.get_location().set_radius(radius)
find
Return a list of annotations between begin and end.
Parameters
- begin: sppasPoint or None to start from the beginning of the tier
- end: sppasPoint or None to end at the end of the tier
- overlaps: (bool) Return also overlapped annotations, using midpoints. Not relevant for tiers with points.
- indexes: (bool) Return indexes instead of annotations
- radius_overlaps: (bool) Return also overlapped annotations, using midpoints and radius. Not relevant for tiers with points. overlaps must be true.
Returns
- List of sppasAnnotation or list of indexes
View Source
def find(self, begin, end, overlaps=True, indexes=False, radius_overlaps=False):
"""Return a list of annotations between begin and end.
:param begin: sppasPoint or None to start from the beginning of the tier
:param end: sppasPoint or None to end at the end of the tier
:param overlaps: (bool) Return also overlapped annotations, using midpoints. Not relevant for tiers with points.
:param indexes: (bool) Return indexes instead of annotations
:param radius_overlaps: (bool) Return also overlapped annotations, using midpoints and radius. Not relevant for tiers with points. overlaps must be true.
:returns: List of sppasAnnotation or list of indexes
"""
if len(self.__ann) == 0:
return []
if begin is None:
begin = self.get_first_point()
if end is None:
end = self.get_last_point()
if begin > self.get_last_point() or end < self.get_first_point():
return []
annotations = list()
if self.is_point() is True:
lo = self.index(begin)
if lo == -1:
lo = self.near(begin, direction=1)
for i, ann in enumerate(self.__ann[lo:]):
lowest = ann.get_lowest_localization()
highest = ann.get_highest_localization()
if lowest > end and highest > end:
break
if lowest >= begin and highest <= end:
if indexes is True:
annotations.append(lo + i)
else:
annotations.append(ann)
else:
lo = self.__find(begin, radius_overlaps, closest=True)
if lo != -1:
if overlaps is True:
for i, ann in enumerate(self.__ann[lo:]):
b = ann.get_lowest_localization()
e = ann.get_highest_localization()
if b > end:
break
if radius_overlaps is False:
if end > b and begin < e:
if indexes is True:
annotations.append(lo + i)
else:
annotations.append(ann)
elif end >= b and begin <= e:
if indexes is True:
annotations.append(lo + i)
else:
annotations.append(ann)
else:
for i, ann in enumerate(self.__ann[lo:]):
b = ann.get_lowest_localization()
e = ann.get_highest_localization()
if b >= begin and e <= end:
if indexes is True:
annotations.append(lo + i)
else:
annotations.append(ann)
if b >= end:
break
else:
logging.info('No annotation is matching begin={}, end={}'.format(begin, end))
return annotations
index
Return the index of the moment (int), or -1.
Only for tier with points.
Parameters
View Source
def index(self, moment):
"""Return the index of the moment (int), or -1.
Only for tier with points.
:param moment: (sppasPoint)
"""
if self.is_point() is False:
return -1
lo = 0
hi = len(self.__ann)
mid = (lo + hi) // 2
found = False
while lo < hi:
mid = (lo + hi) // 2
a = self.__ann[mid]
if moment < a.get_lowest_localization():
hi = mid
elif moment > a.get_highest_localization():
lo = mid + 1
else:
found = True
break
if found is False:
return -1
return mid
lindex
Return the index of the interval starting at a given moment, or -1.
Only for tier with intervals or disjoint.
If the tier contains more than one annotation starting at the same
moment, the method returns the first one.
Parameters
View Source
def lindex(self, moment):
"""Return the index of the interval starting at a given moment, or -1.
Only for tier with intervals or disjoint.
If the tier contains more than one annotation starting at the same
moment, the method returns the first one.
:param moment: (sppasPoint)
"""
if self.is_point() is True:
return -1
lo = 0
hi = len(self.__ann)
mid = (lo + hi) // 2
found = False
while lo < hi:
mid = (lo + hi) // 2
begin = self.__ann[mid].get_lowest_localization()
if moment < begin:
hi = mid
elif moment > begin:
lo = mid + 1
else:
found = True
break
if found is False:
return -1
if mid == 0:
return 0
while mid >= 0 and self.__ann[mid].get_lowest_localization() == moment:
mid -= 1
return mid + 1
mindex
Return index of the interval containing the given moment.
Only for tier with intervals or disjoint.
If the tier contains more than one annotation at the same moment,
the method returns the first one (i.e. the one which started at first).
Parameters
- moment: (sppasPoint)
- bound: (int) - 0 to exclude bounds of the interval; - -1 to include begin bound; - +1 to include end bound; - +2 to include both begin/end bounds; - others: the midpoint of moment is strictly inside
Returns
- (int) Index of the 1st annotation containing moment or -1
View Source
def mindex(self, moment, bound=0):
"""Return index of the interval containing the given moment.
Only for tier with intervals or disjoint.
If the tier contains more than one annotation at the same moment,
the method returns the first one (i.e. the one which started at first).
:param moment: (sppasPoint)
:param bound: (int)
- 0 to exclude bounds of the interval;
- -1 to include begin bound;
- +1 to include end bound;
- +2 to include both begin/end bounds;
- others: the midpoint of moment is strictly inside
:returns: (int) Index of the 1st annotation containing moment or -1
"""
if self.is_point() is True:
return -1
for i, a in enumerate(self.__ann):
b = a.get_lowest_localization()
e = a.get_highest_localization()
if bound == -1:
if b <= moment < e:
return i
elif bound == 1:
if b < moment <= e:
return i
elif bound == 2:
if b <= moment <= e:
return i
elif bound == 0:
if b < moment < e:
return i
elif b < moment.get_midpoint() < e:
return i
return -1
rindex
Return the index of the interval ending at the given moment.
Only for tier with intervals or disjoint.
If the tier contains more than one annotation ending at the same moment,
the method returns the last one.
Parameters
View Source
def rindex(self, moment):
"""Return the index of the interval ending at the given moment.
Only for tier with intervals or disjoint.
If the tier contains more than one annotation ending at the same moment,
the method returns the last one.
:param moment: (sppasPoint)
"""
if self.is_point() is True:
return -1
lo = 0
hi = len(self.__ann)
mid = (lo + hi) // 2
found = False
while lo < hi:
mid = (lo + hi) // 2
a = self.__ann[mid]
if moment < a.get_highest_localization():
hi = mid
elif moment > a.get_highest_localization():
lo = mid + 1
else:
found = True
break
if found is False:
return -1
if mid == len(self.__ann) - 1:
return mid
while mid + 1 < len(self.__ann) and self.__ann[mid + 1].get_highest_localization() == moment:
mid += 1
return mid
is_superset
Return True if this tier contains all points of the other tier.
Parameters
Returns
View Source
def is_superset(self, other):
"""Return True if this tier contains all points of the other tier.
:param other: (sppasTier)
:returns: Boolean
"""
if len(other) == 0:
return True
tier_points = self.get_all_points()
other_points = other.get_all_points()
for op in other_points:
if op not in tier_points:
return False
return True
near
Search for the annotation whose localization is closest.
Search for the nearest localization to the given moment into a
given direction.
Parameters
- moment: (sppasPoint)
- direction: (int) - nearest 0 - nereast forward 1 - nereast backward -1
View Source
def near(self, moment, direction=1):
"""Search for the annotation whose localization is closest.
Search for the nearest localization to the given moment into a
given direction.
:param moment: (sppasPoint)
:param direction: (int)
- nearest 0
- nereast forward 1
- nereast backward -1
"""
if isinstance(moment, sppasPoint) is False:
raise sppasTypeError('moment', 'sppasPoint')
if len(self.__ann) == 0:
return -1
if len(self.__ann) == 1:
return 0
index = self.__find(moment, radius_overlaps=False, closest=True)
if index == -1:
return -1
a = self.__ann[index]
if direction == 1:
if moment <= a.get_lowest_localization():
return index
if index + 1 < len(self.__ann):
return index + 1
return -1
elif direction == -1:
if moment >= a.get_highest_localization():
return index
if index - 1 >= 0:
return index - 1
return -1
a = self.__ann[index]
if a.get_lowest_localization() <= moment <= a.get_highest_localization():
return index
_next = index + 1
if _next >= len(self.__ann):
return index
time = moment.get_midpoint()
prev_time = self.__ann[index].get_highest_localization().get_midpoint()
next_time = self.__ann[_next].get_lowest_localization().get_midpoint()
if abs(time - prev_time) > abs(next_time - time):
return _next
return index
is_string
All label tags are string or unicode or None.
View Source
def is_string(self):
"""All label tags are string or unicode or None."""
if len(self.__ann) == 0:
return False
for ann in self.__ann:
if ann.is_labelled() is True:
return ann.label_is_string()
return False
is_float
All label tags are float values or None.
View Source
def is_float(self):
"""All label tags are float values or None."""
if len(self.__ann) == 0:
return False
for ann in self.__ann:
if ann.is_labelled() is True:
return ann.label_is_float()
return False
is_int
All label tags are integer values or None.
View Source
def is_int(self):
"""All label tags are integer values or None."""
if len(self.__ann) == 0:
return False
for ann in self.__ann:
if ann.is_labelled() is True:
return ann.label_is_int()
return False
is_bool
All label tags are boolean values or None.
View Source
def is_bool(self):
"""All label tags are boolean values or None."""
if len(self.__ann) == 0:
return False
for ann in self.__ann:
if ann.is_labelled() is True:
return ann.label_is_bool()
return False
is_fuzzypoint
All label tags are fuzzy point values or None.
View Source
def is_fuzzypoint(self):
"""All label tags are fuzzy point values or None."""
if len(self.__ann) == 0:
return False
for ann in self.__ann:
if ann.is_labelled() is True:
return ann.label_is_point()
return False
is_fuzzyrect
All label tags are fuzzy rectangle values or None.
View Source
def is_fuzzyrect(self):
"""All label tags are fuzzy rectangle values or None."""
if len(self.__ann) == 0:
return False
for ann in self.__ann:
if ann.is_labelled() is True:
return ann.label_is_rect()
return False
get_labels_type
Return the current type of labels, or an empty string.
View Source
def get_labels_type(self):
"""Return the current type of labels, or an empty string."""
if len(self.__ann) == 0:
return ''
for ann in self.__ann:
if ann.is_labelled() is True:
return ann.get_label_type()
return ''
get_nb_filled_labels
Return the number annotation with a filled label.
View Source
def get_nb_filled_labels(self):
"""Return the number annotation with a filled label."""
nb = 0
for ann in self.__ann:
if ann.is_labelled() is True:
nb += 1
return nb
validate
View Source
def validate(self):
if self.__parent is not None:
self.__parent.validate_hierarchy(self)
validate_annotation
Validate the annotation and set its parent to this tier.
Parameters
- annotation: (sppasAnnotation)
Raises
AnnDataTypeError, CtrlVocabContainsError, HierarchyContainsError, HierarchyTypeError
View Source
def validate_annotation(self, annotation):
"""Validate the annotation and set its parent to this tier.
:param annotation: (sppasAnnotation)
:raises: AnnDataTypeError, CtrlVocabContainsError, HierarchyContainsError, HierarchyTypeError
"""
if isinstance(annotation, sppasAnnotation) is False:
raise AnnDataTypeError(annotation, 'sppasAnnotation')
if len(self.__ann) > 0:
if annotation.location_is_point() is True and self.is_point() is False:
raise AnnDataTypeError(str(annotation) + ' (sppasPoint)', 'sppasInterval')
if annotation.location_is_interval() is True and self.is_interval() is False:
raise AnnDataTypeError(str(annotation) + ' (sppasInterval)', 'sppasPoint')
if annotation.location_is_disjoint() is True and self.is_disjoint() is False:
raise AnnDataTypeError(annotation, 'sppasDisjoint')
annotation.set_parent(self)
validate_annotation_label
Validate a label.
Parameters
Raises
CtrlVocabContainsError
View Source
def validate_annotation_label(self, label):
"""Validate a label.
:param label: (sppasLabel)
:raises: CtrlVocabContainsError
"""
if self.__ctrl_vocab is not None:
for tag, score in label:
if tag.is_empty() is False and self.__ctrl_vocab.contains(tag) is False:
raise CtrlVocabContainsError(tag)
if (self.is_bool() or self.is_float() or self.is_int() or self.is_string()) is False:
return
if label.is_tagged():
if label.get_type() != self.get_labels_type():
raise AnnDataTypeError(label, self.get_labels_type())
validate_annotation_location
Ask the parent to validate a location.
Parameters
- location: (sppasLocation)
Raises
AnnDataTypeError, HierarchyContainsError, HierarchyTypeError
View Source
def validate_annotation_location(self, location):
"""Ask the parent to validate a location.
:param location: (sppasLocation)
:raises: AnnDataTypeError, HierarchyContainsError, HierarchyTypeError
"""
if self.__parent is not None:
self.__parent.validate_annotation_location(self, location)
get_annotation
Find an annotation from its metadata 'id'.
Parameters
- identifier: (str) Metadata 'id' of an annotation.
Returns
View Source
def get_annotation(self, identifier):
"""Find an annotation from its metadata 'id'.
:param identifier: (str) Metadata 'id' of an annotation.
:returns: sppasAnnotation or None
"""
for a in self:
if a.get_meta('id') == identifier:
return a
return None
get_annotation_index
Find an annotation.
Parameters
Returns
View Source
def get_annotation_index(self, ann):
"""Find an annotation.
:param ann: (sppasAnnotation)
:returns: (int) -1 if not found
"""
if self.is_point():
return self.index(ann.get_highest_localization())
else:
i1 = self.lindex(ann.get_lowest_localization())
i2 = self.rindex(ann.get_highest_localization())
for i in range(i1, i2 + 1):
ai = self.__ann[i]
if ai is ann:
return i
if ai.get_id() == ann.get_id():
return i
return -1
create_ctrl_vocab
Create the controlled vocabulary from annotation labels.
Create (or re-create) the controlled vocabulary from the list of
already existing annotation labels.
The current controlled vocabulary is deleted.
Parameters
- name: (str) Name of the controlled vocabulary. The name of the tier is used by default.
View Source
def create_ctrl_vocab(self, name=None):
"""Create the controlled vocabulary from annotation labels.
Create (or re-create) the controlled vocabulary from the list of
already existing annotation labels.
The current controlled vocabulary is deleted.
:param name: (str) Name of the controlled vocabulary. The name of the tier is used by default.
"""
if name is None:
name = self.__name
self.__ctrl_vocab = sppasCtrlVocab(name)
for ann in self.__ann:
for label in ann.get_labels():
if label.is_tagged():
for tag, score in label:
self.__ctrl_vocab.add(tag)
export_to_intervals
Create a tier with the consecutive filled intervals.
Return an empty tier if 'self' is not of type "interval".
The created intervals are not filled.
Parameters
Returns
View Source
def export_to_intervals(self, separators):
"""Create a tier with the consecutive filled intervals.
Return an empty tier if 'self' is not of type "interval".
The created intervals are not filled.
:param separators: (list)
:returns: (sppasTier)
"""
intervals = sppasTier('intervals')
if self.is_interval() is False:
return intervals
begin = self.get_first_point()
end = begin
prev_ann = None
for ann in self.__ann:
tag = None
if ann.label_is_filled():
tag = ann.get_best_tag()
if prev_ann is not None:
if tag is None or tag.get_typed_content() in separators or prev_ann.get_highest_localization() < ann.get_lowest_localization():
if end > begin:
intervals.create_annotation(sppasLocation(sppasInterval(begin, prev_ann.get_highest_localization())))
if tag is None or tag.get_typed_content() in separators:
begin = ann.get_highest_localization()
else:
begin = ann.get_lowest_localization()
elif tag is None or tag.get_typed_content() in separators:
begin = ann.get_highest_localization()
end = ann.get_highest_localization()
prev_ann = ann
if end > begin:
ann = self.__ann[-1]
end = ann.get_highest_localization()
intervals.create_annotation(sppasLocation(sppasInterval(begin, end)))
return intervals
export_unfilled
Create a tier with the unlabelled/unfilled intervals.
Only for tiers of type Interval.
It represents the "NOT tier", ie where this tier is not annotated.
IMPORTANT: Never tested with overlapped annotations,
actually not tested at all (but used in the plugin StatGroups).
Returns
View Source
def export_unfilled(self):
"""Create a tier with the unlabelled/unfilled intervals.
Only for tiers of type Interval.
It represents the "NOT tier", ie where this tier is not annotated.
IMPORTANT: Never tested with overlapped annotations,
actually not tested at all (but used in the plugin StatGroups).
:return: (sppasTier) or None
"""
if self.is_empty() is True:
return None
if self.is_interval() is False:
return None
intervals = self.export_to_intervals([])
not_intervals = sppasTier('NotIntervals')
if intervals.is_empty():
not_intervals.create_annotation(sppasLocation(sppasInterval(self.get_first_point(), self.get_last_point())))
return not_intervals
begin = self.__ann[0].get_lowest_localization()
prev_ann = intervals[0]
prev_begin = prev_ann.get_lowest_localization()
if prev_begin > begin:
not_intervals.create_annotation(sppasLocation(sppasInterval(begin, prev_begin)))
for i in range(1, len(intervals)):
prev_end = prev_ann.get_highest_localization()
ann = intervals[i]
begin = ann.get_lowest_localization()
if begin > prev_end:
not_intervals.create_annotation(sppasLocation(sppasInterval(prev_end, begin)))
prev_ann = ann
end = self.__ann[-1].get_highest_localization()
prev_end = intervals[-1].get_highest_localization()
if prev_end < end:
not_intervals.create_annotation(sppasLocation(sppasInterval(prev_end, end)))
return not_intervals
fit
Select then slice or extend annotations to fit in other tier.
Keep only the annotations of self that have some overlapping time
with the given other tier and slice the localization of
such selected annotations to exactly match those of the other tier.
Example:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
tier1: |a|b| |c| |d| |e| |f|
tier2: |w| |_x_| |y| |z|
tier1.fit(tier2) result is:
|a|b| |c| |d| |e|
tier2.fit(tier1) result is:
|w|w| |x| |x| |y|
Parameters
Returns
View Source
def fit(self, other):
"""Select then slice or extend annotations to fit in other tier.
Keep only the annotations of self that have some overlapping time
with the given other tier and slice the localization of
such selected annotations to exactly match those of the other tier.
Example:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
tier1: |_a_|_b_| |_c_| |_d_| |_e_| |f|
tier2: |_w_| |_______x_______| |_y_| |_z_|
tier1.fit(tier2) result is:
|a|b| |_c_| |_d_| |e|
tier2.fit(tier1) result is:
|w|w| |_x_| |_x_| |y|
:param other: (sppasTier)
:return: (sppasTier)
"""
ft = sppasTier(self.__name + '-inter-' + other.get_name())
for ann in other:
b = ann.get_lowest_localization()
e = ann.get_highest_localization()
found_anns = self.find(b, e, overlaps=True)
if len(found_anns) == 1:
labels = list()
for la in found_anns[0].get_labels():
labels.append(la.copy())
bf = max(b, found_anns[0].get_lowest_localization())
ef = min(e, found_anns[0].get_highest_localization())
ft.create_annotation(sppasLocation(sppasInterval(bf.copy(), ef.copy())), labels)
elif len(found_anns) > 1:
for i, ao in enumerate(found_anns):
labels = list()
for la in ao.get_labels():
labels.append(la.copy())
if ao is found_anns[0]:
bf = max(b, ao.get_lowest_localization())
location = sppasLocation(sppasInterval(bf.copy(), ao.get_highest_localization().copy()))
elif ao is found_anns[-1]:
ef = min(e, ao.get_highest_localization())
location = sppasLocation(sppasInterval(ao.get_lowest_localization().copy(), ef.copy()))
else:
location = ao.get_location().copy()
ft.create_annotation(location, labels)
return ft