def fields_checker(fields):
    """
    returns a fields argument formatted as a list of strings.
    and doesn't allow zero.
    """
    ## make sure fields will work
    if isinstance(fields, int):
        fields = str(fields)
    if isinstance(fields, str):
        if "," in fields:
            fields = [str(i) for i in fields.split(",")]
        else:
            fields = [str(fields)]
    elif isinstance(fields, (tuple, list)):
        fields = [str(i) for i in fields]
    else:
        raise IPyradWarningExit("fields not properly formatted")

    ## do not allow zero in fields
    fields = [i for i in fields if i != '0']

    return fields

def
fields_checker
(
fields
)
:
if
isinstance
(
fields
,
int
)
:
fields
=
str
(
fields
)
if
isinstance
(
fields
,
str
)
:
if
","
in
fields
:
fields
=
[
str
(
i
)
for
i
in
fields
.
split
(
","
)
]
else
:
fields
=
[
str
(
fields
)
]
elif
isinstance
(
fields
,
(
tuple
,
list
)
)
:
fields
=
[
str
(
i
)
for
i
in
fields
]
else
:
raise
IPyradWarningExit
(
"fields not properly formatted"
)
fields
=
[
i
for
i
in
fields
if
i
!=
'0'
]
return
fields

returns a fields argument formatted as a list of strings . and doesn t allow zero .


def run(self,
        force=False,
        ipyclient=None,
        name_fields=30,
        name_separator="_",
        dry_run=False):
        """
        Download the accessions into a the designated workdir.

        Parameters
        ----------
        force: (bool)
            If force=True then existing files with the same name
            will be overwritten.

        ipyclient: (ipyparallel.Client)
            If provided, work will be distributed across a parallel
            client, otherwise download will be run on a single core.

        name_fields: (int, str):
            Provide the index of the name fields to be used as a prefix
            for fastq output files. The default is 30, which is the
            SampleName field. Use sra.fetch_fields to see all available
            fields and their indices. A likely alternative is 1 (Run).
            If multiple are listed then they will be joined by a "_"
            character. For example (29,30) would yield something like:
            latin-name_sample-name (e.g., mus_musculus-NR10123).

        dry_run: (bool)
            If True then a table of file names that _would_ be downloaded
            will be shown, but the actual files will note be downloaded.
        """

        ## temporarily set directory for tmpfiles used by fastq-dump
        ## if this fails then just skip it.
        try:
            ## ensure output directory, also used as tmpdir
            if not os.path.exists(self.workdir):
                os.makedirs(self.workdir)

            ## get original directory for sra files
            ## probably /home/ncbi/public/sra by default.
            self._set_vdbconfig_path()

            ## register ipyclient for cleanup
            if ipyclient:
                self._ipcluster["pids"] = {}
                for eid in ipyclient.ids:
                    engine = ipyclient[eid]
                    if not engine.outstanding:
                        pid = engine.apply(os.getpid).get()
                        self._ipcluster["pids"][eid] = pid

            ## submit jobs to engines or local
            self._submit_jobs(
                force=force,
                ipyclient=ipyclient,
                name_fields=name_fields,
                name_separator=name_separator,
                dry_run=dry_run,
                )

        except IPyradWarningExit as inst:
            print(inst)
        ## exceptions to catch, cleanup and handle ipyclient interrupts
        except KeyboardInterrupt:
            print("keyboard interrupt...")
        except Exception as inst:
            print("Exception in run() - {}".format(inst))
        finally:
            ## reset working sra path
            self._restore_vdbconfig_path()

            ## if it made a new sra directory then it should be empty when
            ## we are finished if all .sra files were removed. If so, then
            ## let's also remove the dir. if not empty, leave it.
            sradir = os.path.join(self.workdir, "sra")
            if os.path.exists(sradir) and (not os.listdir(sradir)):
                shutil.rmtree(sradir)
            else:
                ## print warning
                try:
                    print(FAILED_DOWNLOAD.format(os.listdir(sradir)))
                except OSError as inst:
                    ## If sra dir doesn't even exist something very bad is broken.
                    raise IPyradWarningExit("Download failed. Exiting.")
                ## remove fastq file matching to cached sra file
                for srr in os.listdir(sradir):
                    isrr = srr.split(".")[0]
                    ipath = os.path.join(self.workdir, "*_{}*.gz".format(isrr))
                    ifile = glob.glob(ipath)[0]
                    if os.path.exists(ifile):
                        os.remove(ifile)
                ## remove cache of sra files
                shutil.rmtree(sradir)

            ## cleanup ipcluster shutdown
            if ipyclient:
                ## send SIGINT (2) to all engines still running tasks
                try:
                    ipyclient.abort()
                    time.sleep(0.5)
                    for engine_id, pid in self._ipcluster["pids"].items():
                        if ipyclient.queue_status()[engine_id]["tasks"]:
                            os.kill(pid, 2)
                        time.sleep(0.1)
                except ipp.NoEnginesRegistered:
                    pass
                ## clean memory space
                if not ipyclient.outstanding:
                    ipyclient.purge_everything()
                ## uh oh, kill everything, something bad happened
                else:
                    ipyclient.shutdown(hub=True, block=False)
                    ipyclient.close()
                    print("\nwarning: ipcluster shutdown and must be restarted")

Download the accessions into a the designated workdir .


def Async(cls, token, session=None, **options):
        """Returns the client in async mode."""
        return cls(token, session=session, is_async=True, **options)

def Async ( cls , token , session = None , ** options ) : return cls ( token , session = session , is_async = True , ** options )


def get_constants(self, **params: keys):
        """Get the CR Constants

        Parameters
        ----------
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout
        """
        url = self.api.CONSTANTS
        return self._get_model(url, **params)

def get_constants ( self , ** params : keys ) : url = self . api . CONSTANTS return self . _get_model ( url , ** params )


def get_player(self, *tags: crtag, **params: keys):
        """Get a player information

        Parameters
        ----------
        \*tags: str
            Valid player tags. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout
        """
        url = self.api.PLAYER + '/' + ','.join(tags)
        return self._get_model(url, FullPlayer, **params)

def get_player ( self , * tags : crtag , ** params : keys ) : url = self . api . PLAYER + '/' + ',' . join ( tags ) return self . _get_model ( url , FullPlayer , ** params )


def get_clan(self, *tags: crtag, **params: keys):
        """Get a clan information

        Parameters
        ----------
        \*tags: str
            Valid clan tags. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout
        """
        url = self.api.CLAN + '/' + ','.join(tags)
        return self._get_model(url, FullClan, **params)

def get_clan ( self , * tags : crtag , ** params : keys ) : url = self . api . CLAN + '/' + ',' . join ( tags ) return self . _get_model ( url , FullClan , ** params )


def search_tournaments(self, **params: keys):
        """Search for a tournament

        Parameters
        ----------
        name: str
            The name of the tournament
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout
        """
        url = self.api.TOURNAMENT + '/search'
        return self._get_model(url, PartialClan, **params)

def search_tournaments ( self , ** params : keys ) : url = self . api . TOURNAMENT + '/search' return self . _get_model ( url , PartialClan , ** params )


def get_top_war_clans(self, country_key='', **params: keys):
        """Get a list of top clans by war

        location_id: Optional[str] = ''
            A location ID or '' (global)
            See https://github.com/RoyaleAPI/cr-api-data/blob/master/json/regions.json
            for a list of acceptable location IDs
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout
        """
        url = self.api.TOP + '/war/' + str(country_key)
        return self._get_model(url, PartialClan, **params)

def get_top_war_clans ( self , country_key = '' , ** params : keys ) : url = self . api . TOP + '/war/' + str ( country_key ) return self . _get_model ( url , PartialClan , ** params )


def get_popular_clans(self, **params: keys):
        """Get a list of most queried clans

        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout
        """
        url = self.api.POPULAR + '/clans'
        return self._get_model(url, PartialClan, **params)

def get_popular_clans ( self , ** params : keys ) : url = self . api . POPULAR + '/clans' return self . _get_model ( url , PartialClan , ** params )


def get_popular_players(self, **params: keys):
        """Get a list of most queried players

        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout
        """
        url = self.api.POPULAR + '/players'
        return self._get_model(url, PartialPlayerClan, **params)

def get_popular_players ( self , ** params : keys ) : url = self . api . POPULAR + '/players' return self . _get_model ( url , PartialPlayerClan , ** params )


def get_popular_tournaments(self, **params: keys):
        """Get a list of most queried tournaments

        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout
        """
        url = self.api.POPULAR + '/tournament'
        return self._get_model(url, PartialTournament, **params)

def get_popular_tournaments ( self , ** params : keys ) : url = self . api . POPULAR + '/tournament' return self . _get_model ( url , PartialTournament , ** params )


def get_popular_decks(self, **params: keys):
        """Get a list of most queried decks

        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout
        """
        url = self.api.POPULAR + '/decks'
        return self._get_model(url, **params)

def get_popular_decks ( self , ** params : keys ) : url = self . api . POPULAR + '/decks' return self . _get_model ( url , ** params )


def get_known_tournaments(self, **params: tournamentfilter):
        """Get a list of queried tournaments

        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout
        """
        url = self.api.TOURNAMENT + '/known'
        return self._get_model(url, PartialTournament, **params)

def get_known_tournaments ( self , ** params : tournamentfilter ) : url = self . api . TOURNAMENT + '/known' return self . _get_model ( url , PartialTournament , ** params )


def get_player(self, tag: crtag, timeout=None):
        """Get information about a player

        Parameters
        ----------
        tag: str
            A valid tournament tag. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout
        """
        url = self.api.PLAYER + '/' + tag
        return self._get_model(url, FullPlayer, timeout=timeout)

def get_player ( self , tag : crtag , timeout = None ) : url = self . api . PLAYER + '/' + tag return self . _get_model ( url , FullPlayer , timeout = timeout )


def get_player_chests(self, tag: crtag, timeout: int=None):
        """Get information about a player's chest cycle

        Parameters
        ----------
        tag: str
            A valid tournament tag. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout
        """
        url = self.api.PLAYER + '/' + tag + '/upcomingchests'
        return self._get_model(url, timeout=timeout)

def get_player_chests ( self , tag : crtag , timeout : int = None ) : url = self . api . PLAYER + '/' + tag + '/upcomingchests' return self . _get_model ( url , timeout = timeout )

Get information about a player s chest cycle


def get_clan(self, tag: crtag, timeout: int=None):
        """Get inforamtion about a clan

        Parameters
        ----------
        tag: str
            A valid tournament tag. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout
        """
        url = self.api.CLAN + '/' + tag
        return self._get_model(url, FullClan, timeout=timeout)

def get_clan ( self , tag : crtag , timeout : int = None ) : url = self . api . CLAN + '/' + tag return self . _get_model ( url , FullClan , timeout = timeout )


