<commit_before># -*- encoding: utf-8 -*-
from __future__ import division, print_function, absolute_import, unicode_literals

import itertools

import h2o
from h2o.job import H2OJob
from h2o.frame import H2OFrame
from h2o.exceptions import H2OValueError
from h2o.estimators.estimator_base import H2OEstimator
from h2o.two_dim_table import H2OTwoDimTable
from h2o.display import H2ODisplay
from h2o.grid.metrics import *  # NOQA
from h2o.utils.backward_compatibility import backwards_compatible
from h2o.utils.shared_utils import deprecated, quoted
from h2o.utils.compatibility import *  # NOQA
from h2o.utils.typechecks import assert_is_type, is_type


class H2OGridSearch(backwards_compatible()):
    """
    Grid Search of a Hyper-Parameter Space for a Model

    :param model: The type of model to be explored initialized with optional parameters that will be
        unchanged across explored models.
    :param hyper_params: A dictionary of string parameters (keys) and a list of values to be explored by grid
        search (values).
    :param str grid_id: The unique id assigned to the resulting grid object. If none is given, an id will
        automatically be generated.
    :param search_criteria:  A dictionary of directives which control the search of the hyperparameter space.
        The default strategy "Cartesian" covers the entire space of hyperparameter combinations. Specify the
        "RandomDiscrete" strategy to get random search of all the combinations of your hyperparameters.
        RandomDiscrete should usually be combined with at least one early stopping criterion: max_models
        and/or max_runtime_secs, e.g::

            >>> criteria = {"strategy": "RandomDiscrete", "max_models": 42,
            ...             "max_runtime_secs": 28800, "seed": 1234}
            >>> criteria = {"strategy": "RandomDiscrete", "stopping_metric": "AUTO",
            ...             "stopping_tolerance": 0.001, "stopping_rounds": 10}
            >>> criteria = {"strategy": "RandomDiscrete", "stopping_rounds": 5,
            ...             "stopping_metric": "misclassification",
            ...             "stopping_tolerance": 0.00001}
    :returns: a new H2OGridSearch instance

    Examples
    --------
        >>> from h2o.grid.grid_search import H2OGridSearch
        >>> from h2o.estimators.glm import H2OGeneralizedLinearEstimator
        >>> hyper_parameters = {'alpha': [0.01,0.5], 'lambda': [1e-5,1e-6]}
        >>> gs = H2OGridSearch(H2OGeneralizedLinearEstimator(family='binomial'), hyper_parameters)
        >>> training_data = h2o.import_file("smalldata/logreg/benign.csv")
        >>> gs.train(x=range(3) + range(4,11),y=3, training_frame=training_data)
        >>> gs.show()
    """


    def __init__(self, model, hyper_params, grid_id=None, search_criteria=None):
        super(H2OGridSearch, self).__init__()
        assert_is_type(model, None, H2OEstimator, lambda mdl: issubclass(mdl, H2OEstimator))
        assert_is_type(hyper_params, dict)
        assert_is_type(grid_id, None, str)
        assert_is_type(search_criteria, None, dict)
        if not (model is None or is_type(model, H2OEstimator)): model = model()
        self._id = grid_id
        self.model = model
        self.hyper_params = dict(hyper_params)
        self.search_criteria = None if search_criteria is None else dict(search_criteria)
        self._grid_json = None
        self.models = None  # list of H2O Estimator instances
        self._parms = {}  # internal, for object recycle #
        self.parms = {}  # external#
        self._future = False  # used by __repr__/show to query job state#
        self._job = None  # used when _future is True#


    @property
    def grid_id(self):
        """A key that identifies this grid search object in H2O."""
        return self._id

    @grid_id.setter
    def grid_id(self, value):
        oldname = self.grid_id
        self._id = value
        h2o.rapids('(rename "{}" "{}")'.format(oldname, value))


    @property
    def model_ids(self):
        return [i['name'] for i in self._grid_json["model_ids"]]


    @property
    def hyper_names(self):
        return self._grid_json["hyper_names"]


    @property
    def failed_params(self):
        return self._grid_json.get("failed_params", None)


    @property
    def failure_details(self):
        return self._grid_json.get("failure_details", None)


    @property
    def failure_stack_traces(self):
        return self._grid_json.get("failure_stack_traces", None)


    @property
    def failed_raw_params(self):
        return self._grid_json.get("failed_raw_params", None)


    def start(self, x, y=None, training_frame=None, offset_column=None, fold_column=None, weights_column=None,
              validation_frame=None, **params):
        """
        Asynchronous model build by specifying the predictor columns, response column, and any
        additional frame-specific values.

        To block for results, call :meth:`join`.

        :param x: A list of column names or indices indicating the predictor columns.
        :param y: An index or a column name indicating the response column.
        :param training_frame: The H2OFrame having the columns indicated by x and y (as well as any
            additional columns specified by fold, offset, and weights).
        :param offset_column: The name or index of the column in training_frame that holds the offsets.
        :param fold_column: The name or index of the column in training_frame that holds the per-row fold
            assignments.
        :param weights_column: The name or index of the column in training_frame that holds the per-row weights.
        :param validation_frame: H2OFrame with validation data to be scored on while training.
        """
        self._future = True
        self.train(x=x,
                   y=y,
                   training_frame=training_frame,
                   offset_column=offset_column,
                   fold_column=fold_column,
                   weights_column=weights_column,
                   validation_frame=validation_frame,
                   **params)


    def join(self):
        """Wait until grid finishes computing."""
        self._future = False
        self._job.poll()
        self._job = None


    def train(self, x=None, y=None, training_frame=None, offset_column=None, fold_column=None, weights_column=None,
              validation_frame=None, **params):
        """
        Train the model synchronously (i.e. do not return until the model finishes training).

        To train asynchronously call :meth:`start`.

        :param x: A list of column names or indices indicating the predictor columns.
        :param y: An index or a column name indicating the response column.
        :param training_frame: The H2OFrame having the columns indicated by x and y (as well as any
            additional columns specified by fold, offset, and weights).
        :param offset_column: The name or index of the column in training_frame that holds the offsets.
        :param fold_column: The name or index of the column in training_frame that holds the per-row fold
            assignments.
        :param weights_column: The name or index of the column in training_frame that holds the per-row weights.
        :param validation_frame: H2OFrame with validation data to be scored on while training.
        """
        algo_params = locals()
        parms = self._parms.copy()
        parms.update({k: v for k, v in algo_params.items() if k not in ["self", "params", "algo_params", "parms"]})
        parms["search_criteria"] = self.search_criteria
        parms["hyper_parameters"] = self.hyper_params  # unique to grid search
        parms.update({k: v for k, v in list(self.model._parms.items()) if v is not None})  # unique to grid search
        parms.update(params)
        if '__class__' in parms:  # FIXME: hackt for PY3
            del parms['__class__']
        y = algo_params["y"]
        tframe = algo_params["training_frame"]
        if tframe is None: raise ValueError("Missing training_frame")
        if y is not None:
            if is_type(y, list, tuple):
                if len(y) == 1:
                    parms["y"] = y[0]
                else:
                    raise ValueError('y must be a single column reference')
        if x is None:
            if(isinstance(y, int)):
                xset = set(range(training_frame.ncols)) - {y}
            else:
                xset = set(training_frame.names) - {y}
        else:
            xset = set()
            if is_type(x, int, str): x = [x]
            for xi in x:
                if is_type(xi, int):
                    if not (-training_frame.ncols <= xi < training_frame.ncols):
                        raise H2OValueError("Column %d does not exist in the training frame" % xi)
                    xset.add(training_frame.names[xi])
                else:
                    if xi not in training_frame.names:<commit_msg>Fix handling of dictionaries in grid search<commit_after># -*- encoding: utf-8 -*-
