# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/202_new_data.annotated.ipynb (unless otherwise specified).

__all__ = ['preprocess_extract_annotation_windows', 'pre_categorize', 'TSPerAnnotationGenericClassifier',
           'TSPerAnnClassification', 'TSPerAnnMultiLabelClassification', 'TSAnnotationWindow',
           'get_ats_dls_with_transforms']

# Cell
from tsai.imports import *
from tsai.utils import *
from tsai.data.external import *
from tsai.data.core import *
from tsai.data.preprocessing import *

# Cell
from .synthetic import get_synthetic_sin_data

# Internal Cell
def _sliding_annotations(all_ann, lengths, width=100, offset=None, limit=None, include_empty=False):
    lengths = [lengths]*len(all_ann) if isinstance(lengths, int) else lengths   # lengths for each row
    offset = offset if offset else -width//2
    offsetR = width + offset
    for i, row_anns in enumerate(all_ann):
        row_anns =  [(idx, e) for idx, e in enumerate(row_anns)
                     if e+offset >=0 and e+offsetR <  lengths[i]]  # trim overlap
        if len(row_anns) > 0:
            if limit and len(row_anns) > limit:
                random.shuffle(row_anns)
                row_anns = row_anns[:limit]
            for i_a, pos in row_anns:
                yield i, pos+offset, pos+offsetR, i_a
        elif include_empty:
            pos = random.randint(lengths[i]-width)
            yield i, pos, pos+width, None
        else:
            continue

# Internal Cell
def _compute_new_splits(m, orig_splits):
    return L( L(i for i, old_i in enumerate(m) if old_i in split_set)
              for split_set in orig_splits)

# Internal Cell
def _compute_new_folds(m, orig_folds):
    return orig_folds[m]

# Cell
@delegates(_sliding_annotations)
def preprocess_extract_annotation_windows(X, anns, y=None, splits=None, folds=None,
                                          per_ann_label=False, verbose=False, **kwargs):
    """Extracted timeseries subsequences and optionally label relative to annotated positions"""
    lengths = [x.shape[-1] for x in X]

    if isinstance(y, NoneType):
        if verbose: print('No Label')
        X_win, m = zip(*[[X[i_orig][..., idx_l:idx_r], i_orig]
            for (i_orig, idx_l, idx_r, _) in _sliding_annotations(anns, lengths, **kwargs)])
        y_win = None
    elif per_ann_label:
        if verbose: print('label per annotation')
        X_win, y_win, m = zip(*[[X[i_orig][..., idx_l:idx_r], y[i_orig][i_a], i_orig]
            for (i_orig, idx_l, idx_r, i_a) in _sliding_annotations(anns, lengths, **kwargs)])
        y_win = L(*y_win)
    elif isinstance(y[0], np.ndarray):
        if verbose: print('label is vector')
        assert y[0].shape[-1] == X[0].shape[-1]
        X_win, y_win, m = zip(*[[X[i_orig][..., idx_l:idx_r], y[i_orig][idx_l:idx_r], i_orig]
            for (i_orig, idx_l, idx_r, _) in _sliding_annotations(anns, lengths, **kwargs)])
        y_win = np.array(y_win)
    else:
        if verbose: print('label per row')
        X_win, y_win, m = zip(*[[X[i_orig][..., idx_l:idx_r], y[i_orig], i_orig]
            for (i_orig, idx_l, idx_r, _) in _sliding_annotations(anns, lengths, **kwargs)])
        y_win = L(*y_win)

    ret = [np.array(X_win)]
    if not isinstance(y_win, NoneType):
        ret.append(y_win)

    if not isinstance(splits, NoneType):
        return [*ret, _compute_new_splits(m, splits)]
    elif not isinstance(folds, NoneType):
        return [*ret, _compute_new_folds(m, folds)]
    elif len(ret) == 1:
        return ret[0]
    else:
        return ret

# Cell
def pre_categorize(*args, categorizer=None, splits=None, default=None):
    """Applies categorizer to list or nest list of labels.  Used
    to preprocess per-annotation labels"""
    is_nested = isinstance(args[0][0], (tuple, list, L))
    if categorizer is None: categorizer=Categorize
    if inspect.isclass(categorizer):
        y_train = args[0][splits[0]] if splits else args[0]
        vocab = {y_per for y_row in y_train for y_per in y_row} \
                if is_nested else {y_row for y_row in y_train}
        if default: vocab.add(default)
        c = categorizer()
        c.setups(vocab)
    else:
        c = categorizer
    if is_nested:
        results = [[[c(y_per) for y_per in y_row] for y_row in y_set] for y_set in args]
    else:
        results = [[c(y_row) for y_row in y_set] for y_set in args]
    results = results[0] if len(args) == 1 else results
    return results, c