def search_clans(self, **params: clansearch):
        """Search for a clan. At least one
        of the filters must be present

        Parameters
        ----------
        name: Optional[str]
            The name of a clan
            (has to be at least 3 characters long)
        locationId: Optional[int]
            A location ID
        minMembers: Optional[int]
            The minimum member count
            of a clan
        maxMembers: Optional[int]
            The maximum member count
            of a clan
        minScore: Optional[int]
            The minimum trophy score of
            a clan
        \*\*limit: Optional[int] = None
            Limit the number of items returned in the response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout
        """
        url = self.api.CLAN
        return self._get_model(url, PartialClan, **params)

def search_clans ( self , ** params : clansearch ) : url = self . api . CLAN return self . _get_model ( url , PartialClan , ** params )

Search for a clan . At least one of the filters must be present


def search_tournaments(self, name: str, **params: keys):
        """Search for a tournament by its name

        Parameters
        ----------
        name: str
            The name of a tournament
        \*\*limit: Optional[int] = None
            Limit the number of items returned in the response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout
        """
        url = self.api.TOURNAMENT
        params['name'] = name
        return self._get_model(url, PartialTournament, **params)

def search_tournaments ( self , name : str , ** params : keys ) : url = self . api . TOURNAMENT params [ 'name' ] = name return self . _get_model ( url , PartialTournament , ** params )


def get_all_cards(self, timeout: int=None):
        """Get a list of all the cards in the game

        Parameters
        ----------
        timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout
        """
        url = self.api.CARDS
        return self._get_model(url, timeout=timeout)

def get_all_cards ( self , timeout : int = None ) : url = self . api . CARDS return self . _get_model ( url , timeout = timeout )


def get_all_locations(self, timeout: int=None):
        """Get a list of all locations

        Parameters
        ----------
        timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout
        """
        url = self.api.LOCATIONS
        return self._get_model(url, timeout=timeout)

def get_all_locations ( self , timeout : int = None ) : url = self . api . LOCATIONS return self . _get_model ( url , timeout = timeout )


def get_location(self, location_id: int, timeout: int=None):
        """Get a location information

        Parameters
        ----------
        location_id: int
            A location ID
            See https://github.com/RoyaleAPI/cr-api-data/blob/master/json/regions.json
            for a list of acceptable location IDs
        timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout
        """
        url = self.api.LOCATIONS + '/' + str(location_id)
        return self._get_model(url, timeout=timeout)

def get_location ( self , location_id : int , timeout : int = None ) : url = self . api . LOCATIONS + '/' + str ( location_id ) return self . _get_model ( url , timeout = timeout )


def get_top_clans(self, location_id='global', **params: keys):
        """Get a list of top clans by trophy

        Parameters
        ----------
        location_id: Optional[str] = 'global'
            A location ID or global
            See https://github.com/RoyaleAPI/cr-api-data/blob/master/json/regions.json
            for a list of acceptable location IDs
        \*\*limit: Optional[int] = None
            Limit the number of items returned in the response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout
        """
        url = self.api.LOCATIONS + '/' + str(location_id) + '/rankings/clans'
        return self._get_model(url, PartialClan, **params)

def get_top_clans ( self , location_id = 'global' , ** params : keys ) : url = self . api . LOCATIONS + '/' + str ( location_id ) + '/rankings/clans' return self . _get_model ( url , PartialClan , ** params )


def get_clan_image(self, obj: BaseAttrDict):
        """Get the clan badge image URL

        Parameters
        ---------
        obj: official_api.models.BaseAttrDict
            An object that has the clan badge ID either in ``.clan.badge_id`` or ``.badge_id``
            Can be a clan or a profile for example.

        Returns str
        """

        try:
            badge_id = obj.clan.badge_id
        except AttributeError:
            try:
                badge_id = obj.badge_id
            except AttributeError:
                return 'https://i.imgur.com/Y3uXsgj.png'

        if badge_id is None:
            return 'https://i.imgur.com/Y3uXsgj.png'

        for i in self.constants.alliance_badges:
            if i.id == badge_id:
                return 'https://royaleapi.github.io/cr-api-assets/badges/' + i.name + '.png'


def get_arena_image(self, obj: BaseAttrDict):
        """Get the arena image URL

        Parameters
        ---------
        obj: official_api.models.BaseAttrDict
            An object that has the arena ID in ``.arena.id``
            Can be ``Profile`` for example.

        Returns None or str
        """
        badge_id = obj.arena.id
        for i in self.constants.arenas:
            if i.id == badge_id:
                return 'https://royaleapi.github.io/cr-api-assets/arenas/arena{}.png'.format(i.arena_id)


def get_deck_link(self, deck: BaseAttrDict):
        """Form a deck link

        Parameters
        ---------
        deck: official_api.models.BaseAttrDict
            An object is a deck. Can be retrieved from ``Player.current_deck``

        Returns str
        """
        deck_link = 'https://link.clashroyale.com/deck/en?deck='

        for i in deck:
            card = self.get_card_info(i.name)
            deck_link += '{0.id};'.format(card)

        return deck_link


def get_datetime(self, timestamp: str, unix=True):
        """Converts a %Y%m%dT%H%M%S.%fZ to a UNIX timestamp
        or a datetime.datetime object

        Parameters
        ---------
        timestamp: str
            A timstamp in the %Y%m%dT%H%M%S.%fZ format, usually returned by the API
            in the ``created_time`` field for example (eg. 20180718T145906.000Z)
        unix: Optional[bool] = True
            Whether to return a POSIX timestamp (seconds since epoch) or not

        Returns int or datetime.datetime
        """
        time = datetime.strptime(timestamp, '%Y%m%dT%H%M%S.%fZ')
        if unix:
            return int(time.timestamp())
        else:
            return time

def get_datetime ( self , timestamp : str , unix = True ) : time = datetime . strptime ( timestamp , '%Y%m%dT%H%M%S.%fZ' ) if unix : return int ( time . timestamp ( ) ) else : return time

Converts a %Y%m%dT%H%M%S . %fZ to a UNIX timestamp or a datetime . datetime object


def typecasted(func):
    """Decorator that converts arguments via annotations."""
    signature = inspect.signature(func).parameters.items()

    @wraps(func)
    def wrapper(*args, **kwargs):
        args = list(args)
        new_args = []
        new_kwargs = {}
        for _, param in signature:
            converter = param.annotation
            if converter is inspect._empty:
                converter = lambda a: a  # do nothing
            if param.kind is param.POSITIONAL_OR_KEYWORD:
                if args:
                    to_conv = args.pop(0)
                    new_args.append(converter(to_conv))
            elif param.kind is param.VAR_POSITIONAL:
                for a in args:
                    new_args.append(converter(a))
            else:
                for k, v in kwargs.items():
                    nk, nv = converter(k, v)
                    new_kwargs[nk] = nv
        return func(*new_args, **new_kwargs)
    return wrapper

def typecasted ( func ) : signature = inspect . signature ( func ) . parameters . items ( ) @ wraps ( func ) def wrapper ( * args , ** kwargs ) : args = list ( args ) new_args = [ ] new_kwargs = { } for _ , param in signature : converter = param . annotation if converter is inspect . _empty : converter = lambda a : a if param . kind is param . POSITIONAL_OR_KEYWORD : if args : to_conv = args . pop ( 0 ) new_args . append ( converter ( to_conv ) ) elif param . kind is param . VAR_POSITIONAL : for a in args : new_args . append ( converter ( a ) ) else : for k , v in kwargs . items ( ) : nk , nv = converter ( k , v ) new_kwargs [ nk ] = nv return func ( * new_args , ** new_kwargs ) return wrapper

Decorator that converts arguments via annotations .


def coerce_annotation(ann, namespace):
    '''Validate that the annotation has the correct namespace,
    and is well-formed.

    If the annotation is not of the correct namespace, automatic conversion
    is attempted.

    Parameters
    ----------
    ann : jams.Annotation
        The annotation object in question

    namespace : str
        The namespace pattern to match `ann` against

    Returns
    -------
    ann_coerced: jams.Annotation
        The annotation coerced to the target namespace

    Raises
    ------
    NamespaceError
        If `ann` does not match the proper namespace

    SchemaError
        If `ann` fails schema validation

    See Also
    --------
    jams.nsconvert.convert
    '''

    ann = convert(ann, namespace)
    ann.validate(strict=True)

    return ann

def coerce_annotation ( ann , namespace ) : ann = convert ( ann , namespace ) ann . validate ( strict = True ) return ann

Validate that the annotation has the correct namespace and is well - formed .


def beat(ref, est, **kwargs):
    r'''Beat tracking evaluation

    Parameters
    ----------
    ref : jams.Annotation
        Reference annotation object
    est : jams.Annotation
        Estimated annotation object
    kwargs
        Additional keyword arguments

    Returns
    -------
    scores : dict
        Dictionary of scores, where the key is the metric name (str) and
        the value is the (float) score achieved.

    See Also
    --------
    mir_eval.beat.evaluate

    Examples
    --------
    >>> # Load in the JAMS objects
    >>> ref_jam = jams.load('reference.jams')
    >>> est_jam = jams.load('estimated.jams')
    >>> # Select the first relevant annotations
    >>> ref_ann = ref_jam.search(namespace='beat')[0]
    >>> est_ann = est_jam.search(namespace='beat')[0]
    >>> scores = jams.eval.beat(ref_ann, est_ann)
    '''

    namespace = 'beat'
    ref = coerce_annotation(ref, namespace)
    est = coerce_annotation(est, namespace)

    ref_times, _ = ref.to_event_values()
    est_times, _ = est.to_event_values()

    return mir_eval.beat.evaluate(ref_times, est_times, **kwargs)

def beat ( ref , est , ** kwargs ) : r namespace = 'beat' ref = coerce_annotation ( ref , namespace ) est = coerce_annotation ( est , namespace ) ref_times , _ = ref . to_event_values ( ) est_times , _ = est . to_event_values ( ) return mir_eval . beat . evaluate ( ref_times , est_times , ** kwargs )


def chord(ref, est, **kwargs):
    r'''Chord evaluation

    Parameters
    ----------
    ref : jams.Annotation
        Reference annotation object
    est : jams.Annotation
        Estimated annotation object
    kwargs
        Additional keyword arguments

    Returns
    -------
    scores : dict
        Dictionary of scores, where the key is the metric name (str) and
        the value is the (float) score achieved.

    See Also
    --------
    mir_eval.chord.evaluate

    Examples
    --------
    >>> # Load in the JAMS objects
    >>> ref_jam = jams.load('reference.jams')
    >>> est_jam = jams.load('estimated.jams')
    >>> # Select the first relevant annotations
    >>> ref_ann = ref_jam.search(namespace='chord')[0]
    >>> est_ann = est_jam.search(namespace='chord')[0]
    >>> scores = jams.eval.chord(ref_ann, est_ann)
    '''

    namespace = 'chord'
    ref = coerce_annotation(ref, namespace)
    est = coerce_annotation(est, namespace)
    ref_interval, ref_value = ref.to_interval_values()
    est_interval, est_value = est.to_interval_values()

    return mir_eval.chord.evaluate(ref_interval, ref_value,
                                   est_interval, est_value, **kwargs)

