o
    piz                     @   s   d Z ddlZddlZddlmZ ddlmZmZmZm	Z	m
Z
mZmZmZmZmZmZmZ ddlZddlmZ ddlmZmZmZ ddlmZmZ dd	lmZ dd
lm Z  ddl!m"Z"m#Z# ddl$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z* errddl+Z,G dd dZ-dS )a	  
##########
Annotation
##########

.. plot:: pyplots/annotation.py

:class:`pyannote.core.Annotation` instances are ordered sets of non-empty
tracks:

  - ordered, because segments are sorted by start time (and end time in case of tie)
  - set, because one cannot add twice the same track
  - non-empty, because one cannot add empty track

A track is a (support, name) pair where `support` is a Segment instance,
and `name` is an additional identifier so that it is possible to add multiple
tracks with the same support.

To define the annotation depicted above:

.. code-block:: ipython

    In [1]: from pyannote.core import Annotation, Segment

    In [6]: annotation = Annotation()
       ...: annotation[Segment(1, 5)] = 'Carol'
       ...: annotation[Segment(6, 8)] = 'Bob'
       ...: annotation[Segment(12, 18)] = 'Carol'
       ...: annotation[Segment(7, 20)] = 'Alice'
       ...:

which is actually a shortcut for

.. code-block:: ipython

    In [6]: annotation = Annotation()
       ...: annotation[Segment(1, 5), '_'] = 'Carol'
       ...: annotation[Segment(6, 8), '_'] = 'Bob'
       ...: annotation[Segment(12, 18), '_'] = 'Carol'
       ...: annotation[Segment(7, 20), '_'] = 'Alice'
       ...:

where all tracks share the same (default) name ``'_'``.

In case two tracks share the same support, use a different track name:

.. code-block:: ipython

    In [6]: annotation = Annotation(uri='my_video_file', modality='speaker')
       ...: annotation[Segment(1, 5), 1] = 'Carol'  # track name = 1
       ...: annotation[Segment(1, 5), 2] = 'Bob'    # track name = 2
       ...: annotation[Segment(12, 18)] = 'Carol'
       ...:

The track name does not have to be unique over the whole set of tracks.

.. note::

  The optional *uri* and *modality* keywords argument can be used to remember
  which document and modality (e.g. speaker or face) it describes.

Several convenient methods are available. Here are a few examples:

.. code-block:: ipython

  In [9]: annotation.labels()   # sorted list of labels
  Out[9]: ['Bob', 'Carol']

  In [10]: annotation.chart()   # label duration chart
  Out[10]: [('Carol', 10), ('Bob', 4)]

  In [11]: list(annotation.itertracks())
  Out[11]: [(<Segment(1, 5)>, 1), (<Segment(1, 5)>, 2), (<Segment(12, 18)>, u'_')]

  In [12]: annotation.label_timeline('Carol')
  Out[12]: <Timeline(uri=my_video_file, segments=[<Segment(1, 5)>, <Segment(12, 18)>])>

See :class:`pyannote.core.Annotation` for the complete reference.
    N)defaultdict)HashableOptionalDictUnionIterableListSetTextIOTupleIteratorTextTYPE_CHECKING)
SortedDict   )PYANNOTE_SEGMENTPYANNOTE_TRACKPYANNOTE_LABEL)SegmentSlidingWindow)Timeline)SlidingWindowFeature)string_generatorint_generator)LabelKeySupportLabelGenerator	TrackNameCropModec                   @   s  e Zd ZdZe		ddddee dee dd fdd	Zddee dee fd
dZe	dd Z
e
jdefddZ
dd Zdd Zdd Zdd Zdeeeef  fddZdd Z	ddedeeeeef eeeef f  fddZdd  Zdd"edefd#d$Zdd&d'Zdd(d)Zd*eeef fd+d,Zdee  fd-d.Z!de fd/d0Z"d1e#fd2d3Z$dee  fd4d5Z%de fd6d7Z&d1e#fd8d9Z'dd;e(d<e)dd fd=d>Z*	:dd?e(d<e)dd fd@dAZ+ddBee,e  ddCfdDdEZ-dFede.e fdGdHZ/dFedIedefdJdKZ0ddLdMZ1		ddFedNee dOee defdPdQZ2dRdS Z3dTe4fdUdVZ5dTe4defdWdXZ6dTe4dYefdZd[Z7dd\d]Z8de9e fd^d_Z:	!ddFed`edee.e e9e f fdadbZ;ddBe,e dcedd fdddeZ<ddfd d"edd fdgdhZ=ddYed"edefdidjZ>dYedefdkdlZ?dYede@fdmdnZAddoede9eee@f  fdpdqZBdd;ee( dee fdrdsZCddueDdd fdvdwZE		t	!ddxeeF dueDd"edd fdydzZGddueDdd fd{d|ZHdd~e@dd fddZId%d deeeeef eeef f  fddZJd%d deKjLfddZM				dd;ee dee@eNf dBee9eO  dee@ fddZPe		ddeeeeef  dee dee dd fddZQdd ZRdS )
Annotationa  Annotation

    Parameters
    ----------
    uri : string, optional
        name of annotated resource (e.g. audio or video file)
    modality : string, optional
        name of annotated modality

    Returns
    -------
    annotation : Annotation
        New annotation

    Ndfzpd.DataFrameurimodalityreturnc                 C   s$   |t ttg }t|jdd||S )NF)index)r   r   r   r    from_records
