Beispiel #1
0
def eval_metric(metric_name_or_func,
                *args,
                remove_unused_kwargs=False,
                **kwargs):
    metric = metric_name_or_func

    if is_str(metric):
        metric = _get_metric_func_by_name(metric)
    if callable(metric):
        if remove_unused_kwargs:
            kwargs = filter_unused_kwargs(metric, kwargs)
        metric_value = metric(*args, **kwargs)
        result = metric_value
    else:
        raise ValueError('Unknown metric: {}'.format(metric))

    return result
Beispiel #2
0
def eval_metrics(metric_names_or_funcs,
                 *args,
                 remove_unused_kwargs=False,
                 **kwargs):
    results = {}
    throw_out = kwargs.pop('throw_out', 0)  # TODO throw out first x samples...

    for metric in metric_names_or_funcs:
        if throw_out:  # TODO
            args = list(args)
            for i, arg in enumerate(args):  # TODO
                arg = arg[throw_out:]  # TODO
                args[i] = arg  # TODO :'(
        metric_value = eval_metric(metric,
                                   *args,
                                   remove_unused_kwargs=remove_unused_kwargs,
                                   **kwargs)
        if not is_str(metric):
            metric = _get_metric_name_by_func(metric)
        results[metric] = metric_value

    return results
Beispiel #3
0
def get_cv(cv=3,
           n_repeats=None,
           y=None,
           classification=None,
           random_state=None,
           shuffle=True):
    """Input checker utility for building a cross-validator

    Args:
        cv:        int, cross-validation generator or an iterable
                   Determines the cross-validation splitting strategy.
                   Possible inputs for cv are:
                   - None, to use the default 3-fold cross-validation,
                   - integer, to specify the number of folds.
                   - An object to be used as a cross-validation generator.
                   - An iterable yielding train/test splits.
                   For integer/None inputs, if classification is True and
                   `y` is either binary or multiclass, `StratifiedKFold` is
                   used. In all other cases, `KFold` is used.
        n_repeats: The number of times to repeat splits.
        y:         The target variable for supervised learning problems.
        classification:
                   Whether the task is a classification task, in which case
                   stratified KFold will be used. Infers based on `y` if
                   specified and `classification` is None.
        random_state:
                   Random state to be used to generate random state for each
                   repetition.
        shuffle:   Whether to shuffle.

    Returns:
        A cross-validator instance that generates the train/test splits via
        its `split` method.
    """
    if cv is None:
        cv = 3
    if n_repeats is None:
        n_repeats = 1
    elif n_repeats > 1 and not shuffle:
        raise ValueError('shuffle cannot be False with more than 1 CV '
                         'repetition.')

    if is_int(cv, ignore_unsigned=False):
        # Infer classification if None and y is provided
        if classification is None:
            if ((y is not None)
                    and (type_of_target(y) in ('binary', 'multiclass'))):
                classification = True
            else:
                classification = False
        # Select appropriate CV type
        if classification:
            if n_repeats == 1:
                return StratifiedKFold(n_splits=cv,
                                       shuffle=shuffle,
                                       random_state=random_state)
            else:
                return RepeatedStratifiedKFold(n_splits=cv,
                                               n_repeats=n_repeats,
                                               random_state=random_state)
        else:
            if n_repeats == 1:
                return KFold(n_splits=cv,
                             shuffle=shuffle,
                             random_state=random_state)
            else:
                return RepeatedKFold(n_splits=cv,
                                     n_repeats=n_repeats,
                                     random_state=random_state)

    # For iterables and error-handling (rely on sklearn)
    if not hasattr(cv, 'split') or is_str(cv):
        return check_cv(cv)

    return cv
Beispiel #4
0
def get_most_recent_k_in_dir(dirname,
                             k,
                             delim='_',
                             ext=None,
                             raise_=False,
                             strict=False,
                             return_fns=False):
    if is_str(dirname):
        # Note this checks files only.
        if not raise_ and not os.path.isdir(dirname):
            return None

        dirs = os.listdir(dirname)  # Exception can be raised here

        if not dirs:

            if raise_:
                raise EmptyDirError('Directory "{}" cannot be '
                                    'empty.'.format(dirname))
            else:
                return None

        filenames = os.listdir(dirname)
    else:
        # Assume iterable
        filenames = dirname

    fn_dt_dict = {}

    for filename in filenames:

        if os.path.isfile(filename):

            filename_raw = filename

            if ext is not None:
                suffix = '.' + ext
                if not filename.endswith(suffix):
                    continue

                filename = filename[:-len(suffix)]

            if delim is not None:
                split = filename.rsplit(delim, 1)

                if len(split) != 2:
                    continue

                filename = split[1]

            try:
                dt = date_parser.parse(filename)
            except ValueError:
                continue

            fn_dt_dict[filename_raw] = dt

    if not fn_dt_dict and raise_:
        raise EmptyDirError(
            'Directory "{}" does not contain any files with {}{}a valid '
            'timestamp.'.format(
                dirname, 'extension "{}" and '.format(ext) if ext else '',
                'delimiter "{}" and '.format(delim) if delim else ''))

    most_recent_fns = sorted(fn_dt_dict, key=fn_dt_dict.get, reverse=True)[:k]

    if strict and len(most_recent_fns) != k:
        raise ValueError(
            'Directory "{}" does not contain {} files with {}{}a valid '
            'timestamp.'.format(
                dirname, k, 'extension "{}" and '.format(ext) if ext else '',
                'delimiter "{}" and '.format(delim) if delim else ''))

    if return_fns:
        return most_recent_fns, list(fn_dt_dict.keys())
    else:
        return most_recent_fns