# Cell
class TSPerAnnotationGenericClassifier(DisplayedTransform):
    """Common base class supporting slassification of list of Labels"""
    def __init__(self, classifier, default=None, **kwargs):
        assert classifier is not None
        self.classifier = classifier(**kwargs) if inspect.isclass(classifier) else classifier
        self.default=default

    @property
    def vocab(self):
        """Returns vocabulary with map"""
        return self.classifier.vocab

    @property
    def internal_classifier(self):
        """Returns underlying classifier (TSClassification or TSMultiLabelClassification)"""
        return self.classifier

    def setups(self, dsets):
        if self.classifier.vocab is None and dsets is not None:
            label_values = [label for row in dsets for label in row]
            if self.default: label_values.append(label_values)
            self.classifier.setups(label_values)

    def encodes(self, o):
        return L(self.classifier.encodes(oo) for oo in o)

    def decodes(self, o):
        return L(self.classifier.decodes(oo) for oo in o)

# Cell
class TSPerAnnClassification(TSPerAnnotationGenericClassifier):
    """Classifies list of per-annotation labels, returns L(TensorCategory)"""
    @delegates(TSPerAnnotationGenericClassifier.__init__)
    @delegates(TSClassification.__init__)
    def __init__(self, **kwargs):
        """Classifies list of per-annotation labels, returns L(TensorCategory)"""
        super().__init__(TSClassification, **kwargs)

# Cell
class TSPerAnnMultiLabelClassification(TSPerAnnotationGenericClassifier):
    """Classifies list of per-annotation multi-labels,
    returns (L(tensor) contining one-hot encoded values"""
    @delegates(ItemTransform.__init__)
    @delegates(TSMultiLabelClassification.__init__)
    def __init__(self, **kwargs):
        """Classifies list of per-annotation multi-labels,
        returns (L(tensor) contining one-hot encoded values"""
        super().__init__(TSMultiLabelClassification, **kwargs)

# Cell
class TSAnnotationWindow(ItemTransform):
    """Extract subsequence corresponding to annotation and selects appropriate label"""
    @delegates(ItemTransform.__init__)
    def __init__(self, width=100, offset=None, x_out_type=None, y_out_type=None,
                 default_label=None, verbose=False, classifier=None, **kwargs):
        """Extract subsequence corresponding to annotation and selects appropriate label"""
        self.width, self.offset, self.verbose = width, offset, verbose
        self.x_out_type, self.y_out_type, self.default_label = x_out_type, y_out_type, default_label
        self.y_mode, self.classifier = None, classifier
        super().__init__(**kwargs)


    def _setup(self, o):
        o_x, o_y = o
        self.is_batch =  len(o_x.data.shape) == 3 and o_x.data.shape[0] > 1
        if self.is_batch:
            o_y = o_y[0]  # loot at first row of y values when assessing type

        if isinstance(o_y, (TensorCategory, TensorMultiCategory)):
            self.y_mode = 'common'
        elif isinstance(o_y, (TSTensor, Tensor)):
            self.y_mode = 'tensor'
            if not self.y_out_type and isinstance(o_y, TSTensor):
                self.y_out_type = TSTensor
        elif isinstance(o_y, np.ndarray):
            self.y_mode = 'numpy'
            if not self.y_out_type:
                self.y_out_type = TSTensor
        elif isinstance(o_y, (list, tuple, L)):
            self.y_mode = 'per_ann'
        else:
            raise Exception(f'Unknown y type: {type(o_y)}')
        if self.x_out_type is None:
            self.x_out_type = TSTensor


    def encodes(self, o):
        o_x, o_y = o
        if self.y_mode is None:
            self._setup(o)
        #o_y = L(*o_y) if isinstance(o_y, list) else o_y

        windows = [z for z in _sliding_annotations(o_x.ann, o_x.data.size()[-1], width=self.width,
                                              offset=self.offset, include_empty=True, limit=1)]
        output_x = torch.stack([o_x[i_row, :, idxL:idxR] for i_row, idxL, idxR, idx_a in windows] )
        output_x = self.x_out_type(output_x)

        if self.y_mode == 'common':
            output_y = o_y
        elif self.y_mode == 'tensor':
            output_y = torch.stack([o_y[i_row, ..., idxL:idxR]
                                    for i_row, idxL, idxR, idx_a in windows] )
            if self.y_out_type:
                output_y = self.y_out_type(output_y)
            elif isinstance(o_y, TSTensor):
                output_y = o_y.new(output_y)
        elif self.y_mode == 'numpy':
            output_y = np.vstack([o_y[i_row][..., idxL:idxR]
                                    for i_row, idxL, idxR, idx_a in windows] )
            if self.y_out_type:
                output_y = self.y_out_type(output_y)
            elif isinstance(o_y, TSTensor):
                output_y = o_y.new(output_y)
        elif self.y_mode == 'per_ann':
            default_label = self.classifier.encode(self.default_label) \
                            if self.classifier and self.default_label else None
            output_y =  [o_y[i_row][idx_a] if idx_a else default_label if default_label \
                         else o_y[i_row][idx_a]
                         for i_row, idxL, idxR, idx_a in windows]
        else:
            raise exception(f'unknown y_mode: {self.y_mode}')

        if self.y_mode in ('per_ann', 'common'):
            if self.y_out_type:
                output_y = self.y_out_type(output_y)
            else:
                output_y = output_y[0].new(output_y)

        return output_x, output_y