itertuples)clsr!   r"   r#    r)   L/home/ubuntu/.local/lib/python3.10/site-packages/pyannote/core/annotation.pyfrom_df   s   zAnnotation.from_dfc                 C   s0   || _ || _t | _i | _i | _d | _d| _d S )NT)_urir#   r   _tracks_labels_labelNeedsUpdate	_timeline_timelineNeedsUpdate)selfr"   r#   r)   r)   r*   __init__   s   
zAnnotation.__init__c                 C   s   | j S N)r,   r2   r)   r)   r*   r"      s   zAnnotation.uric                 C   s>   |   D ]}| j|dd}||_q| jdd}||_|| _d S )NFcopy)labelslabel_timeliner"   get_timeliner,   )r2   r"   labeltimeliner)   r)   r*   r"      s   
c                 C   s   t dd | j D }dd |D }| jddD ]\}}}||v r)|| | q|D ]&}|| rDt|| | jd| j|< d| j|< q,| j|d  | j|d  q,d S )	Nc                 s   s    | ]	\}}|r|V  qd S r4   r)   ).0r;   updater)   r)   r*   	<genexpr>   s    
z+Annotation._updateLabels.<locals>.<genexpr>c                 S   s   i | ]}|g qS r)   r)   r=   r;   r)   r)   r*   
<dictcomp>       z,Annotation._updateLabels.<locals>.<dictcomp>Tyield_labelsegmentsr"   F)	setr/   items
itertracksappendr   r"   r.   pop)r2   r>   	_segmentssegmenttrackr;   r)   r)   r*   _updateLabels   s   zAnnotation._updateLabelsc                 C   
   t | jS )zhNumber of segments

        >>> len(annotation)  # annotation contains three segments
        3
        lenr-   r5   r)   r)   r*   __len__   s   
zAnnotation.__len__c                 C   s   |   S r4   )__bool__r5   r)   r)   r*   __nonzero__   s   zAnnotation.__nonzero__c                 C   s   t | jdkS )zEmptiness

        >>> if annotation:
        ...    # annotation is not empty
        ... else:
        ...    # annotation is empty
        r   rQ   r5   r)   r)   r*   rT      s   zAnnotation.__bool__c                 c   s(    | j ddD ]
\}}}||fV  qdS )zIterate over (segment, label) pairs in chronological order

        Examples
        --------

        >>> for segment, label in annotation:
        ...     # do something with the segment and its label
        TrC   N)rI   )r2   rM   _r;   r)   r)   r*   __iter__   s   	zAnnotation.__iter__c                 C   rP   )a  Iterate over segments (in chronological order)

        >>> for segment in annotation.itersegments():
        ...     # do something with the segment

        See also
        --------
        :class:`pyannote.core.Segment` describes how segments are sorted.
        )iterr-   r5   r)   r)   r*   itersegments  s   

zAnnotation.itersegmentsFrD   c                 c   sT    | j  D ]!\}}t| dd dD ]\}}|r!|||fV  q||fV  qqdS )aN  Iterate over tracks (in chronological order)

        Parameters
        ----------
        yield_label : bool, optional
            When True, yield (segment, track, label) tuples, such that
            annotation[segment, track] == label. Defaults to yielding
            (segment, track) tuple.

        Examples
        --------

        >>> for segment, track in annotation.itertracks():
        ...     # do something with the track

        >>> for segment, track, label in annotation.itertracks(yield_label=True):
        ...     # do something with the track and its label
        c                 S   s   t | d t | d fS )Nr   r   )str)tlr)   r)   r*   <lambda>*  s    z'Annotation.itertracks.<locals>.<lambda>keyN)r-   rH   sorted)r2   rD   rM   tracksrN   lblr)   r)   r*   rI     s   zAnnotation.itertracksc                 C   s   t | j| jd| _d| _d S )NrE   F)r   r-   r"   r0   r1   r5   r)   r)   r*   _updateTimeline1  s   
zAnnotation._updateTimelineTr7   c                 C   s"   | j r|   |r| j S | jS )aM  Get timeline made of all annotated segments

        Parameters
        ----------
        copy : bool, optional
            Defaults (True) to returning a copy of the internal timeline.
            Set to False to return the actual internal timeline (faster).

        Returns
        -------
        timeline : Timeline
            Timeline made of all annotated segments.

        Note
        ----
        In case copy is set to False, be careful **not** to modify the returned
        timeline, as it may lead to weird subsequent behavior of the annotation
        instance.

        )r1   rb   r0   r7   )r2   r7   r)   r)   r*   r:   5  s
   
