docs: def relabel(label_list, projections):
      """
      Relabel an entire :py:class:`~audiomate.annotations.LabelList` using user-defined projections.
      Labels can be renamed, removed or overlapping labels can be flattened to a single label per segment.
  
      Each entry in the dictionary of projections represents a single projection that maps a combination of labels (key)
      to a single new label (value). The combination of labels to be mapped is a tuple of naturally sorted labels that
      apply to one or more segments simultaneously. By defining a special wildcard projection using `('**',)` is is not
      required to specify a projection for every single combination of labels.
  
      This method raises a :py:class:`~audiomate.corpus.utils.labellist.UnmappedLabelsException` if a projection for one
      or more combinations of labels is not defined.
  
      Args:
          label_list (audiomate.annotations.LabelList): The label list to relabel
          projections (dict): A dictionary that maps tuples of label combinations to string
                              labels.
      Returns:
          audiomate.annotations.LabelList: New label list with remapped labels
  
      Raises:
          UnmappedLabelsException: If a projection for one or more combinations of labels is not defined.
  
      Example:
          >>> projections = {
          ...     ('a',): 'a',
          ...     ('b',): 'b',
          ...     ('c',): 'c',
          ...     ('a', 'b',): 'a_b',
          ...     ('a', 'b', 'c',): 'a_b_c',
          ...     ('**',): 'b_c',
          ... }
          >>> label_list = annotations.LabelList(labels=[
          ...     annotations.Label('a', 3.2, 4.5),
          ...     annotations.Label('b', 4.0, 4.9),
          ...     annotations.Label('c', 4.2, 5.1)
          ... ])
          >>> ll = relabel(label_list, projections)
          >>> [l.value for l in ll]
          ['a', 'a_b', 'a_b_c', 'b_c', 'c']
      """
      unmapped_combinations = find_missing_projections(label_list, projections)
      if len(unmapped_combinations) > 0:
          raise UnmappedLabelsException('Unmapped combinations: {}'.format(unmapped_combinations))
  
      new_labels = []
      for labeled_segment in label_list.ranges():
          combination = tuple(sorted([label.value for label in labeled_segment[2]]))
          label_mapping = projections[combination] if combination in projections else projections[WILDCARD_COMBINATION]
  
          if label_mapping == '':
              continue
  
          new_labels.append(annotations.Label(label_mapping, labeled_segment[0], labeled_segment[1]))
  
      return annotations.LabelList(idx=label_list.idx, labels=new_labels)