# Internal Cell
def _tfm_to_list(entry):
    if isinstance(entry, (tuple, list, L)):
        return list(*entry)
    elif entry:
        return [entry]
    else:
        return []

# Internal Cell
def _merge_transforms(prefix_tfms, existing_tfms):
    prefix_tfms = [_tfm_to_list(prefix_tfms[0]) , _tfm_to_list(prefix_tfms[1])] \
                   if prefix_tfms else [[], []]
    existing_tfms = [tfm_to_list(existing_tfms[0]) , tfm_to_list(existing_tfms[1])] \
                     if existing_tfms else [[], []]
    return [prefix_tfms[0]+existing_tfms[0], prefix_tfms[1]+existing_tfms[1]]

# Cell
@delegates(get_ats_dls)
def get_ats_dls_with_transforms(X, y=None, per_ann_label=False,
                                window_width=100, window_offset=None,
                                default_label=None,tfms=None, batch_tfms=None,
                                **kwargs):
    """Returns TSAnnotatedDataLoaders with pre-configured transforms based on
    label types.  Also includes classifier to decode/encode individual label"""
    is_vector = isinstance(y[0], (np.ndarray, Tensor))

    if is_vector:
        tfm_y = ToTSTensor
        classifier = None
    elif per_ann_label: # per_ann
        if isinstance(y[0][0], (tuple, list, L)): # multi-category
            tfm_y = TSPerAnnMultiLabelClassification(default=default_label)
        else:
            tfm_y = TSPerAnnClassification(default=default_label)
        classifier = tfm_y.internal_classifier
    else:   # per-row
        if isinstance(y[0], (tuple, list, L)): # multi-category
            tfm_y = TSMultiLabelClassification()
        else:
            tfm_y = TSClassification()
        classifier = tfm_y

    if per_ann_label and default_label:
        tsaw = TSAnnotationWindow(width=window_width, offset=window_offset,
                                  default_label=default_label,
                                  classifier=tfm_y.internal_classifier)
    else:
        tsaw = TSAnnotationWindow(width=window_width, offset=window_offset)

    tfms = _merge_transforms([None, tfm_y], tfms)
    batch_tfms = _tfm_to_list(tsaw) + _tfm_to_list(batch_tfms)
    dls = get_ats_dls(X, y=y, tfms=tfms, batch_tfms=batch_tfms, **kwargs)
    return dls, classifier
# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/202_new_data.annotated.ipynb (unless otherwise specified).

__all__ = ['preprocess_extract_annotation_windows', 'pre_categorize', 'TSPerAnnotationGenericClassifier',
           'TSPerAnnClassification', 'TSPerAnnMultiLabelClassification', 'TSAnnotationWindow',
           'get_ats_dls_with_transforms']

# Cell
from tsai.imports import *
from tsai.utils import *
from tsai.data.external import *
from tsai.data.core import *
from tsai.data.preprocessing import *

# Cell
from .synthetic import get_synthetic_sin_data