zAnnotation.get_timelineotherc                 C   .   t | jdd|jdd}tdd |D S )zEquality

        >>> annotation == other

        Two annotations are equal if and only if their tracks and associated
        labels are equal.
        TrC   c                 s   s    | ]	\}}||kV  qd S r4   r)   r=   t1t2r)   r)   r*   r?   [      z$Annotation.__eq__.<locals>.<genexpr>)	itertoolszip_longestrI   allr2   rc   pairOfTracksr)   r)   r*   __eq__P  s   zAnnotation.__eq__c                 C   rd   )
InequalityTrC   c                 s   s    | ]	\}}||kV  qd S r4   r)   re   r)   r)   r*   r?   c  rh   z$Annotation.__ne__.<locals>.<genexpr>)ri   rj   rI   anyrl   r)   r)   r*   __ne__]  s   zAnnotation.__ne__includedc                 C   s   || j ddv S )a  Inclusion

        Check whether every segment of `included` does exist in annotation.

        Parameters
        ----------
        included : Segment or Timeline
            Segment or timeline being checked for inclusion

        Returns
        -------
        contains : bool
            True if every segment in `included` exists in timeline,
            False otherwise

        Fr6   )r:   )r2   rr   r)   r)   r*   __contains__e  s   zAnnotation.__contains__c              
   c   s    | j r| j nd}t|trd|v rd| d}t|| jddD ],\}}}t|tr:d|v r:d| d}t|d| d	|jd
d|jd
d| d	V  q"dS )zGenerate lines for an RTTM file for this annotation

        Returns
        -------
        iterator: Iterator[str]
            An iterator over RTTM text lines
        z<NA> zSSpace-separated RTTM file format does not allow file URIs containing spaces (got: "").TrC   zPSpace-separated RTTM file format does not allow labels containing spaces (got: "zSPEAKER z 1 .3fz <NA> <NA> z <NA> <NA>
N)r"   
isinstancer   
ValueErrorrI   startduration)r2   r"   msgrM   rV   r;   r)   r)   r*   
_iter_rttmx  s(   zAnnotation._iter_rttmc                 C      d dd |  D S )zSerialize annotation as a string using RTTM format

        Returns
        -------
        serialized: str
            RTTM string
         c                 S      g | ]}|qS r)   r)   r=   liner)   r)   r*   
<listcomp>      z&Annotation.to_rttm.<locals>.<listcomp>)joinr|   r5   r)   r)   r*   to_rttm     zAnnotation.to_rttmfilec                 C      |   D ]}|| qdS )zDump annotation to file using RTTM format

        Parameters
        ----------
        file : file object

        Usage
        -----
        >>> with open('file.rttm', 'w') as file:
        ...     annotation.write_rttm(file)
        N)r|   writer2   r   r   r)   r)   r*   
write_rttm     zAnnotation.write_rttmc                 c   sj    | j ddD ]+\}}}t|trd|v rd| d}t||jdd|j|j dd| dV  qdS )	zGenerate lines for a LAB file for this annotation

        Returns
        -------
        iterator: Iterator[str]
            An iterator over LAB text lines
        TrC   rt   zOSpace-separated LAB file format does not allow labels containing spaces (got: "ru   rv   
N)rI   rw   r   rx   ry   rz   )r2   rM   rV   r;   r{   r)   r)   r*   	_iter_lab  s   (zAnnotation._iter_labc                 C   r}   )zSerialize annotation as a string using LAB format

        Returns
        -------
        serialized: str
            LAB string
        r~   c                 S   r   r)   r)   r   r)   r)   r*   r     r   z%Annotation.to_lab.<locals>.<listcomp>)r   r   r5   r)   r)   r*   to_lab  r   zAnnotation.to_labc                 C   r   )zDump annotation to file using LAB format

        Parameters
        ----------
        file : file object

        Usage
        -----
        >>> with open('file.lab', 'w') as file:
        ...     annotation.write_lab(file)
        N)r   r   r   r)   r)   r*   	write_lab  r   zAnnotation.write_labintersectionsupportmodec                 C   s  t |trt|g| jd}| j||dS t |tr| }| j| j| jd}|dkrmi }tg }| j	dd
|D ]\}}t| j| }|||< ||  q9t||_dd |D |_d	d |D |_d
|_d|_|S |dkri }tg }| j	dd
|D ]\}}	||	vrqt| j| }|||< ||  qt||_dd |D |_dd |D |_d
|_d|_|S |dkr| j	dd
|D ]!\}}	||	@ }
| j|  D ]\}}|j|
|d}|||
|f< qq|S td| dS )a  Crop annotation to new support

        Parameters
        ----------
        support : Segment or Timeline
            If `support` is a `Timeline`, its support is used.
        mode : {'strict', 'loose', 'intersection'}, optional
            Controls how segments that are not fully included in `support` are
            handled. 'strict' mode only keeps fully included segments. 'loose'
            mode keeps any intersecting segment. 'intersection' mode keeps any
            intersecting segment but replace them by their actual intersection.

        Returns
        -------
        cropped : Annotation
            Cropped annotation

        Note
        ----
        In 'intersection' mode, the best is done to keep the track names
        unchanged. However, in some cases where two original segments are
        cropped into the same resulting segments, conflicting track names are
        modified to make sure no track is lost.

        rE   r   r"   r#   looseFr6   c                 S      i | ]}|d qS Tr)   r@   r)   r)   r*   rA   	  rB   z#Annotation.crop.<locals>.<dictcomp>c                 S   r   r4   r)   r@   r)   r)   r*   rA   
  rB   TNstrictc                 S   r   r   r)   r@   r)   r)   r*   rA   #  rB   c                 S   r   r4   r)   r@   r)   r)   r*   rA   $  rB   r   )	candidatezunsupported mode: '%s')rw   r   r   r"   cropr   	__class__r#   rG   r:   co_iterdictr-   r>   valuesr   r/   r.   r1   r0   rH   	new_trackNotImplementedError)r2   r   r   croppedr-   r.   rM   rV   r`   other_segmentr   rN   r;   r)   r)   r*   r     s`   