def chord ( ref , est , ** kwargs ) : r namespace = 'chord' ref = coerce_annotation ( ref , namespace ) est = coerce_annotation ( est , namespace ) ref_interval , ref_value = ref . to_interval_values ( ) est_interval , est_value = est . to_interval_values ( ) return mir_eval . chord . evaluate ( ref_interval , ref_value , est_interval , est_value , ** kwargs )


def hierarchy_flatten(annotation):
    '''Flatten a multi_segment annotation into mir_eval style.

    Parameters
    ----------
    annotation : jams.Annotation
        An annotation in the `multi_segment` namespace

    Returns
    -------
    hier_intervalss : list
        A list of lists of intervals, ordered by increasing specificity.

    hier_labels : list
        A list of lists of labels, ordered by increasing specificity.
    '''

    intervals, values = annotation.to_interval_values()

    ordering = dict()

    for interval, value in zip(intervals, values):
        level = value['level']
        if level not in ordering:
            ordering[level] = dict(intervals=list(), labels=list())

        ordering[level]['intervals'].append(interval)
        ordering[level]['labels'].append(value['label'])

    levels = sorted(list(ordering.keys()))
    hier_intervals = [ordering[level]['intervals'] for level in levels]
    hier_labels = [ordering[level]['labels'] for level in levels]

    return hier_intervals, hier_labels

def hierarchy_flatten ( annotation ) : intervals , values = annotation . to_interval_values ( ) ordering = dict ( ) for interval , value in zip ( intervals , values ) : level = value [ 'level' ] if level not in ordering : ordering [ level ] = dict ( intervals = list ( ) , labels = list ( ) ) ordering [ level ] [ 'intervals' ] . append ( interval ) ordering [ level ] [ 'labels' ] . append ( value [ 'label' ] ) levels = sorted ( list ( ordering . keys ( ) ) ) hier_intervals = [ ordering [ level ] [ 'intervals' ] for level in levels ] hier_labels = [ ordering [ level ] [ 'labels' ] for level in levels ] return hier_intervals , hier_labels

Flatten a multi_segment annotation into mir_eval style .


def hierarchy(ref, est, **kwargs):
    r'''Multi-level segmentation evaluation

    Parameters
    ----------
    ref : jams.Annotation
        Reference annotation object
    est : jams.Annotation
        Estimated annotation object
    kwargs
        Additional keyword arguments

    Returns
    -------
    scores : dict
        Dictionary of scores, where the key is the metric name (str) and
        the value is the (float) score achieved.

    See Also
    --------
    mir_eval.hierarchy.evaluate

    Examples
    --------
    >>> # Load in the JAMS objects
    >>> ref_jam = jams.load('reference.jams')
    >>> est_jam = jams.load('estimated.jams')
    >>> # Select the first relevant annotations
    >>> ref_ann = ref_jam.search(namespace='multi_segment')[0]
    >>> est_ann = est_jam.search(namespace='multi_segment')[0]
    >>> scores = jams.eval.hierarchy(ref_ann, est_ann)
    '''
    namespace = 'multi_segment'
    ref = coerce_annotation(ref, namespace)
    est = coerce_annotation(est, namespace)
    ref_hier, ref_hier_lab = hierarchy_flatten(ref)
    est_hier, est_hier_lab = hierarchy_flatten(est)

    return mir_eval.hierarchy.evaluate(ref_hier, ref_hier_lab,
                                       est_hier, est_hier_lab,
                                       **kwargs)

def hierarchy ( ref , est , ** kwargs ) : r namespace = 'multi_segment' ref = coerce_annotation ( ref , namespace ) est = coerce_annotation ( est , namespace ) ref_hier , ref_hier_lab = hierarchy_flatten ( ref ) est_hier , est_hier_lab = hierarchy_flatten ( est ) return mir_eval . hierarchy . evaluate ( ref_hier , ref_hier_lab , est_hier , est_hier_lab , ** kwargs )


def tempo(ref, est, **kwargs):
    r'''Tempo evaluation

    Parameters
    ----------
    ref : jams.Annotation
        Reference annotation object
    est : jams.Annotation
        Estimated annotation object
    kwargs
        Additional keyword arguments

    Returns
    -------
    scores : dict
        Dictionary of scores, where the key is the metric name (str) and
        the value is the (float) score achieved.

    See Also
    --------
    mir_eval.tempo.evaluate

    Examples
    --------
    >>> # Load in the JAMS objects
    >>> ref_jam = jams.load('reference.jams')
    >>> est_jam = jams.load('estimated.jams')
    >>> # Select the first relevant annotations
    >>> ref_ann = ref_jam.search(namespace='tempo')[0]
    >>> est_ann = est_jam.search(namespace='tempo')[0]
    >>> scores = jams.eval.tempo(ref_ann, est_ann)
    '''

    ref = coerce_annotation(ref, 'tempo')
    est = coerce_annotation(est, 'tempo')

    ref_tempi = np.asarray([o.value for o in ref])
    ref_weight = ref.data[0].confidence
    est_tempi = np.asarray([o.value for o in est])

    return mir_eval.tempo.evaluate(ref_tempi, ref_weight, est_tempi, **kwargs)

def tempo ( ref , est , ** kwargs ) : r ref = coerce_annotation ( ref , 'tempo' ) est = coerce_annotation ( est , 'tempo' ) ref_tempi = np . asarray ( [ o . value for o in ref ] ) ref_weight = ref . data [ 0 ] . confidence est_tempi = np . asarray ( [ o . value for o in est ] ) return mir_eval . tempo . evaluate ( ref_tempi , ref_weight , est_tempi , ** kwargs )


def melody(ref, est, **kwargs):
    r'''Melody extraction evaluation

    Parameters
    ----------
    ref : jams.Annotation
        Reference annotation object
    est : jams.Annotation
        Estimated annotation object
    kwargs
        Additional keyword arguments

    Returns
    -------
    scores : dict
        Dictionary of scores, where the key is the metric name (str) and
        the value is the (float) score achieved.

    See Also
    --------
    mir_eval.melody.evaluate

    Examples
    --------
    >>> # Load in the JAMS objects
    >>> ref_jam = jams.load('reference.jams')
    >>> est_jam = jams.load('estimated.jams')
    >>> # Select the first relevant annotations
    >>> ref_ann = ref_jam.search(namespace='pitch_contour')[0]
    >>> est_ann = est_jam.search(namespace='pitch_contour')[0]
    >>> scores = jams.eval.melody(ref_ann, est_ann)
    '''

    namespace = 'pitch_contour'
    ref = coerce_annotation(ref, namespace)
    est = coerce_annotation(est, namespace)

    ref_times, ref_p = ref.to_event_values()
    est_times, est_p = est.to_event_values()

    ref_freq = np.asarray([p['frequency'] * (-1)**(~p['voiced']) for p in ref_p])
    est_freq = np.asarray([p['frequency'] * (-1)**(~p['voiced']) for p in est_p])

    return mir_eval.melody.evaluate(ref_times, ref_freq,
                                    est_times, est_freq,
                                    **kwargs)

def melody ( ref , est , ** kwargs ) : r namespace = 'pitch_contour' ref = coerce_annotation ( ref , namespace ) est = coerce_annotation ( est , namespace ) ref_times , ref_p = ref . to_event_values ( ) est_times , est_p = est . to_event_values ( ) ref_freq = np . asarray ( [ p [ 'frequency' ] * ( - 1 ) ** ( ~ p [ 'voiced' ] ) for p in ref_p ] ) est_freq = np . asarray ( [ p [ 'frequency' ] * ( - 1 ) ** ( ~ p [ 'voiced' ] ) for p in est_p ] ) return mir_eval . melody . evaluate ( ref_times , ref_freq , est_times , est_freq , ** kwargs )


def pattern_to_mireval(ann):
    '''Convert a pattern_jku annotation object to mir_eval format.

    Parameters
    ----------
    ann : jams.Annotation
        Must have `namespace='pattern_jku'`

    Returns
    -------
    patterns : list of list of tuples
        - `patterns[x]` is a list containing all occurrences of pattern x

        - `patterns[x][y]` is a list containing all notes for
           occurrence y of pattern x

        - `patterns[x][y][z]` contains a time-note tuple
          `(time, midi note)`
    '''

    # It's easier to work with dictionaries, since we can't assume
    # sequential pattern or occurrence identifiers

    patterns = defaultdict(lambda: defaultdict(list))

    # Iterate over the data in interval-value format

    for time, observation in zip(*ann.to_event_values()):

        pattern_id = observation['pattern_id']
        occurrence_id = observation['occurrence_id']
        obs = (time, observation['midi_pitch'])

        # Push this note observation into the correct pattern/occurrence
        patterns[pattern_id][occurrence_id].append(obs)

    # Convert to list-list-tuple format for mir_eval
    return [list(_.values()) for _ in six.itervalues(patterns)]

def pattern_to_mireval ( ann ) : patterns = defaultdict ( lambda : defaultdict ( list ) ) for time , observation in zip ( * ann . to_event_values ( ) ) : pattern_id = observation [ 'pattern_id' ] occurrence_id = observation [ 'occurrence_id' ] obs = ( time , observation [ 'midi_pitch' ] ) patterns [ pattern_id ] [ occurrence_id ] . append ( obs ) return [ list ( _ . values ( ) ) for _ in six . itervalues ( patterns ) ]

Convert a pattern_jku annotation object to mir_eval format .


def pattern(ref, est, **kwargs):
    r'''Pattern detection evaluation

    Parameters
    ----------
    ref : jams.Annotation
        Reference annotation object
    est : jams.Annotation
        Estimated annotation object
    kwargs
        Additional keyword arguments

    Returns
    -------
    scores : dict
        Dictionary of scores, where the key is the metric name (str) and
        the value is the (float) score achieved.

    See Also
    --------
    mir_eval.pattern.evaluate

    Examples
    --------
    >>> # Load in the JAMS objects
    >>> ref_jam = jams.load('reference.jams')
    >>> est_jam = jams.load('estimated.jams')
    >>> # Select the first relevant annotations
    >>> ref_ann = ref_jam.search(namespace='pattern_jku')[0]
    >>> est_ann = est_jam.search(namespace='pattern_jku')[0]
    >>> scores = jams.eval.pattern(ref_ann, est_ann)
    '''

    namespace = 'pattern_jku'
    ref = coerce_annotation(ref, namespace)
    est = coerce_annotation(est, namespace)

    ref_patterns = pattern_to_mireval(ref)
    est_patterns = pattern_to_mireval(est)

    return mir_eval.pattern.evaluate(ref_patterns, est_patterns, **kwargs)

def pattern ( ref , est , ** kwargs ) : r namespace = 'pattern_jku' ref = coerce_annotation ( ref , namespace ) est = coerce_annotation ( est , namespace ) ref_patterns = pattern_to_mireval ( ref ) est_patterns = pattern_to_mireval ( est ) return mir_eval . pattern . evaluate ( ref_patterns , est_patterns , ** kwargs )