Beispiel #5
0
def train_and_eval(X,
                   y,
                   model,
                   train=True,
                   test=True,
                   train_func=None,
                   test_func=None,
                   cv=None,
                   iid=True,
                   return_train_score=False,
                   metrics=None,
                   target_metric=None,
                   time_series=False,
                   eval_kwargs=None):
    """

    Args:
        X:
        y:
        model:
        train:
        test:
        train_func:
        test_func:
        cv:
        iid:
        return_train_score:
        metrics:
        target_metric:
        time_series:
        eval_kwargs:

    Returns:

    """
    if return_train_score:
        raise NotImplementedError  # TODO

    if not metrics or target_metric not in metrics:
        raise ValueError('Invalid specification of metrics for evaluation.')

    if target_metric is None:
        if len(metrics) == 1:  # TODO nonetype metrics...
            target_metric = metrics[0]
        else:
            raise ValueError('"target_metric" must be provided if multiple '
                             'metrics specified.')

    eval_kwargs = eval_kwargs or {}

    if type(X) is type(y) is tuple and len(X) == len(
            y
    ) == 2:  # TODO this good? for manual split specification...maybe require wrapping in object...
        splits = [(X, y)]
        cv = False
    else:
        cv = get_cv(cv)
        splits = cv.split(X, y)

    # Check specified train/eval functions
    if isinstance(
            model, SKBaseEstimator
    ):  # TODO you also know that it has the {set, get}_params methods...
        if train and train_func is None:
            if hasmethod(model, DEFAULT_SKLEARN_TRAIN_FUNC):
                train_func = getattr(model, DEFAULT_SKLEARN_TRAIN_FUNC)
            else:
                raise ValueError('Could not infer the train method from the '
                                 'specified sklearn model. Please specify '
                                 '"train_func" in order to train the model.')
        if test and test_func is None:
            if hasmethod(model, DEFAULT_SKLEARN_TEST_PROBA_FUNC):
                test_func = getattr(model, DEFAULT_SKLEARN_TEST_PROBA_FUNC)
            elif hasmethod(model, DEFAULT_SKLEARN_TEST_FUNC):
                test_func = getattr(model, DEFAULT_SKLEARN_TEST_FUNC)
            else:
                raise ValueError('Could not infer the predict method from the '
                                 'specified sklearn model. Please specify '
                                 '"test_func" in order to test the model.')
    elif isinstance(model, BaseModel):
        raise NotImplementedError  # TODO
    else:
        if train and train_func is None:
            # Take a stab at it
            if hasmethod(model, DEFAULT_MODEL_TRAIN_FUNC):
                train_func = getattr(model, DEFAULT_MODEL_TRAIN_FUNC)
            else:
                raise ValueError(
                    'Could not infer model train function: please '
                    'specify "train_func" in order to train the '
                    'model.')
        if test and test_func is None:
            # Take a stab at it
            if hasmethod(model, DEFAULT_MODEL_TEST_FUNC):
                test_func = getattr(model, DEFAULT_MODEL_TEST_FUNC)
            else:
                raise ValueError('Could not infer model test function: please '
                                 'specify "test_func" in order to test the '
                                 'model.')

    if is_str(train_func):
        train_func = getattr(model, train_func)
    if is_str(test_func):
        test_func = getattr(model, test_func)

    test_score = 0.
    test_scores = None
    n_test = 0

    for train_idx_or_xs, test_idx_or_ys in splits:

        if len(train_idx_or_xs) <= 0 or len(test_idx_or_ys) <= 0:
            raise ValueError(
                'Train and test must be long enough for specified '
                'cross validation.')

        if cv:
            X_train = X[train_idx_or_xs]
            y_train = y[train_idx_or_xs]

            X_test = X[test_idx_or_ys]
            y_test = y[test_idx_or_ys]
        else:
            X_train, X_test = train_idx_or_xs
            y_train, y_test = test_idx_or_ys

        if time_series:
            test_len = X_test.shape[2]
        else:
            test_len = len(X_test)

        if train_func is not None:
            # Train the model
            y_train_pred = train_func(X_train, y_train)
        if test_func is not None:
            y_test_pred = test_func(X_test)
            test_scores = eval_metrics(metrics, y_test, y_test_pred,
                                       **eval_kwargs)

            print(test_scores)  # TODO

            target_score = test_scores[target_metric]

            if iid:
                test_score += target_score * test_len
                n_test += test_len
            else:
                test_score += target_score
                n_test += 1

    if n_test == 0:
        raise ValueError('No evaluation was done (n_test = 0).')

    test_score /= n_test

    return test_score, test_scores  # TODO