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

__all__ = ["MinMaxScaler"]


from ....entrypoints.transforms_minmaxnormalizer import \
    transforms_minmaxnormalizer
from ....utils.utils import trace
from ...base_pipeline_item import BasePipelineItem, DefaultSignature


class MinMaxScaler(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.

        ``MinMaxScaler`` is the default normalizer for many `nimbusml`
        algorithms
        and linearly rescales every feature to the [0,1] or the [-1,1]
        interval.
        Rescaling to the [0,1] interval is done by shifting the values of
        each
        feature so that the minimal value is 0, and then dividing by the new
        maximal value (which is the difference between the original maximal
        and
        minimal values). Rescaling to the [-1,1] interval is done by dividing
        the values of each feature by the maximal absolute value of the
        feature.
        This method is useful for preserving the sparsity of a dataset, since
        0
        values do not change. The scaling method can be specified by setting
        the
        `fix_zero` to `False` for the first method, or setting it to `True`
        for
        the second method.

    :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.

    .. note::

        *MinMaxScaler* as many other transforms requires input to be of
        numeric type.
        It will fail for other types. Most of the times, features are
        float but a column could be unexpectedly of type string. That
        explains why
        the following code raises an exception.
        ::
            in_df = pandas.DataFrame(data=dict(Sepal_Length=["2,2", 1, 2,
        1]))
            normed = MinMaxScaler() << [Sepal_Length']
            normed.fit_transform(in_df)

        The displayed message is::

            'Source column 'Petal_Length' has invalid type ('TX'): Expected
        R4 or R8 item type.

        The input column must be converted into float or double in the
        dataframe
        before running the pipeline or inside the pipeline with transform
        :py:class:`TypeConverter <nimbusml.preprocessing.schema.TypeConverter>`.
        This transform is automatically added in case of integers.

    .. seealso::
        :py:class:`Binner
        <nimbusml.preprocessing.normalization.Binner>`,
        :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/MinMaxScaler.py
              :language: python
    """

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

        self.fix_zero = fix_zero
        self.max_training_examples = max_training_examples

    @property
    def _entrypoint(self):
        return transforms_minmaxnormalizer

    @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,
            fix_zero=self.fix_zero,
            max_training_examples=self.max_training_examples)

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