zAnnotation.cropremovedc                 C   s`   t |tr
t|g}t|   g| jd}|j|d}|dkr#d}n|dkr)d}| j||dS )a  Remove segments that overlap `removed` support.

        A simple illustration:

        .. code-block:: text
        
            A |------|    |------|                      #
            B                  |----------|             # self
            C |--------------|              |------|    #

                                                        #
              |-------|  |-----------|                  # removed
                                                        #
              
            A                                           #  
            B                        |---|              # mode="intersection"
            C         |--|                  |------|    # 
                                                         
            A                                           #
            B                                           # mode="loose"
            C                               |------|    # 

            A |------|                                  #
            B                  |----------|             # mode="strict"
            C |--------------|              |------|    #

        Parameters
        ----------
        removed : Segment or Timeline
            If `support` is a `Timeline`, its support is used.
        mode : {'strict', 'loose', 'intersection'}, optional
            Controls how segments that are not fully included in `removed` are
            handled. 'strict' mode only removes fully included segments. 'loose'
            mode removes any intersecting segment. 'intersection' mode removes
            the overlapping part of any intersecting segment.

        Returns
        -------
        extruded : Annotation
            Extruded annotation

        Note
        ----
        In 'intersection' mode, the best is done to keep the track names
        unchanged. However, in some cases where two original segments are
        cropped into the same resulting segments, conflicting track names are
        modified to make sure no track is lost.

        r"   )r   r   r   r   )rw   r   r   r:   extentr"   gapsr   )r2   r   r   	extent_tltruncating_supportr)   r)   r*   extrude;  s   
4
zAnnotation.extruder8   r   c                 C   sl   |r|  |}n| }t|jd}||D ]\\}}\}}| ||f | ||f kr*q|||@  q| S )a  Get overlapping parts of the annotation.

        A simple illustration:

        .. code-block:: text

            annotation
            A |------|    |------|      |----|
            B  |--|    |-----|      |----------|
            C |--------------|      |------|

            annotation.get_overlap()
              |------| |-----|      |--------|

            annotation.get_overlap(for_labels=["A", "B"])
               |--|       |--|          |----|

        Parameters
        ----------
        labels : optional list of labels
            Labels for which to consider the overlap

        Returns
        -------
        overlap : `pyannote.core.Timeline`
           Timeline of the overlaps.
       r   )subsetr   r"   r   addr   )r2   r8   
annotationoverlaps_tls1rf   s2rg   r)   r)   r*   get_overlap{  s   zAnnotation.get_overlaprM   c                 C   s   t | j|i  S )a  Query tracks by segment

        Parameters
        ----------
        segment : Segment
            Query

        Returns
        -------
        tracks : set
            Set of tracks

        Note
        ----
        This will return an empty set if segment does not exist.
        )rG   r-   getkeys)r2   rM   r)   r)   r*   
get_tracks  s   zAnnotation.get_tracksrN   c                 C   s   || j |i v S )a  Check whether a given track exists

        Parameters
        ----------
        segment : Segment
            Query segment
        track :
            Query track

        Returns
        -------
        exists : bool
            True if track exists for segment
        )r-   r   )r2   rM   rN   r)   r)   r*   	has_track  s   zAnnotation.has_trackc                 C   s   | j | j| jd}g tg }}| j D ]\}}||  ||t	|f qt
||_dd |D |_dd |D |_d|_d|_|S )zGet a copy of the annotation

        Returns
        -------
        annotation : Annotation
            Copy of the annotation
        r   c                 S   r   r4   r)   r@   r)   r)   r*   rA     rB   z#Annotation.copy.<locals>.<dictcomp>c                 S   r   r   r)   r@   r)   r)   r*   rA     rB   NT)r   r"   r#   rG   r-   rH   r>   r   rJ   r   r   r.   r/   r0   r1   )r2   copiedr-   r.   r^   valuer)   r)   r*   r7     s   

zAnnotation.copyr   prefixc                 C   sj   t | j|i }|dur||vr|S |du rd}d}d||f |v r/|d7 }d||f |v s#d||f S )a1  Generate a new track name for given segment

        Ensures that the returned track name does not already
        exist for the given segment.

        Parameters
        ----------
        segment : Segment
            Segment for which a new track name is generated.
        candidate : any valid track name, optional
            When provided, try this candidate name first.
        prefix : str, optional
            Track name prefix. Defaults to the empty string ''.

        Returns
        -------
        name : str
            New track name
        Nr~   r   z%s%dr   )rG   r-   r   )r2   rM   r   r   existing_trackscountr)   r)   r*   r     s   zAnnotation.new_trackc                 C   s   d dd | jddD S )zHuman-friendly representationr   c                 S   s    g | ]\}}}d |||f qS )z%s %s %sr)   )r=   stlr)   r)   r*   r     s     z&Annotation.__str__.<locals>.<listcomp>TrC   )r   rI   r5   r)   r)   r*   __str__  s   zAnnotation.__str__r^   c                 C   s   t |tr| j|}d| _| D ]	\}}d| j|< qdS t |trMt|dkrM| j|d  }||d }d| j|< |sK| j|d  d| _dS dS t	d)zDelete one track

        >>> del annotation[segment, track]

        Delete all tracks of a segment

        >>> del annotation[segment]
        T   r   r   z:Deletion only works with Segment or (Segment, track) keys.N)
rw   r   r-   rK   r1   rH   r/   tuplerR   r   )r2   r^   r`   rN   r;   r)   r)   r*   __delitem__  s"   


