# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
# --------------------------------------------------------------------------------------------
# - Generated by tools/entrypoint_compiler.py: do not edit by hand
"""
Binner
"""

__all__ = ["Binner"]


from ....entrypoints.transforms_binnormalizer import transforms_binnormalizer
from ....utils.utils import trace
from ...base_pipeline_item import BasePipelineItem, DefaultSignature


class Binner(BasePipelineItem, DefaultSignature):
    """

    Normalizes columns as specified below.

    .. remarks::
        In linear classification algorithms instances are viewed as vectors
        in
        multi-dimensional space. Since the range of values of raw data varies
        widely, some objective functions do not work properly without
        normalization. For example, if one of the features has a broad range
        of
        values, the distances between points is governed by this particular
        feature. Therefore, the range of all features should be normalized so
        that each feature contributes approximately proportionately to the
        final
        distance. This can provide significant speedup and accuracy benefits.
        In
        all the linear algorithms in nimbusml (:py:class:`Logistic Regression
        <nimbusml.linear_model.LogisticRegressionClassifier>`,
        :py:class:`Averaged Perceptron
        <nimbusml.linear_model.AveragedPerceptronBinaryClassifier>`, etc.),
        the default is to normalize features before training.

        ``Binner`` creates equi-density bins, and then normalizes every
        value in the bin to be divided by the total number of bins. The
        number of bins the normalizer uses can be defined by the user, and
        the
        default is 1000.

    :param num_bins: Max number of bins, power of 2 recommended.

    :param fix_zero: Whether to map zero to zero, preserving sparsity.

    :param max_training_examples: Max number of examples used to train the
        normalizer.

    :param params: Additional arguments sent to compute engine.

    .. seealso::
        :py:class:`MinMaxScaler
        <nimbusml.preprocessing.normalization.MinMaxScaler>`,
        :py:class:`MeanVarianceScaler
        <nimbusml.preprocessing.normalization.MeanVarianceScaler>`,
        :py:class:`LogMeanVarianceScaler
        <nimbusml.preprocessing.normalization.LogMeanVarianceScaler>`,
        :py:class:`GlobalContrastRowScaler
        <nimbusml.preprocessing.normalization.GlobalContrastRowScaler>`.

    .. index:: normalize, preprocessing

    Example:
       .. literalinclude:: /../nimbusml/examples/Binner.py
              :language: python
    """

    @trace
    def __init__(
            self,
            num_bins=1024,
            fix_zero=True,
            max_training_examples=1000000000,
            **params):
        BasePipelineItem.__init__(
            self, type='transform', **params)

        self.num_bins = num_bins
        self.fix_zero = fix_zero
        self.max_training_examples = max_training_examples

    @property
    def _entrypoint(self):
        return transforms_binnormalizer

    @trace
    def _get_node(self, **all_args):

        input_columns = self.input
        if input_columns is None and 'input' in all_args:
            input_columns = all_args['input']
        if 'input' in all_args:
            all_args.pop('input')

        output_columns = self.output
        if output_columns is None and 'output' in all_args:
            output_columns = all_args['output']
        if 'output' in all_args:
            all_args.pop('output')

        # validate input
        if input_columns is None:
            raise ValueError(
                "'None' input passed when it cannot be none.")

        if not isinstance(input_columns, list):
            raise ValueError(
                "input has to be a list of strings, instead got %s" %
                type(input_columns))

        # validate output
        if output_columns is None:
            output_columns = input_columns

        if not isinstance(output_columns, list):
            raise ValueError(
                "output has to be a list of strings, instead got %s" %
                type(output_columns))

        algo_args = dict(
            column=[
                dict(
                    Source=i,
                    Name=o) for i,
                o in zip(
                    input_columns,
                    output_columns)] if input_columns else None,
            num_bins=self.num_bins,
            fix_zero=self.fix_zero,
            max_training_examples=self.max_training_examples)

        all_args.update(algo_args)
        return self._entrypoint(**all_args)