def transcription(ref, est, **kwargs):
    r'''Note transcription evaluation

    Parameters
    ----------
    ref : jams.Annotation
        Reference annotation object
    est : jams.Annotation
        Estimated annotation object
    kwargs
        Additional keyword arguments

    Returns
    -------
    scores : dict
        Dictionary of scores, where the key is the metric name (str) and
        the value is the (float) score achieved.

    See Also
    --------
    mir_eval.transcription.evaluate

    Examples
    --------
    >>> # Load in the JAMS objects
    >>> ref_jam = jams.load('reference.jams')
    >>> est_jam = jams.load('estimated.jams')
    >>> # Select the first relevant annotations. You can use any annotation
    >>> # type that can be converted to pitch_contour (such as pitch_midi)
    >>> ref_ann = ref_jam.search(namespace='pitch_contour')[0]
    >>> est_ann = est_jam.search(namespace='note_hz')[0]
    >>> scores = jams.eval.transcription(ref_ann, est_ann)
    '''

    namespace = 'pitch_contour'
    ref = coerce_annotation(ref, namespace)
    est = coerce_annotation(est, namespace)
    ref_intervals, ref_p = ref.to_interval_values()
    est_intervals, est_p = est.to_interval_values()

    ref_pitches = np.asarray([p['frequency'] * (-1)**(~p['voiced']) for p in ref_p])
    est_pitches = np.asarray([p['frequency'] * (-1)**(~p['voiced']) for p in est_p])

    return mir_eval.transcription.evaluate(
        ref_intervals, ref_pitches, est_intervals, est_pitches, **kwargs)

def transcription ( ref , est , ** kwargs ) : r namespace = 'pitch_contour' ref = coerce_annotation ( ref , namespace ) est = coerce_annotation ( est , namespace ) ref_intervals , ref_p = ref . to_interval_values ( ) est_intervals , est_p = est . to_interval_values ( ) ref_pitches = np . asarray ( [ p [ 'frequency' ] * ( - 1 ) ** ( ~ p [ 'voiced' ] ) for p in ref_p ] ) est_pitches = np . asarray ( [ p [ 'frequency' ] * ( - 1 ) ** ( ~ p [ 'voiced' ] ) for p in est_p ] ) return mir_eval . transcription . evaluate ( ref_intervals , ref_pitches , est_intervals , est_pitches , ** kwargs )


def add_namespace(filename):
    '''Add a namespace definition to our working set.

    Namespace files consist of partial JSON schemas defining the behavior
    of the `value` and `confidence` fields of an Annotation.

    Parameters
    ----------
    filename : str
        Path to json file defining the namespace object
    '''
    with open(filename, mode='r') as fileobj:
        __NAMESPACE__.update(json.load(fileobj))

def add_namespace ( filename ) : with open ( filename , mode = 'r' ) as fileobj : __NAMESPACE__ . update ( json . load ( fileobj ) )

Add a namespace definition to our working set .


def namespace(ns_key):
    '''Construct a validation schema for a given namespace.

    Parameters
    ----------
    ns_key : str
        Namespace key identifier (eg, 'beat' or 'segment_tut')

    Returns
    -------
    schema : dict
        JSON schema of `namespace`
    '''

    if ns_key not in __NAMESPACE__:
        raise NamespaceError('Unknown namespace: {:s}'.format(ns_key))

    sch = copy.deepcopy(JAMS_SCHEMA['definitions']['SparseObservation'])

    for key in ['value', 'confidence']:
        try:
            sch['properties'][key] = __NAMESPACE__[ns_key][key]
        except KeyError:
            pass

    return sch

def
namespace
(
ns_key
)
:
if
ns_key
not
in
__NAMESPACE__
:
raise
NamespaceError
(
'Unknown namespace: {:s}'
.
format
(
ns_key
)
)
sch
=
copy
.
deepcopy
(
JAMS_SCHEMA
[
'definitions'
]
[
'SparseObservation'
]
)
for
key
in
[
'value'
,
'confidence'
]
:
try
:
sch
[
'properties'
]
[
key
]
=
__NAMESPACE__
[
ns_key
]
[
key
]
except
KeyError
:
pass
return
sch

Construct a validation schema for a given namespace .


def namespace_array(ns_key):
    '''Construct a validation schema for arrays of a given namespace.

    Parameters
    ----------
    ns_key : str
        Namespace key identifier

    Returns
    -------
    schema : dict
        JSON schema of `namespace` observation arrays
    '''

    obs_sch = namespace(ns_key)
    obs_sch['title'] = 'Observation'

    sch = copy.deepcopy(JAMS_SCHEMA['definitions']['SparseObservationList'])
    sch['items'] = obs_sch
    return sch

def namespace_array ( ns_key ) : obs_sch = namespace ( ns_key ) obs_sch [ 'title' ] = 'Observation' sch = copy . deepcopy ( JAMS_SCHEMA [ 'definitions' ] [ 'SparseObservationList' ] ) sch [ 'items' ] = obs_sch return sch

Construct a validation schema for arrays of a given namespace .


def values(ns_key):
    '''Return the allowed values for an enumerated namespace.

    Parameters
    ----------
    ns_key : str
        Namespace key identifier

    Returns
    -------
    values : list

    Raises
    ------
    NamespaceError
        If `ns_key` is not found, or does not have enumerated values

    Examples
    --------
    >>> jams.schema.values('tag_gtzan')
    ['blues', 'classical', 'country', 'disco', 'hip-hop', 'jazz',
     'metal', 'pop', 'reggae', 'rock']
    '''

    if ns_key not in __NAMESPACE__:
        raise NamespaceError('Unknown namespace: {:s}'.format(ns_key))

    if 'enum' not in __NAMESPACE__[ns_key]['value']:
        raise NamespaceError('Namespace {:s} is not enumerated'.format(ns_key))

    return copy.copy(__NAMESPACE__[ns_key]['value']['enum'])

def
values
(
ns_key
)
:
if
ns_key
not
in
__NAMESPACE__
:
raise
NamespaceError
(
'Unknown namespace: {:s}'
.
format
(
ns_key
)
)
if
'enum'
not
in
__NAMESPACE__
[
ns_key
]
[
'value'
]
:
raise
NamespaceError
(
'Namespace {:s} is not enumerated'
.
format
(
ns_key
)
)
return
copy
.
copy
(
__NAMESPACE__
[
ns_key
]
[
'value'
]
[
'enum'
]
)

Return the allowed values for an enumerated namespace .


def get_dtypes(ns_key):
    '''Get the dtypes associated with the value and confidence fields
    for a given namespace.

    Parameters
    ----------
    ns_key : str
        The namespace key in question

    Returns
    -------
    value_dtype, confidence_dtype : numpy.dtype
        Type identifiers for value and confidence fields.
    '''

    # First, get the schema
    if ns_key not in __NAMESPACE__:
        raise NamespaceError('Unknown namespace: {:s}'.format(ns_key))

    value_dtype = __get_dtype(__NAMESPACE__[ns_key].get('value', {}))
    confidence_dtype = __get_dtype(__NAMESPACE__[ns_key].get('confidence', {}))

    return value_dtype, confidence_dtype

def
get_dtypes
(
ns_key
)
:
if
ns_key
not
in
__NAMESPACE__
:
raise
NamespaceError
(
'Unknown namespace: {:s}'
.
format
(
ns_key
)
)
value_dtype
=
__get_dtype
(
__NAMESPACE__
[
ns_key
]
.
get
(
'value'
,
{
}
)
)
confidence_dtype
=
__get_dtype
(
__NAMESPACE__
[
ns_key
]
.
get
(
'confidence'
,
{
}
)
)
return
value_dtype
,
confidence_dtype

Get the dtypes associated with the value and confidence fields for a given namespace .


def list_namespaces():
    '''Print out a listing of available namespaces'''
    print('{:30s}\t{:40s}'.format('NAME', 'DESCRIPTION'))
    print('-' * 78)
    for sch in sorted(__NAMESPACE__):
        desc = __NAMESPACE__[sch]['description']
        desc = (desc[:44] + '..') if len(desc) > 46 else desc
        print('{:30s}\t{:40s}'.format(sch, desc))

def list_namespaces ( ) : print ( '{:30s}\t{:40s}' . format ( 'NAME' , 'DESCRIPTION' ) ) print ( '-' * 78 ) for sch in sorted ( __NAMESPACE__ ) : desc = __NAMESPACE__ [ sch ] [ 'description' ] desc = ( desc [ : 44 ] + '..' ) if len ( desc ) > 46 else desc print ( '{:30s}\t{:40s}' . format ( sch , desc ) )


def __get_dtype(typespec):
    '''Get the dtype associated with a jsonschema type definition

    Parameters
    ----------
    typespec : dict
        The schema definition

    Returns
    -------
    dtype : numpy.dtype
        The associated dtype
    '''

    if 'type' in typespec:
        return __TYPE_MAP__.get(typespec['type'], np.object_)

    elif 'enum' in typespec:
        # Enums map to objects
        return np.object_

    elif 'oneOf' in typespec:
        # Recurse
        types = [__get_dtype(v) for v in typespec['oneOf']]

        # If they're not all equal, return object
        if all([t == types[0] for t in types]):
            return types[0]

    return np.object_

def __get_dtype ( typespec ) : if 'type' in typespec : return __TYPE_MAP__ . get ( typespec [ 'type' ] , np . object_ ) elif 'enum' in typespec : return np . object_ elif 'oneOf' in typespec : types = [ __get_dtype ( v ) for v in typespec [ 'oneOf' ] ] if all ( [ t == types [ 0 ] for t in types ] ) : return types [ 0 ] return np . object_


def __load_jams_schema():
    '''Load the schema file from the package.'''

    schema_file = os.path.join(SCHEMA_DIR, 'jams_schema.json')

    jams_schema = None
    with open(resource_filename(__name__, schema_file), mode='r') as fdesc:
        jams_schema = json.load(fdesc)

    if jams_schema is None:
        raise JamsError('Unable to load JAMS schema')

    return jams_schema

def
__load_jams_schema
(
)
:
schema_file
=
os
.
path
.
join
(
SCHEMA_DIR
,
'jams_schema.json'
)
jams_schema
=
with
open
(
resource_filename
(
__name__
,
schema_file
)
,
mode
=
'r'
)
as
fdesc
:
jams_schema
=
json
.
load
(
fdesc
)
if
jams_schema
is
:
raise
JamsError
(
'Unable to load JAMS schema'
)
return
jams_schema