# Internal Cell
def _sliding_annotations(all_ann, lengths, width=100, offset=None, limit=None, include_empty=False):
    lengths = [lengths]*len(all_ann) if isinstance(lengths, int) else lengths   # lengths for each row
    offset = offset if offset else -width//2
    offsetR = width + offset
    for i, row_anns in enumerate(all_ann):
        row_anns =  [(idx, e) for idx, e in enumerate(row_anns)
                     if e+offset >=0 and e+offsetR <  lengths[i]]  # trim overlap
        if len(row_anns) > 0:
            if limit and len(row_anns) > limit:
                random.shuffle(row_anns)
                row_anns = row_anns[:limit]
            for i_a, pos in row_anns:
                yield i, pos+offset, pos+offsetR, i_a
        elif include_empty:
            pos = random.randint(lengths[i]-width)
            yield i, pos, pos+width, None
        else:
            continue

# Internal Cell
def _compute_new_splits(m, orig_splits):
    return L( L(i for i, old_i in enumerate(m) if old_i in split_set)
              for split_set in orig_splits)

# Internal Cell
def _compute_new_folds(m, orig_folds):
    return orig_folds[m]

# Cell
@delegates(_sliding_annotations)
def preprocess_extract_annotation_windows(X, anns, y=None, splits=None, folds=None,
                                          per_ann_label=False, verbose=False, **kwargs):
    """Extracted timeseries subsequences and optionally label relative to annotated positions"""
    lengths = [x.shape[-1] for x in X]

    if isinstance(y, NoneType):
        if verbose: print('No Label')
        X_win, m = zip(*[[X[i_orig][..., idx_l:idx_r], i_orig]
            for (i_orig, idx_l, idx_r, _) in _sliding_annotations(anns, lengths, **kwargs)])
        y_win = None
    elif per_ann_label:
        if verbose: print('label per annotation')
        X_win, y_win, m = zip(*[[X[i_orig][..., idx_l:idx_r], y[i_orig][i_a], i_orig]
            for (i_orig, idx_l, idx_r, i_a) in _sliding_annotations(anns, lengths, **kwargs)])
        y_win = L(*y_win)
    elif isinstance(y[0], np.ndarray):
        if verbose: print('label is vector')
        assert y[0].shape[-1] == X[0].shape[-1]
        X_win, y_win, m = zip(*[[X[i_orig][..., idx_l:idx_r], y[i_orig][idx_l:idx_r], i_orig]
            for (i_orig, idx_l, idx_r, _) in _sliding_annotations(anns, lengths, **kwargs)])
        y_win = np.array(y_win)
    else:
        if verbose: print('label per row')
        X_win, y_win, m = zip(*[[X[i_orig][..., idx_l:idx_r], y[i_orig], i_orig]
            for (i_orig, idx_l, idx_r, _) in _sliding_annotations(anns, lengths, **kwargs)])
        y_win = L(*y_win)

    ret = [np.array(X_win)]
    if not isinstance(y_win, NoneType):
        ret.append(y_win)

    if not isinstance(splits, NoneType):
        return [*ret, _compute_new_splits(m, splits)]
    elif not isinstance(folds, NoneType):
        return [*ret, _compute_new_folds(m, folds)]
    elif len(ret) == 1:
        return ret[0]
    else:
        return ret

# Cell
def pre_categorize(*args, categorizer=None, splits=None, default=None):
    """Applies categorizer to list or nest list of labels.  Used
    to preprocess per-annotation labels"""
    is_nested = isinstance(args[0][0], (tuple, list, L))
    if categorizer is None: categorizer=Categorize
    if inspect.isclass(categorizer):
        y_train = args[0][splits[0]] if splits else args[0]
        vocab = {y_per for y_row in y_train for y_per in y_row} \
                if is_nested else {y_row for y_row in y_train}
        if default: vocab.add(default)
        c = categorizer()
        c.setups(vocab)
    else:
        c = categorizer
    if is_nested:
        results = [[[c(y_per) for y_per in y_row] for y_row in y_set] for y_set in args]
    else:
        results = [[c(y_row) for y_row in y_set] for y_set in args]
    results = results[0] if len(args) == 1 else results
    return results, c