zAnnotation.__delitem__c                 C   s(   t |tr	|df}| j|d  |d  S )zGet track label

        >>> label = annotation[segment, track]

        Note
        ----
        ``annotation[segment]`` is equivalent to ``annotation[segment, '_']``

        rV   r   r   )rw   r   r-   )r2   r^   r)   r)   r*   __getitem__R  s   
zAnnotation.__getitem__r;   c                 C   s~   t |tr	|df}|\}}|sdS || jvri | j|< d| _|| j| v r1| j| | }d| j|< || j| |< d| j|< dS )a  Add new or update existing track

        >>> annotation[segment, track] = label

        If (segment, track) does not exist, it is added.
        If (segment, track) already exists, it is updated.

        Note
        ----
        ``annotation[segment] = label`` is equivalent to ``annotation[segment, '_'] = label``

        Note
        ----
        If `segment` is empty, it does nothing.
        rV   NT)rw   r   r-   r1   r/   )r2   r^   r;   rM   rN   	old_labelr)   r)   r*   __setitem__c  s   



zAnnotation.__setitem__c                 C   s   | j | j| jdS )zReturn an empty copy

        Returns
        -------
        empty : Annotation
            Empty annotation using the same 'uri' and 'modality' attributes.

        r   )r   r"   r#   r5   r)   r)   r*   empty  s   	zAnnotation.emptyc                 C   s.   t dd | j D r|   t| jtdS )z{Get sorted list of labels

        Returns
        -------
        labels : list
            Sorted list of labels
        c                 S   r   r)   r)   )r=   lnur)   r)   r*   r     r   z%Annotation.labels.<locals>.<listcomp>r]   )rp   r/   r   rO   r_   r.   rZ   r5   r)   r)   r*   r8     s   zAnnotation.labelsuniquec                 C   s&   | j |i  }|rt|S t|S )aZ  Query labels by segment

        Parameters
        ----------
        segment : Segment
            Query
        unique : bool, optional
            When False, return the list of (possibly repeated) labels.
            Defaults to returning the set of labels.

        Returns
        -------
        labels : set or list
            Set (resp. list) of labels for `segment` if it exists, empty set (resp. list) otherwise
            if unique (resp. if not unique).

        Examples
        --------
        >>> annotation = Annotation()
        >>> segment = Segment(0, 2)
        >>> annotation[segment, 'speaker1'] = 'Bernard'
        >>> annotation[segment, 'speaker2'] = 'John'
        >>> print sorted(annotation.get_labels(segment))
        set(['Bernard', 'John'])
        >>> print annotation.get_labels(Segment(1, 2))
        set([])

        )r-   r   r   rG   list)r2   rM   r   r8   r)   r)   r*   
get_labels  s    zAnnotation.get_labelsinvertc           	         s   t   |rt |     n t |  @  | j| j| jd}i t g }}| j D ]\}} fdd| D }|rH|||< ||  q,t	||_dd |D |_
dd |D |_d|_d|_|S )aH  Filter annotation by labels

        Parameters
        ----------
        labels : iterable
            List of filtered labels
        invert : bool, optional
            If invert is True, extract all but requested labels

        Returns
        -------
        filtered : Annotation
            Filtered annotation
        r   c                    s   i | ]\}}| v r||qS r)   r)   r=   rN   r;   r8   r)   r*   rA     s    z%Annotation.subset.<locals>.<dictcomp>c                 S   r   r   r)   r@   r)   r)   r*   rA     rB   c                 S   r   r4   r)   r@   r)   r)   r*   rA     rB   TN)rG   r8   r   r"   r#   r-   rH   r>   r   r   r/   r.   r1   r0   )	r2   r8   r   subr-   r.   rM   r`   