def import_lab(namespace, filename, infer_duration=True, **parse_options):
    r'''Load a .lab file as an Annotation object.

    .lab files are assumed to have the following format:

        ``TIME_START\tTIME_END\tANNOTATION``

    By default, .lab files are assumed to have columns separated by one
    or more white-space characters, and have no header or index column
    information.

    If the .lab file contains only two columns, then an empty duration
    field is inferred.

    If the .lab file contains more than three columns, each row's
    annotation value is assigned the contents of last non-empty column.

    Parameters
    ----------
    namespace : str
        The namespace for the new annotation

    filename : str
        Path to the .lab file

    infer_duration : bool
        If `True`, interval durations are inferred from `(start, end)` columns,
        or difference between successive times.

        If `False`, interval durations are assumed to be explicitly coded as
        `(start, duration)` columns.  If only one time column is given, then
        durations are set to 0.

        For instantaneous event annotations (e.g., beats or onsets), this
        should be set to `False`.

    parse_options : additional keyword arguments
        Passed to ``pandas.DataFrame.read_csv``

    Returns
    -------
    annotation : Annotation
        The newly constructed annotation object

    See Also
    --------
    pandas.DataFrame.read_csv
    '''

    # Create a new annotation object
    annotation = core.Annotation(namespace)

    parse_options.setdefault('sep', r'\s+')
    parse_options.setdefault('engine', 'python')
    parse_options.setdefault('header', None)
    parse_options.setdefault('index_col', False)

    # This is a hack to handle potentially ragged .lab data
    parse_options.setdefault('names', range(20))

    data = pd.read_csv(filename, **parse_options)

    # Drop all-nan columns
    data = data.dropna(how='all', axis=1)

    # Do we need to add a duration column?
    # This only applies to event annotations
    if len(data.columns) == 2:
        # Insert a column of zeros after the timing
        data.insert(1, 'duration', 0)
        if infer_duration:
            data['duration'][:-1] = data.loc[:, 0].diff()[1:].values

    else:
        # Convert from time to duration
        if infer_duration:
            data.loc[:, 1] -= data[0]

    for row in data.itertuples():
        time, duration = row[1:3]

        value = [x for x in row[3:] if x is not None][-1]

        annotation.append(time=time,
                          duration=duration,
                          confidence=1.0,
                          value=value)

    return annotation

def import_lab ( namespace , filename , infer_duration = True , ** parse_options ) : r annotation = core . Annotation ( namespace ) parse_options . setdefault ( 'sep' , r'\s+' ) parse_options . setdefault ( 'engine' , 'python' ) parse_options . setdefault ( 'header' , None ) parse_options . setdefault ( 'index_col' , False ) parse_options . setdefault ( 'names' , range ( 20 ) ) data = pd . read_csv ( filename , ** parse_options ) data = data . dropna ( how = 'all' , axis = 1 ) if len ( data . columns ) == 2 : data . insert ( 1 , 'duration' , 0 ) if infer_duration : data [ 'duration' ] [ : - 1 ] = data . loc [ : , 0 ] . diff ( ) [ 1 : ] . values else : if infer_duration : data . loc [ : , 1 ] -= data [ 0 ] for row in data . itertuples ( ) : time , duration = row [ 1 : 3 ] value = [ x for x in row [ 3 : ] if x is not None ] [ - 1 ] annotation . append ( time = time , duration = duration , confidence = 1.0 , value = value ) return annotation

r Load a . lab file as an Annotation object .


def expand_filepaths(base_dir, rel_paths):
    """Expand a list of relative paths to a give base directory.

    Parameters
    ----------
    base_dir : str
        The target base directory

    rel_paths : list (or list-like)
        Collection of relative path strings

    Returns
    -------
    expanded_paths : list
        `rel_paths` rooted at `base_dir`

    Examples
    --------
    >>> jams.util.expand_filepaths('/data', ['audio', 'beat', 'seglab'])
    ['/data/audio', '/data/beat', '/data/seglab']

    """
    return [os.path.join(base_dir, os.path.normpath(rp)) for rp in rel_paths]

def expand_filepaths ( base_dir , rel_paths ) : return [ os . path . join ( base_dir , os . path . normpath ( rp ) ) for rp in rel_paths ]

Expand a list of relative paths to a give base directory .


def smkdirs(dpath, mode=0o777):
    """Safely make a full directory path if it doesn't exist.

    Parameters
    ----------
    dpath : str
        Path of directory/directories to create

    mode : int [default=0777]
        Permissions for the new directories

    See also
    --------
    os.makedirs
    """
    if not os.path.exists(dpath):
        os.makedirs(dpath, mode=mode)

def smkdirs ( dpath , mode = 0o777 ) : if not os . path . exists ( dpath ) : os . makedirs ( dpath , mode = mode )

Safely make a full directory path if it doesn t exist .


def find_with_extension(in_dir, ext, depth=3, sort=True):
    """Naive depth-search into a directory for files with a given extension.

    Parameters
    ----------
    in_dir : str
        Path to search.
    ext : str
        File extension to match.
    depth : int
        Depth of directories to search.
    sort : bool
        Sort the list alphabetically

    Returns
    -------
    matched : list
        Collection of matching file paths.

    Examples
    --------
    >>> jams.util.find_with_extension('Audio', 'wav')
    ['Audio/LizNelson_Rainfall/LizNelson_Rainfall_MIX.wav',
     'Audio/LizNelson_Rainfall/LizNelson_Rainfall_RAW/LizNelson_Rainfall_RAW_01_01.wav',
     'Audio/LizNelson_Rainfall/LizNelson_Rainfall_RAW/LizNelson_Rainfall_RAW_02_01.wav',
     ...
     'Audio/Phoenix_ScotchMorris/Phoenix_ScotchMorris_STEMS/Phoenix_ScotchMorris_STEM_02.wav',
     'Audio/Phoenix_ScotchMorris/Phoenix_ScotchMorris_STEMS/Phoenix_ScotchMorris_STEM_03.wav',
    'Audio/Phoenix_ScotchMorris/Phoenix_ScotchMorris_STEMS/Phoenix_ScotchMorris_STEM_04.wav']

    """
    assert depth >= 1
    ext = ext.strip(os.extsep)
    match = list()
    for n in range(1, depth+1):
        wildcard = os.path.sep.join(["*"]*n)
        search_path = os.path.join(in_dir, os.extsep.join([wildcard, ext]))
        match += glob.glob(search_path)

    if sort:
        match.sort()
    return match

def find_with_extension ( in_dir , ext , depth = 3 , sort = True ) : assert depth >= 1 ext = ext . strip ( os . extsep ) match = list ( ) for n in range ( 1 , depth + 1 ) : wildcard = os . path . sep . join ( [ "*" ] * n ) search_path = os . path . join ( in_dir , os . extsep . join ( [ wildcard , ext ] ) ) match += glob . glob ( search_path ) if sort : match . sort ( ) return match

Naive depth - search into a directory for files with a given extension .


def get_comments(jam, ann):
    '''Get the metadata from a jam and an annotation, combined as a string.

    Parameters
    ----------
    jam : JAMS
        The jams object

    ann : Annotation
        An annotation object

    Returns
    -------
    comments : str
        The jam.file_metadata and ann.annotation_metadata, combined and serialized
    '''
    jam_comments = jam.file_metadata.__json__
    ann_comments = ann.annotation_metadata.__json__
    return json.dumps({'metadata': jam_comments,
                       'annotation metadata': ann_comments},
                      indent=2)

def
get_comments
(
jam
,
ann
)
:
jam_comments
=
jam
.
file_metadata
.
__json__
ann_comments
=
ann
.
annotation_metadata
.
__json__
return
json
.
dumps
(
{
'metadata'
:
jam_comments
,
'annotation metadata'
:
ann_comments
}
,
indent
=
2
)

Get the metadata from a jam and an annotation combined as a string .


def convert_jams(jams_file, output_prefix, csv=False, comment_char='#', namespaces=None):
    '''Convert jams to labs.

    Parameters
    ----------
    jams_file : str
        The path on disk to the jams file in question

    output_prefix : str
        The file path prefix of the outputs

    csv : bool
        Whether to output in csv (True) or lab (False) format

    comment_char : str
        The character used to denote comments

    namespaces : list-like
        The set of namespace patterns to match for output
    '''

    if namespaces is None:
        raise ValueError('No namespaces provided. Try ".*" for all namespaces.')

    jam = jams.load(jams_file)

    # Get all the annotations
    # Filter down to the unique ones
    # For each annotation
    #     generate the comment string
    #     generate the output filename
    #     dump to csv

    # Make a counter object for each namespace type
    counter = collections.Counter()

    annotations = []

    for query in namespaces:
        annotations.extend(jam.search(namespace=query))

    if csv:
        suffix = 'csv'
        sep = ','
    else:
        suffix = 'lab'
        sep = '\t'

    for ann in annotations:
        index = counter[ann.namespace]
        counter[ann.namespace] += 1
        filename = os.path.extsep.join([get_output_name(output_prefix,
                                                        ann.namespace,
                                                        index),
                                        suffix])

        comment = get_comments(jam, ann)

        # Dump to disk
        lab_dump(ann, comment, filename, sep, comment_char)


def parse_arguments(args):
    '''Parse arguments from the command line'''
    parser = argparse.ArgumentParser(description='Convert JAMS to .lab files')

    parser.add_argument('-c',
                        '--comma-separated',
                        dest='csv',
                        action='store_true',
                        default=False,
                        help='Output in .csv instead of .lab')

    parser.add_argument('--comment', dest='comment_char', type=str, default='#',
                        help='Comment character')

    parser.add_argument('-n',
                        '--namespace',
                        dest='namespaces',
                        nargs='+',
                        default=['.*'],
                        help='One or more namespaces to output.  Default is all.')

    parser.add_argument('jams_file',
                        help='Path to the input jams file')

    parser.add_argument('output_prefix', help='Prefix for output files')

    return vars(parser.parse_args(args))


def _conversion(target, source):
    '''A decorator to register namespace conversions.

    Usage
    -----
    >>> @conversion('tag_open', 'tag_.*')
    ... def tag_to_open(annotation):
    ...     annotation.namespace = 'tag_open'
    ...     return annotation
    '''

    def register(func):
        '''This decorator registers func as mapping source to target'''
        __CONVERSION__[target][source] = func
        return func

    return register

def _conversion ( target , source ) : def register ( func ) : __CONVERSION__ [ target ] [ source ] = func return func return register

A decorator to register namespace conversions .


def convert(annotation, target_namespace):
    '''Convert a given annotation to the target namespace.

    Parameters
    ----------
    annotation : jams.Annotation
        An annotation object

    target_namespace : str
        The target namespace

    Returns
    -------
    mapped_annotation : jams.Annotation
        if `annotation` already belongs to `target_namespace`, then
        it is returned directly.

        otherwise, `annotation` is copied and automatically converted
        to the target namespace.

    Raises
    ------
    SchemaError
        if the input annotation fails to validate

    NamespaceError
        if no conversion is possible

    Examples
    --------
    Convert frequency measurements in Hz to MIDI

    >>> ann_midi = jams.convert(ann_hz, 'note_midi')

    And back to Hz

    >>> ann_hz2 = jams.convert(ann_midi, 'note_hz')
    '''

    # First, validate the input. If this fails, we can't auto-convert.
    annotation.validate(strict=True)

    # If we're already in the target namespace, do nothing
    if annotation.namespace == target_namespace:
        return annotation

    if target_namespace in __CONVERSION__:
        # Otherwise, make a copy to mangle
        annotation = deepcopy(annotation)

        # Look for a way to map this namespace to the target
        for source in __CONVERSION__[target_namespace]:
            if annotation.search(namespace=source):
                return __CONVERSION__[target_namespace][source](annotation)

    # No conversion possible
    raise NamespaceError('Unable to convert annotation from namespace='
                         '"{0}" to "{1}"'.format(annotation.namespace,
                                                 target_namespace))