# Cell
class TSPerAnnotationGenericClassifier(DisplayedTransform):
    """Common base class supporting slassification of list of Labels"""
    def __init__(self, classifier, default=None, **kwargs):
        assert classifier is not None
        self.classifier = classifier(**kwargs) if inspect.isclass(classifier) else classifier
        self.default=default

    @property
    def vocab(self):
        """Returns vocabulary with map"""
        return self.classifier.vocab

    @property
    def internal_classifier(self):
        """Returns underlying classifier (TSClassification or TSMultiLabelClassification)"""
        return self.classifier

    def setups(self, dsets):
        if self.classifier.vocab is None and dsets is not None:
            label_values = [label for row in dsets for label in row]
            if self.default: label_values.append(label_values)
            self.classifier.setups(label_values)

    def encodes(self, o):
        return L(self.classifier.encodes(oo) for oo in o)

    def decodes(self, o):
        return L(self.classifier.decodes(oo) for oo in o)

# Cell
class TSPerAnnClassification(TSPerAnnotationGenericClassifier):
    """Classifies list of per-annotation labels, returns L(TensorCategory)"""
    @delegates(TSPerAnnotationGenericClassifier.__init__)
    @delegates(TSClassification.__init__)
    def __init__(self, **kwargs):
        """Classifies list of per-annotation labels, returns L(TensorCategory)"""
        super().__init__(TSClassification, **kwargs)

# Cell
class TSPerAnnMultiLabelClassification(TSPerAnnotationGenericClassifier):
    """Classifies list of per-annotation multi-labels,
    returns (L(tensor) contining one-hot encoded values"""
    @delegates(ItemTransform.__init__)
    @delegates(TSMultiLabelClassification.__init__)
    def __init__(self, **kwargs):
        """Classifies list of per-annotation multi-labels,
        returns (L(tensor) contining one-hot encoded values"""
        super().__init__(TSMultiLabelClassification, **kwargs)

# Cell
class TSAnnotationWindow(ItemTransform):
    """Extract subsequence corresponding to annotation and selects appropriate label"""
    @delegates(ItemTransform.__init__)
    def __init__(self, width=100, offset=None, x_out_type=None, y_out_type=None,
                 default_label=None, verbose=False, classifier=None, **kwargs):
        """Extract subsequence corresponding to annotation and selects appropriate label"""
        self.width, self.offset, self.verbose = width, offset, verbose
        self.x_out_type, self.y_out_type, self.default_label = x_out_type, y_out_type, default_label
        self.y_mode, self.classifier = None, classifier
        super().__init__(**kwargs)


    def _setup(self, o):
        o_x, o_y = o
        self.is_batch =  len(o_x.data.shape) == 3 and o_x.data.shape[0] > 1
        if self.is_batch:
            o_y = o_y[0]  # loot at first row of y values when assessing type

        if isinstance(o_y, (TensorCategory, TensorMultiCategory)):
            self.y_mode = 'common'
        elif isinstance(o_y, (TSTensor, Tensor)):
            self.y_mode = 'tensor'
            if not self.y_out_type and isinstance(o_y, TSTensor):
                self.y_out_type = TSTensor
        elif isinstance(o_y, np.ndarray):
            self.y_mode = 'numpy'
            if not self.y_out_type:
                self.y_out_type = TSTensor
        elif isinstance(o_y, (list, tuple, L)):
            self.y_mode = 'per_ann'
        else:
            raise Exception(f'Unknown y type: {type(o_y)}')
        if self.x_out_type is None:
            self.x_out_type = TSTensor


    def encodes(self, o):
        o_x, o_y = o
        if self.y_mode is None:
            self._setup(o)
        #o_y = L(*o_y) if isinstance(o_y, list) else o_y

        windows = [z for z in _sliding_annotations(o_x.ann, o_x.data.size()[-1], width=self.width,
                                              offset=self.offset, include_empty=True, limit=1)]
        output_x = torch.stack([o_x[i_row, :, idxL:idxR] for i_row, idxL, idxR, idx_a in windows] )
        output_x = self.x_out_type(output_x)

        if self.y_mode == 'common':
            output_y = o_y
        elif self.y_mode == 'tensor':
            output_y = torch.stack([o_y[i_row, ..., idxL:idxR]
                                    for i_row, idxL, idxR, idx_a in windows] )
            if self.y_out_type:
                output_y = self.y_out_type(output_y)
            elif isinstance(o_y, TSTensor):
                output_y = o_y.new(output_y)
        elif self.y_mode == 'numpy':
            output_y = np.vstack([o_y[i_row][..., idxL:idxR]
                                    for i_row, idxL, idxR, idx_a in windows] )
            if self.y_out_type:
                output_y = self.y_out_type(output_y)
            elif isinstance(o_y, TSTensor):
                output_y = o_y.new(output_y)
        elif self.y_mode == 'per_ann':
            default_label = self.classifier.encode(self.default_label) \
                            if self.classifier and self.default_label else None
            output_y =  [o_y[i_row][idx_a] if idx_a else default_label if default_label \
                         else o_y[i_row][idx_a]
                         for i_row, idxL, idxR, idx_a in windows]
        else:
            raise exception(f'unknown y_mode: {self.y_mode}')

        if self.y_mode in ('per_ann', 'common'):
            if self.y_out_type:
                output_y = self.y_out_type(output_y)
            else:
                output_y = output_y[0].new(output_y)

        return output_x, output_y