sub_tracksr)   r   r*   r     s(   

zAnnotation.subsetr   c                 C   s8   |r|   n| }|jddD ]\}}}||||f< q|S )a  Add every track of an existing annotation (in place)

        Parameters
        ----------
        annotation : Annotation
            Annotation whose tracks are being added
        copy : bool, optional
            Return a copy of the annotation. Defaults to updating the
            annotation in-place.

        Returns
        -------
        self : Annotation
            Updated annotation

        Note
        ----
        Existing tracks are updated with the new label.
        TrC   )r7   rI   )r2   r   r7   resultrM   rN   r;   r)   r)   r*   r>     s   zAnnotation.updatec                 C   sF   ||   vrt| jdS | j| r|   |r| j|  S | j| S )a  Query segments by label

        Parameters
        ----------
        label : object
            Query
        copy : bool, optional
            Defaults (True) to returning a copy of the internal timeline.
            Set to False to return the actual internal timeline (faster).

        Returns
        -------
        timeline : Timeline
            Timeline made of all segments for which at least one track is
            annotated as label

        Note
        ----
        If label does not exist, this will return an empty timeline.

        Note
        ----
        In case copy is set to False, be careful **not** to modify the returned
        timeline, as it may lead to weird subsequent behavior of the annotation
        instance.

        r   )r8   r   r"   r/   rO   r.   r7   )r2   r;   r7   r)   r)   r*   r9     s   

zAnnotation.label_timelinec                 C      | j |dd S )a  Label support

        Equivalent to ``Annotation.label_timeline(label).support()``

        Parameters
        ----------
        label : object
            Query

        Returns
        -------
        support : Timeline
            Label support

        See also
        --------
        :func:`~pyannote.core.Annotation.label_timeline`
        :func:`~pyannote.core.Timeline.support`

        Fr6   )r9   r   r2   r;   r)   r)   r*   label_support;  s   zAnnotation.label_supportc                 C   r   )a  Label duration

        Equivalent to ``Annotation.label_timeline(label).duration()``

        Parameters
        ----------
        label : object
            Query

        Returns
        -------
        duration : float
            Duration, in seconds.

        See also
        --------
        :func:`~pyannote.core.Annotation.label_timeline`
        :func:`~pyannote.core.Timeline.duration`

        Fr6   )r9   rz   r   r)   r)   r*   label_durationR  s   zAnnotation.label_durationpercentc                    sR   t  fdd  D dd dd}|r'tdd |D fd	d|D }|S )