Convert a given annotation to the target namespace .


def can_convert(annotation, target_namespace):
    '''Test if an annotation can be mapped to a target namespace

    Parameters
    ----------
    annotation : jams.Annotation
        An annotation object

    target_namespace : str
        The target namespace

    Returns
    -------
    True
        if `annotation` can be automatically converted to
        `target_namespace`

    False
        otherwise
    '''

    # If we're already in the target namespace, do nothing
    if annotation.namespace == target_namespace:
        return True

    if target_namespace in __CONVERSION__:
        # Look for a way to map this namespace to the target
        for source in __CONVERSION__[target_namespace]:
            if annotation.search(namespace=source):
                return True
    return False

def can_convert ( annotation , target_namespace ) : if annotation . namespace == target_namespace : return True if target_namespace in __CONVERSION__ : for source in __CONVERSION__ [ target_namespace ] : if annotation . search ( namespace = source ) : return True return False


def pitch_hz_to_contour(annotation):
    '''Convert a pitch_hz annotation to a contour'''
    annotation.namespace = 'pitch_contour'
    data = annotation.pop_data()

    for obs in data:
        annotation.append(time=obs.time, duration=obs.duration,
                          confidence=obs.confidence,
                          value=dict(index=0,
                                     frequency=np.abs(obs.value),
                                     voiced=obs.value > 0))
    return annotation

def pitch_hz_to_contour ( annotation ) : annotation . namespace = 'pitch_contour' data = annotation . pop_data ( ) for obs in data : annotation . append ( time = obs . time , duration = obs . duration , confidence = obs . confidence , value = dict ( index = 0 , frequency = np . abs ( obs . value ) , voiced = obs . value > 0 ) ) return annotation


def note_hz_to_midi(annotation):
    '''Convert a pitch_hz annotation to pitch_midi'''

    annotation.namespace = 'note_midi'

    data = annotation.pop_data()

    for obs in data:
        annotation.append(time=obs.time, duration=obs.duration,
                          confidence=obs.confidence,
                          value=12 * (np.log2(obs.value) - np.log2(440.0)) + 69)

    return annotation

def note_hz_to_midi ( annotation ) : annotation . namespace = 'note_midi' data = annotation . pop_data ( ) for obs in data : annotation . append ( time = obs . time , duration = obs . duration , confidence = obs . confidence , value = 12 * ( np . log2 ( obs . value ) - np . log2 ( 440.0 ) ) + 69 ) return annotation


def scaper_to_tag(annotation):
    '''Convert scaper annotations to tag_open'''

    annotation.namespace = 'tag_open'

    data = annotation.pop_data()
    for obs in data:
        annotation.append(time=obs.time, duration=obs.duration,
                          confidence=obs.confidence, value=obs.value['label'])

    return annotation

def scaper_to_tag ( annotation ) : annotation . namespace = 'tag_open' data = annotation . pop_data ( ) for obs in data : annotation . append ( time = obs . time , duration = obs . duration , confidence = obs . confidence , value = obs . value [ 'label' ] ) return annotation


def deprecated(version, version_removed):
    '''This is a decorator which can be used to mark functions
    as deprecated.

    It will result in a warning being emitted when the function is used.'''

    def __wrapper(func, *args, **kwargs):
        '''Warn the user, and then proceed.'''
        code = six.get_function_code(func)
        warnings.warn_explicit(
            "{:s}.{:s}\n\tDeprecated as of JAMS version {:s}."
            "\n\tIt will be removed in JAMS version {:s}."
            .format(func.__module__, func.__name__,
                    version, version_removed),
            category=DeprecationWarning,
            filename=code.co_filename,
            lineno=code.co_firstlineno + 1
        )
        return func(*args, **kwargs)

    return decorator(__wrapper)

This is a decorator which can be used to mark functions as deprecated .


def _open(name_or_fdesc, mode='r', fmt='auto'):
    '''An intelligent wrapper for ``open``.

    Parameters
    ----------
    name_or_fdesc : string-type or open file descriptor
        If a string type, refers to the path to a file on disk.

        If an open file descriptor, it is returned as-is.

    mode : string
        The mode with which to open the file.
        See ``open`` for details.

    fmt : string ['auto', 'jams', 'json', 'jamz']
        The encoding for the input/output stream.

        If `auto`, the format is inferred from the filename extension.

        Otherwise, use the specified coding.

    See Also
    --------
    open
    gzip.open
    '''

    open_map = {'jams': open,
                'json': open,
                'jamz': gzip.open,
                'gz': gzip.open}

    # If we've been given an open descriptor, do the right thing
    if hasattr(name_or_fdesc, 'read') or hasattr(name_or_fdesc, 'write'):
        yield name_or_fdesc

    elif isinstance(name_or_fdesc, six.string_types):
        # Infer the opener from the extension

        if fmt == 'auto':
            _, ext = os.path.splitext(name_or_fdesc)

            # Pull off the extension separator
            ext = ext[1:]
        else:
            ext = fmt

        try:
            ext = ext.lower()

            # Force text mode if we're using gzip
            if ext in ['jamz', 'gz'] and 't' not in mode:
                mode = '{:s}t'.format(mode)

            with open_map[ext](name_or_fdesc, mode=mode) as fdesc:
                yield fdesc

        except KeyError:
            raise ParameterError('Unknown JAMS extension '
                                 'format: "{:s}"'.format(ext))

    else:
        # Don't know how to handle this. Raise a parameter error
        raise ParameterError('Invalid filename or '
                             'descriptor: {}'.format(name_or_fdesc))

def
_open
(
name_or_fdesc
,
mode
=
'r'
,
fmt
=
'auto'
)
:
open_map
=
{
'jams'
:
open
,
'json'
:
open
,
'jamz'
:
gzip
.
open
,
'gz'
:
gzip
.
open
}
if
hasattr
(
name_or_fdesc
,
'read'
)
or
hasattr
(
name_or_fdesc
,
'write'
)
:
yield
name_or_fdesc
elif
isinstance
(
name_or_fdesc
,
six
.
string_types
)
:
if
fmt
==
'auto'
:
_
,
ext
=
os
.
path
.
splitext
(
name_or_fdesc
)
ext
=
ext
[
1
:
]
else
:
ext
=
fmt
try
:
ext
=
ext
.
lower
(
)
if
ext
in
[
'jamz'
,
'gz'
]
and
't'
not
in
mode
:
mode
=
'{:s}t'
.
format
(
mode
)
with
open_map
[
ext
]
(
name_or_fdesc
,
mode
=
mode
)
as
fdesc
:
yield
fdesc
except
KeyError
:
raise
ParameterError
(
'Unknown JAMS extension '
'format: "{:s}"'
.
format
(
ext
)
)
else
:
raise
ParameterError
(
'Invalid filename or '
'descriptor: {}'
.
format
(
name_or_fdesc
)
)


def load(path_or_file, validate=True, strict=True, fmt='auto'):
    r"""Load a JAMS Annotation from a file.

    Parameters
    ----------
    path_or_file : str or file-like
        Path to the JAMS file to load
        OR
        An open file handle to load from.

    validate : bool
        Attempt to validate the JAMS object

    strict : bool
        if `validate == True`, enforce strict schema validation

    fmt : str ['auto', 'jams', 'jamz']
        The encoding format of the input

        If `auto`, encoding is inferred from the file name.

        If the input is an open file handle, `jams` encoding
        is used.

    Returns
    -------
    jam : JAMS
        The loaded JAMS object

    Raises
    ------
    SchemaError
        if `validate == True`, `strict==True`, and validation fails

    See also
    --------
    JAMS.validate
    JAMS.save

    Examples
    --------
    >>> # Load a jams object from a file name
    >>> J = jams.load('data.jams')
    >>> # Or from an open file descriptor
    >>> with open('data.jams', 'r') as fdesc:
    ...     J = jams.load(fdesc)
    >>> # Non-strict validation
    >>> J = jams.load('data.jams', strict=False)
    >>> # No validation at all
    >>> J = jams.load('data.jams', validate=False)
    """

    with _open(path_or_file, mode='r', fmt=fmt) as fdesc:
        jam = JAMS(**json.load(fdesc))

    if validate:
        jam.validate(strict=strict)

    return jam

def load ( path_or_file , validate = True , strict = True , fmt = 'auto' ) : r with _open ( path_or_file , mode = 'r' , fmt = fmt ) as fdesc : jam = JAMS ( ** json . load ( fdesc ) ) if validate : jam . validate ( strict = strict ) return jam


def query_pop(query, prefix, sep='.'):
    '''Pop a prefix from a query string.

    Parameters
    ----------
    query : str
        The query string

    prefix : str
        The prefix string to pop, if it exists

    sep : str
        The string to separate fields

    Returns
    -------
    popped : str
        `query` with a `prefix` removed from the front (if found)
        or `query` if the prefix was not found

    Examples
    --------
    >>> query_pop('Annotation.namespace', 'Annotation')
    'namespace'
    >>> query_pop('namespace', 'Annotation')
    'namespace'

    '''

    terms = query.split(sep)

    if terms[0] == prefix:
        terms = terms[1:]

    return sep.join(terms)

def query_pop ( query , prefix , sep = '.' ) : terms = query . split ( sep ) if terms [ 0 ] == prefix : terms = terms [ 1 : ] return sep . join ( terms )


def match_query(string, query):
    '''Test if a string matches a query.

    Parameters
    ----------
    string : str
        The string to test

    query : string, callable, or object
        Either a regular expression, callable function, or object.

    Returns
    -------
    match : bool
        `True` if:
        - `query` is a callable and `query(string) == True`
        - `query` is a regular expression and `re.match(query, string)`
        - or `string == query` for any other query

        `False` otherwise

    '''

    if six.callable(query):
        return query(string)

    elif (isinstance(query, six.string_types) and
          isinstance(string, six.string_types)):
        return re.match(query, string) is not None

    else:
        return query == string

def match_query ( string , query ) : if six . callable ( query ) : return query ( string ) elif ( isinstance ( query , six . string_types ) and isinstance ( string , six . string_types ) ) : return re . match ( query , string ) is not None else : return query == string