# Internal Cell
def _tfm_to_list(entry):
    if isinstance(entry, (tuple, list, L)):
        return list(*entry)
    elif entry:
        return [entry]
    else:
        return []

# Internal Cell
def _merge_transforms(prefix_tfms, existing_tfms):
    prefix_tfms = [_tfm_to_list(prefix_tfms[0]) , _tfm_to_list(prefix_tfms[1])] \
                   if prefix_tfms else [[], []]
    existing_tfms = [tfm_to_list(existing_tfms[0]) , tfm_to_list(existing_tfms[1])] \
                     if existing_tfms else [[], []]
    return [prefix_tfms[0]+existing_tfms[0], prefix_tfms[1]+existing_tfms[1]]

# Cell
@delegates(get_ats_dls)
def get_ats_dls_with_transforms(X, y=None, per_ann_label=False,
                                window_width=100, window_offset=None,
                                default_label=None,tfms=None, batch_tfms=None,
                                **kwargs):
    """Returns TSAnnotatedDataLoaders with pre-configured transforms based on
    label types.  Also includes classifier to decode/encode individual label"""
    is_vector = isinstance(y[0], (np.ndarray, Tensor))

    if is_vector:
        tfm_y = ToTSTensor
        classifier = None
    elif per_ann_label: # per_ann
        if isinstance(y[0][0], (tuple, list, L)): # multi-category
            tfm_y = TSPerAnnMultiLabelClassification(default=default_label)
        else:
            tfm_y = TSPerAnnClassification(default=default_label)
        classifier = tfm_y.internal_classifier
    else:   # per-row
        if isinstance(y[0], (tuple, list, L)): # multi-category
            tfm_y = TSMultiLabelClassification()
        else:
            tfm_y = TSClassification()
        classifier = tfm_y

    if per_ann_label and default_label:
        tsaw = TSAnnotationWindow(width=window_width, offset=window_offset,
                                  default_label=default_label,
                                  classifier=tfm_y.internal_classifier)
    else:
        tsaw = TSAnnotationWindow(width=window_width, offset=window_offset)

    tfms = _merge_transforms([None, tfm_y], tfms)
    batch_tfms = _tfm_to_list(tsaw) + _tfm_to_list(batch_tfms)
    dls = get_ats_dls(X, y=y, tfms=tfms, batch_tfms=batch_tfms, **kwargs)
    return dls, classifier
# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/202_new_data.annotated.ipynb (unless otherwise specified). # Cell # Cell # Internal Cell # lengths for each row # trim overlap # Internal Cell # Internal Cell # Cell Extracted timeseries subsequences and optionally label relative to annotated positions # Cell Applies categorizer to list or nest list of labels.  Used     to preprocess per-annotation labels # Cell Common base class supporting slassification of list of Labels Returns vocabulary with map Returns underlying classifier (TSClassification or TSMultiLabelClassification) # Cell Classifies list of per-annotation labels, returns L(TensorCategory) Classifies list of per-annotation labels, returns L(TensorCategory) # Cell Classifies list of per-annotation multi-labels,     returns (L(tensor) contining one-hot encoded values Classifies list of per-annotation multi-labels,         returns (L(tensor) contining one-hot encoded values # Cell Extract subsequence corresponding to annotation and selects appropriate label Extract subsequence corresponding to annotation and selects appropriate label # loot at first row of y values when assessing type #o_y = L(*o_y) if isinstance(o_y, list) else o_y # Internal Cell # Internal Cell # Cell Returns TSAnnotatedDataLoaders with pre-configured transforms based on     label types.  Also includes classifier to decode/encode individual label # per_ann # multi-category # per-row # multi-category