a  Get labels chart (from longest to shortest duration)

        Parameters
        ----------
        percent : bool, optional
            Return list of (label, percentage) tuples.
            Defaults to returning list of (label, duration) tuples.

        Returns
        -------
        chart : list
            List of (label, duration), sorted by duration in decreasing order.
        c                 3       | ]
}|  |fV  qd S r4   r   )r=   Lr5   r)   r*   r?   z      z#Annotation.chart.<locals>.<genexpr>c                 S      | d S Nr   r)   xr)   r)   r*   r\   {      z"Annotation.chart.<locals>.<lambda>T)r^   reversec                 S   s   g | ]\}}|qS r)   r)   )r=   rV   rz   r)   r)   r*   r     s    z$Annotation.chart.<locals>.<listcomp>c                    s   g | ]
\}}||  fqS r)   r)   )r=   r;   rz   )totalr)   r*   r     s    )r_   r8   npsum)r2   r   chartr)   )r2   r   r*   r   j  s   zAnnotation.chartc                    sH   |  |dur j |dd  sdS t fdd  D dd dd	 S )
aM  Get label with longest duration

        Parameters
        ----------
        support : Segment or Timeline, optional
            Find label with longest duration within provided support.
            Defaults to whole extent.

        Returns
        -------
        label : any existing label or None
            Label with longest intersection

        Examples
        --------
        >>> annotation = Annotation(modality='speaker')
        >>> annotation[Segment(0, 10), 'speaker1'] = 'Alice'
        >>> annotation[Segment(8, 20), 'speaker1'] = 'Bob'
        >>> print "%s is such a talker!" % annotation.argmax()
        Bob is such a talker!
        >>> segment = Segment(22, 23)
        >>> if not annotation.argmax(support):
        ...    print "No label intersecting %s" % segment
        No label intersection [22 --> 23]

        Nr   r   c                 3   r   r4   r   )r=   rV   r   r)   r*   r?     r   z$Annotation.argmax.<locals>.<genexpr>c                 S   r   r   r)   r   r)   r)   r*   r\     r   z#Annotation.argmax.<locals>.<lambda>r]   r   )r   maxr8   )r2   r   r)   r   r*   argmax  s   zAnnotation.argmaxstring	generatorc                 C   s\   | j | j| jd}|dkrt }n|dkrt }| jddD ]\}}}|||t|f< q|S )a  Rename all tracks

        Parameters
        ----------
        generator : 'string', 'int', or iterable, optional
            If 'string' (default) rename tracks to 'A', 'B', 'C', etc.
            If 'int', rename tracks to 0, 1, 2, etc.
            If iterable, use it to generate track names.

        Returns
        -------
        renamed : Annotation
            Copy of the original annotation where tracks are renamed.

        Example
        -------
        >>> annotation = Annotation()
        >>> annotation[Segment(0, 1), 'a'] = 'a'
        >>> annotation[Segment(0, 1), 'b'] = 'b'
        >>> annotation[Segment(1, 2), 'a'] = 'a'
        >>> annotation[Segment(1, 3), 'c'] = 'c'
        >>> print(annotation)
        [ 00:00:00.000 -->  00:00:01.000] a a
        [ 00:00:00.000 -->  00:00:01.000] b b
        [ 00:00:01.000 -->  00:00:02.000] a a
        [ 00:00:01.000 -->  00:00:03.000] c c
        >>> print(annotation.rename_tracks(generator='int'))
        [ 00:00:00.000 -->  00:00:01.000] 0 a
        [ 00:00:00.000 -->  00:00:01.000] 1 b
        [ 00:00:01.000 -->  00:00:02.000] 2 a
        [ 00:00:01.000 -->  00:00:03.000] 3 c
        r   r   intTrC   )r   r"   r#   r   r   rI   next)r2   r   renamedr   rV   r;   r)   r)   r*   rename_tracks  s   "zAnnotation.rename_tracksmappingc           
         s   du r dkrt   n dkrt   fdd|  D |r$|  n| } D ]\}}d|j|< d|j|< q*| j D ]\}}fdd| D }	|	|j|< q>|S )a/  Rename labels

        Parameters
        ----------
        mapping : dict, optional
            {old_name: new_name} mapping dictionary.
        generator : 'string', 'int' or iterable, optional
            If 'string' (default) rename label to 'A', 'B', 'C', ... If 'int',
            rename to 0, 1, 2, etc. If iterable, use it to generate labels.
        copy : bool, optional
            Set to True to return a copy of the annotation. Set to False to
            update the annotation in-place. Defaults to True.

        Returns
        -------
        renamed : Annotation
            Annotation where labels have been renamed

        Note
        ----
        Unmapped labels are kept unchanged.

        Note
        ----
        Parameter `generator` has no effect when `mapping` is provided.

        Nr   r   c                    s   i | ]}|t  qS r)   )r   r@   )r   r)   r*   rA         z,Annotation.rename_labels.<locals>.<dictcomp>Tc                    s   i | ]\}}|  ||qS r)   )r   r   )r   r)   r*   rA     s    )r   r   r8   r7   rH   r/   r-   )
r2   r   r   r7   r   r   	new_labelrM   r`   
new_tracksr)   )r   r   r*   rename_labels  s    "

zAnnotation.rename_labelsc                 C   sR   |dkrt  }n|dkrt }|  }| jddD ]\}}}t||||f< q|S )a  Relabel tracks

        Create a new annotation where each track has a unique label.

        Parameters
        ----------
        generator : 'string', 'int' or iterable, optional
            If 'string' (default) relabel tracks to 'A', 'B', 'C', ... If 'int'
            relabel to 0, 1, 2, ... If iterable, use it to generate labels.

        Returns
        -------
        renamed : Annotation
            New annotation with relabeled tracks.
        r   r   TrC   )r   r   r   rI   r   )r2   r   	relabeledr   r   rV   r)   r)   r*   relabel_tracks  s   zAnnotation.relabel_tracks        collarc                 C   sV   t  }|  }|  D ]}| j|dd}||}| D ]
}|||t|f< qq|S )a  Annotation support

        The support of an annotation is an annotation where contiguous tracks
        with same label are merged into one unique covering track.

        A picture is worth a thousand words::

            collar
            |---|

            annotation
            |--A--| |--A--|     |-B-|
              |-B-|    |--C--|     |----B-----|

            annotation.support(collar)
            |------A------|     |------B------|
              |-B-|    |--C--|

        Parameters
        ----------
        collar : float, optional
            Merge tracks with same label and separated by less than `collar`
            seconds. This is why 'A' tracks are merged in above figure.
            Defaults to 0.

        Returns
        -------
        support : Annotation
            Annotation support

        Note
        ----
        Track names are lost in the process.
        Tr6   )r   r   r8   r9   r   r   )r2   r   r   r   r;   r<   rM   r)   r)   r*   r   /  s   $
zAnnotation.supportc           
      c   s~    | j dd}|j dd}||D ]*\}}t| |td}t||td}t||D ]\}}	||f||	ffV  q.qdS )a  Iterate over pairs of intersecting tracks

        Parameters
        ----------
        other : Annotation
            Second annotation

        Returns
        -------
        iterable : (Segment, object), (Segment, object) iterable
            Yields pairs of intersecting tracks, in chronological (then
            alphabetical) order.

        See also
        --------
        :func:`~pyannote.core.Timeline.co_iter`

        Fr6   r]   N)r:   r   r_   r   rZ   ri   product)
r2   rc   r<   other_timeliner   Sr`   other_tracksr   Tr)   r)   r*   r   f  s   zAnnotation.co_iterc                 C   s   t |ts	td|  }| }dd t|D }dd t|D }tt|t|f}| |D ]'\\}}\}	}
|| ||f  }|||	|
f  }||	@ j	}|||f  |7  < q3|S )a  Cooccurrence (or confusion) matrix

        >>> matrix = annotation * other

        Parameters
        ----------
        other : Annotation
            Second annotation

        Returns
        -------
        cooccurrence : (n_self, n_other) np.ndarray
            Cooccurrence matrix where `n_self` (resp. `n_other`) is the number
            of labels in `self` (resp. `other`).
        zCcomputing cooccurrence matrix only works with Annotation instances.c                 S      i | ]\}}||qS r)   r)   )r=   ir;   r)   r)   r*   rA     r   z&Annotation.__mul__.<locals>.<dictcomp>c                 S   r   r)   r)   )r=   jr;   r)   r)   r*   rA     r   )