def serialize_obj(obj):
    '''Custom serialization functionality for working with advanced data types.

    - numpy arrays are converted to lists
    - lists are recursively serialized element-wise

    '''

    if isinstance(obj, np.integer):
        return int(obj)

    elif isinstance(obj, np.floating):
        return float(obj)

    elif isinstance(obj, np.ndarray):
        return obj.tolist()

    elif isinstance(obj, list):
        return [serialize_obj(x) for x in obj]

    elif isinstance(obj, Observation):
        return {k: serialize_obj(v) for k, v in six.iteritems(obj._asdict())}

    return obj

def serialize_obj ( obj ) : if isinstance ( obj , np . integer ) : return int ( obj ) elif isinstance ( obj , np . floating ) : return float ( obj ) elif isinstance ( obj , np . ndarray ) : return obj . tolist ( ) elif isinstance ( obj , list ) : return [ serialize_obj ( x ) for x in obj ] elif isinstance ( obj , Observation ) : return { k : serialize_obj ( v ) for k , v in six . iteritems ( obj . _asdict ( ) ) } return obj

Custom serialization functionality for working with advanced data types .


def summary(obj, indent=0):
    '''Helper function to format repr strings for JObjects and friends.

    Parameters
    ----------
    obj
        The object to repr

    indent : int >= 0
        indent each new line by `indent` spaces

    Returns
    -------
    r : str
        If `obj` has a `__summary__` method, it is used.

        If `obj` is a `SortedKeyList`, then it returns a description
        of the length of the list.

        Otherwise, `repr(obj)`.
    '''
    if hasattr(obj, '__summary__'):
        rep = obj.__summary__()
    elif isinstance(obj, SortedKeyList):
        rep = '<{:d} observations>'.format(len(obj))
    else:
        rep = repr(obj)

    return rep.replace('\n', '\n' + ' ' * indent)

def
summary
(
obj
,
indent
=
0
)
:
if
hasattr
(
obj
,
'__summary__'
)
:
rep
=
obj
.
__summary__
(
)
elif
isinstance
(
obj
,
SortedKeyList
)
:
rep
=
'<{:d} observations>'
.
format
(
len
(
obj
)
)
else
:
rep
=
repr
(
obj
)
return
rep
.
replace
(
'\n'
,
'\n'
+
' '
*
indent
)

Helper function to format repr strings for JObjects and friends .


def update(self, **kwargs):
        '''Update the attributes of a JObject.

        Parameters
        ----------
        kwargs
            Keyword arguments of the form `attribute=new_value`

        Examples
        --------
        >>> J = jams.JObject(foo=5)
        >>> J.dumps()
        '{"foo": 5}'
        >>> J.update(bar='baz')
        >>> J.dumps()
        '{"foo": 5, "bar": "baz"}'
        '''
        for name, value in six.iteritems(kwargs):
            setattr(self, name, value)

def update ( self , ** kwargs ) : for name , value in six . iteritems ( kwargs ) : setattr ( self , name , value )


def validate(self, strict=True):
        '''Validate a JObject against its schema

        Parameters
        ----------
        strict : bool
            Enforce strict schema validation

        Returns
        -------
        valid : bool
            True if the jam validates
            False if not, and `strict==False`

        Raises
        ------
        SchemaError
            If `strict==True` and `jam` fails validation
        '''

        valid = True

        try:
            jsonschema.validate(self.__json__, self.__schema__)

        except jsonschema.ValidationError as invalid:
            if strict:
                raise SchemaError(str(invalid))
            else:
                warnings.warn(str(invalid))

            valid = False

        return valid

def validate ( self , strict = True ) : valid = True try : jsonschema . validate ( self . __json__ , self . __schema__ ) except jsonschema . ValidationError as invalid : if strict : raise SchemaError ( str ( invalid ) ) else : warnings . warn ( str ( invalid ) ) valid = False return valid


def append(self, time=None, duration=None, value=None, confidence=None):
        '''Append an observation to the data field

        Parameters
        ----------
        time : float >= 0
        duration : float >= 0
            The time and duration of the new observation, in seconds
        value
        confidence
            The value and confidence of the new observations.

            Types and values should conform to the namespace of the
            Annotation object.

        Examples
        --------
        >>> ann = jams.Annotation(namespace='chord')
        >>> ann.append(time=3, duration=2, value='E#')
        '''

        self.data.add(Observation(time=float(time),
                                  duration=float(duration),
                                  value=value,
                                  confidence=confidence))

def append ( self , time = None , duration = None , value = None , confidence = None ) : self . data . add ( Observation ( time = float ( time ) , duration = float ( duration ) , value = value , confidence = confidence ) )


def append_records(self, records):
        '''Add observations from row-major storage.

        This is primarily useful for deserializing sparsely packed data.

        Parameters
        ----------
        records : iterable of dicts or Observations
            Each element of `records` corresponds to one observation.
        '''
        for obs in records:
            if isinstance(obs, Observation):
                self.append(**obs._asdict())
            else:
                self.append(**obs)

def append_records ( self , records ) : for obs in records : if isinstance ( obs , Observation ) : self . append ( ** obs . _asdict ( ) ) else : self . append ( ** obs )

Add observations from row - major storage .


def append_columns(self, columns):
        '''Add observations from column-major storage.

        This is primarily used for deserializing densely packed data.

        Parameters
        ----------
        columns : dict of lists
            Keys must be `time, duration, value, confidence`,
            and each much be a list of equal length.

        '''
        self.append_records([dict(time=t, duration=d, value=v, confidence=c)
                             for (t, d, v, c)
                             in six.moves.zip(columns['time'],
                                              columns['duration'],
                                              columns['value'],
                                              columns['confidence'])])

def append_columns ( self , columns ) : self . append_records ( [ dict ( time = t , duration = d , value = v , confidence = c ) for ( t , d , v , c ) in six . moves . zip ( columns [ 'time' ] , columns [ 'duration' ] , columns [ 'value' ] , columns [ 'confidence' ] ) ] )

Add observations from column - major storage .


def validate(self, strict=True):
        '''Validate this annotation object against the JAMS schema,
        and its data against the namespace schema.

        Parameters
        ----------
        strict : bool
            If `True`, then schema violations will cause an Exception.
            If `False`, then schema violations will issue a warning.

        Returns
        -------
        valid : bool
            `True` if the object conforms to schema.
            `False` if the object fails to conform to schema,
            but `strict == False`.

        Raises
        ------
        SchemaError
            If `strict == True` and the object fails validation

        See Also
        --------
        JObject.validate
        '''

        # Get the schema for this annotation
        ann_schema = schema.namespace_array(self.namespace)

        valid = True

        try:
            jsonschema.validate(self.__json_light__(data=False),
                                schema.JAMS_SCHEMA)

            # validate each record in the frame
            data_ser = [serialize_obj(obs) for obs in self.data]
            jsonschema.validate(data_ser, ann_schema)

        except jsonschema.ValidationError as invalid:
            if strict:
                raise SchemaError(str(invalid))
            else:
                warnings.warn(str(invalid))
            valid = False

        return valid

def validate ( self , strict = True ) : ann_schema = schema . namespace_array ( self . namespace ) valid = True try : jsonschema . validate ( self . __json_light__ ( data = False ) , schema . JAMS_SCHEMA ) data_ser = [ serialize_obj ( obs ) for obs in self . data ] jsonschema . validate ( data_ser , ann_schema ) except jsonschema . ValidationError as invalid : if strict : raise SchemaError ( str ( invalid ) ) else : warnings . warn ( str ( invalid ) ) valid = False return valid

Validate this annotation object against the JAMS schema and its data against the namespace schema .


def trim(self, start_time, end_time, strict=False):
        '''
        Trim the annotation and return as a new `Annotation` object.

        Trimming will result in the new annotation only containing observations
        that occur in the intersection of the time range spanned by the
        annotation and the time range specified by the user. The new annotation
        will span the time range ``[trim_start, trim_end]`` where
        ``trim_start = max(self.time, start_time)`` and ``trim_end =
        min(self.time + self.duration, end_time)``.

        If ``strict=False`` (default) observations that start before
        ``trim_start`` and end after it will be trimmed such that they start at
        ``trim_start``, and similarly observations that start before
        ``trim_end`` and end after it will be trimmed to end at ``trim_end``.
        If ``strict=True`` such borderline observations will be discarded.

        The new duration of the annotation will be ``trim_end - trim_start``.

        Note that if the range defined by ``[start_time, end_time]``
        doesn't intersect with the original time range spanned by the
        annotation the resulting annotation will contain no observations, will
        have the same start time as the original annotation and have duration
        0.

        This function also copies over all the annotation metadata from the
        original annotation and documents the trim operation by adding a list
        of tuples to the annotation's sandbox keyed by
        ``Annotation.sandbox.trim`` which documents each trim operation with a
        tuple ``(start_time, end_time, trim_start, trim_end)``.

        Parameters
        ----------
        start_time : float
            The desired start time for the trimmed annotation in seconds.
        end_time
            The desired end time for the trimmed annotation in seconds. Must be
            greater than ``start_time``.
        strict : bool
            When ``False`` (default) observations that lie at the boundaries of
            the trimming range (given by ``[trim_start, trim_end]`` as
            described above), i.e. observations that start before and end after
            either the trim start or end time, will have their time and/or
            duration adjusted such that only the part of the observation that
            lies within the trim range is kept. When ``True`` such observations
            are discarded and not included in the trimmed annotation.

        Returns
        -------
        ann_trimmed : Annotation
            The trimmed annotation, returned as a new jams.Annotation object.
            If the trim range specified by ``[start_time, end_time]`` does not
            intersect at all with the original time range of the annotation a
            warning will be issued and the returned annotation will be empty.

        Raises
        ------
        ParameterError
            If ``end_time`` is not greater than ``start_time``.

        Examples
        --------
        >>> ann = jams.Annotation(namespace='tag_open', time=2, duration=8)
        >>> ann.append(time=2, duration=2, value='one')
        >>> ann.append(time=4, duration=2, value='two')
        >>> ann.append(time=6, duration=2, value='three')
        >>> ann.append(time=7, duration=2, value='four')
        >>> ann.append(time=8, duration=2, value='five')
        >>> ann_trim = ann.trim(5, 8, strict=False)
        >>> print(ann_trim.time, ann_trim.duration)
        (5, 3)
        >>> ann_trim.to_dataframe()
           time  duration  value confidence
        0     5         1    two       None
        1     6         2  three       None
        2     7         1   four       None
        >>> ann_trim_strict = ann.trim(5, 8, strict=True)
        >>> print(ann_trim_strict.time, ann_trim_strict.duration)
        (5, 3)
        >>> ann_trim_strict.to_dataframe()
           time  duration  value confidence
        0     6         2  three       None
        '''
        # Check for basic start_time and end_time validity
        if end_time <= start_time:
            raise ParameterError(
                'end_time must be greater than start_time.')

        # If the annotation does not have a set duration value, we'll assume
        # trimming is possible (up to the user to ensure this is valid).
        if self.duration is None:
            orig_time = start_time
            orig_duration = end_time - start_time
            warnings.warn(
                "Annotation.duration is not defined, cannot check "
                "for temporal intersection, assuming the annotation "
                "is valid between start_time and end_time.")
        else:
            orig_time = self.time
            orig_duration = self.duration

        # Check whether there is intersection between the trim range and
        # annotation: if not raise a warning and set trim_start and trim_end
        # appropriately.
        if start_time > (orig_time + orig_duration) or (end_time < orig_time):
            warnings.warn(
                'Time range defined by [start_time,end_time] does not '
                'intersect with the time range spanned by this annotation, '
                'the trimmed annotation will be empty.')
            trim_start = self.time
            trim_end = trim_start
        else:
            # Determine new range
            trim_start = max(orig_time, start_time)
            trim_end = min(orig_time + orig_duration, end_time)

        # Create new annotation with same namespace/metadata
        ann_trimmed = Annotation(
            self.namespace,
            data=None,
            annotation_metadata=self.annotation_metadata,
            sandbox=self.sandbox,
            time=trim_start,
            duration=trim_end - trim_start)

        # Selectively add observations based on their start time / duration
        # We do this rather than copying and directly manipulating the
        # annotation' data frame (which might be faster) since this way trim is
        # independent of the internal data representation.
        for obs in self.data:

            obs_start = obs.time
            obs_end = obs_start + obs.duration

            if obs_start < trim_end and obs_end > trim_start:

                new_start = max(obs_start, trim_start)
                new_end = min(obs_end, trim_end)
                new_duration = new_end - new_start

                if ((not strict) or
                        (new_start == obs_start and new_end == obs_end)):
                    ann_trimmed.append(time=new_start,
                                       duration=new_duration,
                                       value=obs.value,
                                       confidence=obs.confidence)

        if 'trim' not in ann_trimmed.sandbox.keys():
            ann_trimmed.sandbox.update(
                trim=[{'start_time': start_time, 'end_time': end_time,
                       'trim_start': trim_start, 'trim_end': trim_end}])
        else:
            ann_trimmed.sandbox.trim.append(
                {'start_time': start_time, 'end_time': end_time,
                 'trim_start': trim_start, 'trim_end': trim_end})

        return ann_trimmed