from __future__ import division, print_function, absolute_import, unicode_literals

import itertools

import h2o
from h2o.job import H2OJob
from h2o.frame import H2OFrame
from h2o.exceptions import H2OValueError
from h2o.estimators.estimator_base import H2OEstimator
from h2o.two_dim_table import H2OTwoDimTable
from h2o.display import H2ODisplay
from h2o.grid.metrics import *  # NOQA
from h2o.utils.backward_compatibility import backwards_compatible
from h2o.utils.shared_utils import deprecated, quoted
from h2o.utils.compatibility import *  # NOQA
from h2o.utils.typechecks import assert_is_type, is_type


class H2OGridSearch(backwards_compatible()):
    """
    Grid Search of a Hyper-Parameter Space for a Model

    :param model: The type of model to be explored initialized with optional parameters that will be
        unchanged across explored models.
    :param hyper_params: A dictionary of string parameters (keys) and a list of values to be explored by grid
        search (values).
    :param str grid_id: The unique id assigned to the resulting grid object. If none is given, an id will
        automatically be generated.
    :param search_criteria:  A dictionary of directives which control the search of the hyperparameter space.
        The default strategy "Cartesian" covers the entire space of hyperparameter combinations. Specify the
        "RandomDiscrete" strategy to get random search of all the combinations of your hyperparameters.
        RandomDiscrete should usually be combined with at least one early stopping criterion: max_models
        and/or max_runtime_secs, e.g::

            >>> criteria = {"strategy": "RandomDiscrete", "max_models": 42,
            ...             "max_runtime_secs": 28800, "seed": 1234}
            >>> criteria = {"strategy": "RandomDiscrete", "stopping_metric": "AUTO",
            ...             "stopping_tolerance": 0.001, "stopping_rounds": 10}
            >>> criteria = {"strategy": "RandomDiscrete", "stopping_rounds": 5,
            ...             "stopping_metric": "misclassification",
            ...             "stopping_tolerance": 0.00001}
    :returns: a new H2OGridSearch instance

    Examples
    --------
        >>> from h2o.grid.grid_search import H2OGridSearch
        >>> from h2o.estimators.glm import H2OGeneralizedLinearEstimator
        >>> hyper_parameters = {'alpha': [0.01,0.5], 'lambda': [1e-5,1e-6]}
        >>> gs = H2OGridSearch(H2OGeneralizedLinearEstimator(family='binomial'), hyper_parameters)
        >>> training_data = h2o.import_file("smalldata/logreg/benign.csv")
        >>> gs.train(x=range(3) + range(4,11),y=3, training_frame=training_data)
        >>> gs.show()
    """


    def __init__(self, model, hyper_params, grid_id=None, search_criteria=None):
        super(H2OGridSearch, self).__init__()
        assert_is_type(model, None, H2OEstimator, lambda mdl: issubclass(mdl, H2OEstimator))
        assert_is_type(hyper_params, dict)
        assert_is_type(grid_id, None, str)
        assert_is_type(search_criteria, None, dict)
        if not (model is None or is_type(model, H2OEstimator)): model = model()
        self._id = grid_id
        self.model = model
        self.hyper_params = dict(hyper_params)
        self.search_criteria = None if search_criteria is None else dict(search_criteria)
        self._grid_json = None
        self.models = None  # list of H2O Estimator instances
        self._parms = {}  # internal, for object recycle #
        self.parms = {}  # external#
        self._future = False  # used by __repr__/show to query job state#
        self._job = None  # used when _future is True#


    @property
    def grid_id(self):
        """A key that identifies this grid search object in H2O."""
        return self._id

    @grid_id.setter
    def grid_id(self, value):
        oldname = self.grid_id
        self._id = value
        h2o.rapids('(rename "{}" "{}")'.format(oldname, value))


    @property
    def model_ids(self):
        return [i['name'] for i in self._grid_json["model_ids"]]


    @property
    def hyper_names(self):
        return self._grid_json["hyper_names"]


    @property
    def failed_params(self):
        return self._grid_json.get("failed_params", None)


    @property
    def failure_details(self):
        return self._grid_json.get("failure_details", None)


    @property
    def failure_stack_traces(self):
        return self._grid_json.get("failure_stack_traces", None)


    @property
    def failed_raw_params(self):
        return self._grid_json.get("failed_raw_params", None)


    def start(self, x, y=None, training_frame=None, offset_column=None, fold_column=None, weights_column=None,
              validation_frame=None, **params):
        """
        Asynchronous model build by specifying the predictor columns, response column, and any
        additional frame-specific values.

        To block for results, call :meth:`join`.

        :param x: A list of column names or indices indicating the predictor columns.
        :param y: An index or a column name indicating the response column.
        :param training_frame: The H2OFrame having the columns indicated by x and y (as well as any
            additional columns specified by fold, offset, and weights).
        :param offset_column: The name or index of the column in training_frame that holds the offsets.
        :param fold_column: The name or index of the column in training_frame that holds the per-row fold
            assignments.
        :param weights_column: The name or index of the column in training_frame that holds the per-row weights.
        :param validation_frame: H2OFrame with validation data to be scored on while training.
        """
        self._future = True
        self.train(x=x,
                   y=y,
                   training_frame=training_frame,
                   offset_column=offset_column,
                   fold_column=fold_column,
                   weights_column=weights_column,
                   validation_frame=validation_frame,
                   **params)


    def join(self):
        """Wait until grid finishes computing."""
        self._future = False
        self._job.poll()
        self._job = None


    def train(self, x=None, y=None, training_frame=None, offset_column=None, fold_column=None, weights_column=None,
              validation_frame=None, **params):
        """
        Train the model synchronously (i.e. do not return until the model finishes training).

        To train asynchronously call :meth:`start`.

        :param x: A list of column names or indices indicating the predictor columns.
        :param y: An index or a column name indicating the response column.
        :param training_frame: The H2OFrame having the columns indicated by x and y (as well as any
            additional columns specified by fold, offset, and weights).
        :param offset_column: The name or index of the column in training_frame that holds the offsets.
        :param fold_column: The name or index of the column in training_frame that holds the per-row fold
            assignments.
        :param weights_column: The name or index of the column in training_frame that holds the per-row weights.
        :param validation_frame: H2OFrame with validation data to be scored on while training.
        """
        algo_params = locals()
        parms = self._parms.copy()
        parms.update({k: v for k, v in algo_params.items() if k not in ["self", "params", "algo_params", "parms"]})
        # dictionaries have special handling in grid search, avoid the implicit conversion
        parms["search_criteria"] = None if self.search_criteria is None else str(self.search_criteria)
        parms["hyper_parameters"] = None if self.hyper_params  is None else str(self.hyper_params) # unique to grid search
        parms.update({k: v for k, v in list(self.model._parms.items()) if v is not None})  # unique to grid search
        parms.update(params)
        if '__class__' in parms:  # FIXME: hackt for PY3
            del parms['__class__']
        y = algo_params["y"]
        tframe = algo_params["training_frame"]
        if tframe is None: raise ValueError("Missing training_frame")
        if y is not None:
            if is_type(y, list, tuple):
                if len(y) == 1:
                    parms["y"] = y[0]
                else:
                    raise ValueError('y must be a single column reference')
        if x is None:
            if(isinstance(y, int)):
                xset = set(range(training_frame.ncols)) - {y}
            else:
                xset = set(training_frame.names) - {y}
        else:
            xset = set()
            if is_type(x, int, str): x = [x]
            for xi in x:
                if is_type(xi, int):
                    if not (-training_frame.ncols <= xi < training_frame.ncols):
                        raise H2OValueError("Column %d does not exist in the training frame" % xi)
                    xset.add(training_frame.names[xi])
                else:
                    if xi not in training_frame.names:
{'fraction_non_alphanumeric': 0.08710494438938443, 'fraction_numerical': 0.008919722497522299, 'mean_word_length': 2.552317621748484, 'pattern_counts': {'":': 24, '<': 7, '<?xml version=': 0, '>': 63, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7564124', 'n_tokens_mistral': 5233, 'n_tokens_neox': 5034, 'n_words': 1680}