rw   r    	TypeErrorr8   	enumerater   zerosrR   r   rz   )r2   rc   i_labelsj_labelsIJmatrixrM   rN   r   other_trackr   r   rz   r)   r)   r*   __mul__  s   

zAnnotation.__mul__{Gz?
resolutionrz   c                 C   s.  |du r
|    }|\}}| j|dd}|du r| }t|tr,t||j|jd}nt|||d}||}|du rF||}	|	| }
n	t	t
||j }
tj|
t|ftjd}t|D ](\}}||}|j|dddD ]\}}|td	|t||
|f  d
7  < qpq_tj|d
|d}t|||dS )a  Discretize
        
        Parameters
        ----------
        support : Segment, optional
            Part of annotation to discretize. 
            Defaults to annotation full extent.
        resolution : float or SlidingWindow, optional
            Defaults to 10ms frames.
        labels : list of labels, optional
            Defaults to self.labels()
        duration : float, optional
            Overrides support duration and ensures that the number of
            returned frames is fixed (which might otherwise not be the case
            because of rounding errors).

        Returns
        -------
        discretized : SlidingWindowFeature
            (num_frames, num_labels)-shaped binary features.
        Nr   r   )ry   steprz   )dtypecenterT)r   return_rangesr   r   )outr   )r:   r   r   r8   rw   r   r	  rz   closest_framer   roundr   r   rR   uint8r   r9   r   minminimumr   )r2   r   r  r8   rz   
start_timeend_timer   start_frame	end_frame
num_framesdatakr;   rF   ry   stopr)   r)   r*   
discretize  s8   





&zAnnotation.discretizerecordsc           
      C   s|   | ||d}t t}t }|D ]\}}}	|	|| |< ||	 qt||_dd |D |_dd |jD |_d|_d|_	|S )a  Annotation

        Parameters
        ----------
        records : iterator of tuples
            (segment, track, label) tuples
        uri : string, optional
            name of annotated resource (e.g. audio or video file)
        modality : string, optional
            name of annotated modality

        Returns
        -------
        annotation : Annotation
            New annotation

        r   c                 S   r   r4   r)   r@   r)   r)   r*   rA   
  rB   z+Annotation.from_records.<locals>.<dictcomp>c                 S   r   r   r)   r@   r)   r)   r*   rA     rB   NT)
r   r   rG   r   r   r-   r.   r/   r0   r1   )
r(   r  r"   r#   r   r`   r8   rM   rN   r;   r)   r)   r*   r&     s   
zAnnotation.from_recordsc                 C   sB   ddl m}m} |st|j| jjd dS ddl m} || S )zjIPython notebook support

        See also
        --------
        :mod:`pyannote.core.notebook`
        r   )MATPLOTLIB_IS_AVAILABLEMATPLOTLIB_WARNING)klassN)repr_annotation)	notebookr  r  warningswarnformatr   __name__r   )r2   r  r  r   r)   r)   r*   
_repr_png_  s   zAnnotation._repr_png_)NN)Fr   )rc   r    )r   r4   )r$   r    )r   )Nr   T)r   )Nr  NN)Sr%  
__module____qualname____doc__classmethodr   rZ   r+   r3   propertyr"   setterrO   rS   rU   rT   r   r   r   r   rW   rY   boolr   r   rI   rb   r   r:   rn   rq   rs   r   r|   r   r
   r   r   r   r   r   r   r   r   r   r   r	   r   r   r7   r   r   r   r   r   r   r   r   r8   r   r   r>   r9   r   floatr   r   r   r   r   r   r   r   r   r   r   ndarrayr  r   r   r  r&   r&  r)   r)   r)   r*   r       s   

	
 




f
@)

25
*
',' (0
87
)


A%r    ).r)  ri   r"  collectionsr   typingr   r   r   r   r   r   r	   r
   r   r   r   r   numpyr   sortedcontainersr   r~   r   r   r   rM   r   r   r<   r   featurer   utils.generatorsr   r   utils.typesr   r   r   r   r   r   pandaspdr    r)   r)   r)   r*   <module>   s    O8 