Trim the annotation and return as a new Annotation object .


def slice(self, start_time, end_time, strict=False):
        '''
        Slice the annotation and return as a new `Annotation` object.

        Slicing has the same effect as trimming (see `Annotation.trim`) except
        that while trimming does not modify the start time of the annotation or
        the observations it contains, slicing will set the new annotation's
        start time to ``max(0, trimmed_annotation.time - start_time)`` and the
        start time of its observations will be set with respect to this new
        reference start time.

        This function documents the slice operation by adding a list of tuples
        to the annotation's sandbox keyed by ``Annotation.sandbox.slice`` which
        documents each slice operation with a tuple
        ``(start_time, end_time, slice_start, slice_end)``, where
        ``slice_start`` and ``slice_end`` are given by ``trim_start`` and
        ``trim_end`` (see `Annotation.trim`).

        Since slicing is implemented  using trimming, the trimming operation
        will also be documented in ``Annotation.sandbox.trim`` as described in
        `Annotation.trim`.

        This function is useful for example when trimming an audio file,
        allowing the user to trim the annotation while ensuring all time
        information matches the new trimmed audio file.

        Parameters
        ----------
        start_time : float
            The desired start time for slicing in seconds.
        end_time
            The desired end time for slicing in seconds. Must be greater than
            ``start_time``.
        strict : bool
            When ``False`` (default) observations that lie at the boundaries of
            the slice (see `Annotation.trim` for details) will have their time
            and/or duration adjusted such that only the part of the observation
            that lies within the slice range is kept. When ``True`` such
            observations are discarded and not included in the sliced
            annotation.

        Returns
        -------
        sliced_ann : Annotation
            The sliced annotation.

        See Also
        --------
        Annotation.trim

        Examples
        --------
        >>> ann = jams.Annotation(namespace='tag_open', time=2, duration=8)
        >>> ann.append(time=2, duration=2, value='one')
        >>> ann.append(time=4, duration=2, value='two')
        >>> ann.append(time=6, duration=2, value='three')
        >>> ann.append(time=7, duration=2, value='four')
        >>> ann.append(time=8, duration=2, value='five')
        >>> ann_slice = ann.slice(5, 8, strict=False)
        >>> print(ann_slice.time, ann_slice.duration)
        (0, 3)
        >>> ann_slice.to_dataframe()
           time  duration  value confidence
        0   0.0       1.0    two       None
        1   1.0       2.0  three       None
        2   2.0       1.0   four       None
        >>> ann_slice_strict = ann.slice(5, 8, strict=True)
        >>> print(ann_slice_strict.time, ann_slice_strict.duration)
        (0, 3)
        >>> ann_slice_strict.to_dataframe()
           time  duration  value confidence
        0   1.0       2.0  three       None
        '''
        # start by trimming the annotation
        sliced_ann = self.trim(start_time, end_time, strict=strict)
        raw_data = sliced_ann.pop_data()

        # now adjust the start time of the annotation and the observations it
        # contains.

        for obs in raw_data:
            new_time = max(0, obs.time - start_time)
            # if obs.time > start_time,
            #   duration doesn't change
            # if obs.time < start_time,
            #   duration shrinks by start_time - obs.time
            sliced_ann.append(time=new_time,
                              duration=obs.duration,
                              value=obs.value,
                              confidence=obs.confidence)

        ref_time = sliced_ann.time
        slice_start = ref_time
        slice_end = ref_time + sliced_ann.duration

        if 'slice' not in sliced_ann.sandbox.keys():
            sliced_ann.sandbox.update(
                slice=[{'start_time': start_time, 'end_time': end_time,
                        'slice_start': slice_start, 'slice_end': slice_end}])
        else:
            sliced_ann.sandbox.slice.append(
                {'start_time': start_time, 'end_time': end_time,
                 'slice_start': slice_start, 'slice_end': slice_end})

        # Update the timing for the sliced annotation
        sliced_ann.time = max(0, ref_time - start_time)

        return sliced_ann

def slice ( self , start_time , end_time , strict = False ) : sliced_ann = self . trim ( start_time , end_time , strict = strict ) raw_data = sliced_ann . pop_data ( ) for obs in raw_data : new_time = max ( 0 , obs . time - start_time ) sliced_ann . append ( time = new_time , duration = obs . duration , value = obs . value , confidence = obs . confidence ) ref_time = sliced_ann . time slice_start = ref_time slice_end = ref_time + sliced_ann . duration if 'slice' not in sliced_ann . sandbox . keys ( ) : sliced_ann . sandbox . update ( slice = [ { 'start_time' : start_time , 'end_time' : end_time , 'slice_start' : slice_start , 'slice_end' : slice_end } ] ) else : sliced_ann . sandbox . slice . append ( { 'start_time' : start_time , 'end_time' : end_time , 'slice_start' : slice_start , 'slice_end' : slice_end } ) sliced_ann . time = max ( 0 , ref_time - start_time ) return sliced_ann

Slice the annotation and return as a new Annotation object .


def pop_data(self):
        '''Replace this observation's data with a fresh container.

        Returns
        -------
        annotation_data : SortedKeyList
            The original annotation data container
        '''

        data = self.data
        self.data = SortedKeyList(key=self._key)
        return data

def pop_data ( self ) : data = self . data self . data = SortedKeyList ( key = self . _key ) return data

Replace this observation s data with a fresh container .


def to_samples(self, times, confidence=False):
        '''Sample the annotation at specified times.

        Parameters
        ----------
        times : np.ndarray, non-negative, ndim=1
            The times (in seconds) to sample the annotation

        confidence : bool
            If `True`, return both values and confidences.
            If `False` (default) only return values.

        Returns
        -------
        values : list
            `values[i]` is a list of observation values for intervals
            that cover `times[i]`.

        confidence : list (optional)
            `confidence` values corresponding to `values`
        '''
        times = np.asarray(times)
        if times.ndim != 1 or np.any(times < 0):
            raise ParameterError('times must be 1-dimensional and non-negative')

        idx = np.argsort(times)
        samples = times[idx]

        values = [list() for _ in samples]
        confidences = [list() for _ in samples]

        for obs in self.data:
            start = np.searchsorted(samples, obs.time)
            end = np.searchsorted(samples, obs.time + obs.duration, side='right')
            for i in range(start, end):
                values[idx[i]].append(obs.value)
                confidences[idx[i]].append(obs.confidence)

        if confidence:
            return values, confidences
        else:
            return values

Sample the annotation at specified times .


def to_html(self, max_rows=None):
        '''Render this annotation list in HTML

        Returns
        -------
        rendered : str
            An HTML table containing this annotation's data.
        '''
        n = len(self.data)

        div_id = _get_divid(self)

        out = r'''

                            <button
                                type="button"
                                data-toggle="collapse"
                                data-parent="#accordion"
                                href="#{0}"
                                aria-expanded="false"
                                class="collapsed btn btn-info btn-block"
                                aria-controls="{0}">
                                {1:s}
                                {2:d}
                            </button>

'''.format(div_id, self.namespace, n)

        out += r'''

'''.format(div_id)

        out += r'''

                        {}

'''.format(self.annotation_metadata._repr_html_())
        out += r'''

                        {}

'''.format(self.sandbox._repr_html_())

        # -- Annotation content starts here
        out += r'''

                            time
                            duration
                            value
                            confidence

'''.format(self.namespace, n)

        out += r'''
'''

        if max_rows is None or n <= max_rows:
            out += self._fmt_rows(0, n)
        else:
            out += self._fmt_rows(0, max_rows//2)
            out += r'''

                            ...
                            ...
                            ...
                            ...
                            ...

'''
            out += self._fmt_rows(n-max_rows//2, n)

        out += r'''
'''

        out += r'''

'''

        out += r'''

'''
        return out

def to_html ( self , max_rows = None ) : n = len ( self . data ) div_id = _get_divid ( self ) out = r . format ( div_id , self . namespace , n ) out += r . format ( div_id ) out += r . format ( self . annotation_metadata . _repr_html_ ( ) ) out += r . format ( self . sandbox . _repr_html_ ( ) ) out += r . format ( self . namespace , n ) out += r if max_rows is None or n <= max_rows : out += self . _fmt_rows ( 0 , n ) else : out += self . _fmt_rows ( 0 , max_rows // 2 ) out += r out += self . _fmt_rows ( n - max_rows // 2 , n ) out += r out += r out += r return out


def _key(cls, obs):
        '''Provides sorting index for Observation objects'''
        if not isinstance(obs, Observation):
            raise JamsError('{} must be of type jams.Observation'.format(obs))

        return obs.time

def
_key
(
cls
,
obs
)
:
if
not
isinstance
(
obs
,
Observation
)
:
raise
JamsError
(
'{} must be of type jams.Observation'
.
format
(
obs
)
)
return
obs
.
time