diff --git a/README.md b/README.md index 76a9056bc..d6eeda624 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ Please share your story by answering 1 quick question * ArcSinhTransformer ### Variable Scaling methods -* MeanNormalizationScaler +* MeanNormalisationScaler ### Variable Creation: * MathFeatures diff --git a/docs/api_doc/scaling/MeanNormalisationScaler.rst b/docs/api_doc/scaling/MeanNormalisationScaler.rst new file mode 100644 index 000000000..2cc963470 --- /dev/null +++ b/docs/api_doc/scaling/MeanNormalisationScaler.rst @@ -0,0 +1,6 @@ +MeanNormalisationScaler +======================= + +.. autoclass:: feature_engine.scaling.MeanNormalisationScaler + :members: + diff --git a/docs/api_doc/scaling/MeanNormalizationScaler.rst b/docs/api_doc/scaling/MeanNormalizationScaler.rst deleted file mode 100644 index a7ed38bd8..000000000 --- a/docs/api_doc/scaling/MeanNormalizationScaler.rst +++ /dev/null @@ -1,6 +0,0 @@ -MeanNormalizationScaler -======================= - -.. autoclass:: feature_engine.scaling.MeanNormalizationScaler - :members: - diff --git a/docs/api_doc/scaling/index.rst b/docs/api_doc/scaling/index.rst index 892925f38..d49af7f1c 100644 --- a/docs/api_doc/scaling/index.rst +++ b/docs/api_doc/scaling/index.rst @@ -9,4 +9,4 @@ given columns. .. toctree:: :maxdepth: 1 - MeanNormalizationScaler + MeanNormalisationScaler diff --git a/docs/index.rst b/docs/index.rst index 6a05f7e9e..8c35fe84a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -328,7 +328,7 @@ procedures to a subset of the variables only, check out the :doc:`api_doc/wrappe Feature-engine extends scikit-learn's scaling functionality with the following transformer: -- :doc:`api_doc/scaling/MeanNormalizationScaler`: scale variables using mean normalisation +- :doc:`api_doc/scaling/MeanNormalisationScaler`: scale variables using mean normalisation Scikit-learn Wrapper: diff --git a/docs/user_guide/scaling/MeanNormalizationScaler.rst b/docs/user_guide/scaling/MeanNormalisationScaler.rst similarity index 91% rename from docs/user_guide/scaling/MeanNormalizationScaler.rst rename to docs/user_guide/scaling/MeanNormalisationScaler.rst index 4cab88937..30952f06c 100644 --- a/docs/user_guide/scaling/MeanNormalizationScaler.rst +++ b/docs/user_guide/scaling/MeanNormalisationScaler.rst @@ -2,12 +2,12 @@ .. currentmodule:: feature_engine.scaling -MeanNormalizationScaler +MeanNormalisationScaler ======================= .. attention:: - **New in version 2.0:** When `variables` is `None`, :class:`MeanNormalizationScaler()` used to + **New in version 2.0:** When `variables` is `None`, :class:`MeanNormalisationScaler()` used to raise an error if the dataframe contained no numerical variables. You can now set the new parameter `return_empty` to `True` to make the transformer return an empty list of variables and skip the scaling instead, leaving the dataframe @@ -28,23 +28,23 @@ Mean normalisation is given by the following formula: X' = (X - Mean(X)) / (Max(X) - Min(X)) -:class:`MeanNormalizationScaler()` scales variables using mean normalisation. +:class:`MeanNormalisationScaler()` scales variables using mean normalisation. .. note:: - :class:`MeanNormalizationScaler()` only works with non-constant numerical variables. + :class:`MeanNormalisationScaler()` only works with non-constant numerical variables. If the variable is constant, the scaler will raise an error. Python implementation --------------------- -We'll show how to use :class:`MeanNormalizationScaler()` through a toy dataset. Let's create +We'll show how to use :class:`MeanNormalisationScaler()` through a toy dataset. Let's create a toy dataset: .. code:: python import pandas as pd - from feature_engine.scaling import MeanNormalizationScaler + from feature_engine.scaling import MeanNormalisationScaler df = pd.DataFrame.from_dict( { @@ -81,12 +81,12 @@ First, let's make a list with the variable names: 'Height', ] -Now, let's set up :class:`MeanNormalizationScaler()`: +Now, let's set up :class:`MeanNormalisationScaler()`: .. code:: python # set up the scaler - scaler = MeanNormalizationScaler(variables = vars) + scaler = MeanNormalisationScaler(variables = vars) # fit the scaler scaler.fit(df) @@ -155,4 +155,4 @@ For tutorials about this and other feature engineering methods check out these r Both our book and courses are suitable for beginners and more advanced data scientists alike. By purchasing them you are supporting `Sole `_, -the main developer of feature-engine. \ No newline at end of file +the main developer of feature-engine. diff --git a/docs/user_guide/scaling/index.rst b/docs/user_guide/scaling/index.rst index b0d8eb0ff..33209e0b9 100644 --- a/docs/user_guide/scaling/index.rst +++ b/docs/user_guide/scaling/index.rst @@ -59,4 +59,4 @@ Scalers .. toctree:: :maxdepth: 1 - MeanNormalizationScaler + MeanNormalisationScaler diff --git a/feature_engine/scaling/__init__.py b/feature_engine/scaling/__init__.py index 00137a550..eddb3fc47 100644 --- a/feature_engine/scaling/__init__.py +++ b/feature_engine/scaling/__init__.py @@ -3,8 +3,9 @@ scaling methods. """ -from .mean_normalization import MeanNormalizationScaler +from .mean_normalization import MeanNormalisationScaler, MeanNormalizationScaler __all__ = [ + "MeanNormalisationScaler", "MeanNormalizationScaler", ] diff --git a/feature_engine/scaling/mean_normalization.py b/feature_engine/scaling/mean_normalization.py index 320358838..c13949c3e 100644 --- a/feature_engine/scaling/mean_normalization.py +++ b/feature_engine/scaling/mean_normalization.py @@ -1,6 +1,7 @@ # Authors: Vasco Schiavo # License: BSD 3 clause +import warnings from typing import List, Optional, Union import pandas as pd @@ -37,9 +38,9 @@ fit_transform=_fit_transform_docstring, inverse_transform=_inverse_transform_docstring, ) -class MeanNormalizationScaler(BaseNumericalTransformer): +class MeanNormalisationScaler(BaseNumericalTransformer): """ - MeanNormalizationScaler() applies mean normalisation, which consists of subtracting + MeanNormalisationScaler() applies mean normalisation, which consists of subtracting the mean of each feature and then dividing the result by the value range, that is, the difference between its maximum and minimum value. The method aims to center the variables at 0, and rescale the distribution between -1 and 1. @@ -51,7 +52,6 @@ class MeanNormalizationScaler(BaseNumericalTransformer): More details in the :ref:`User Guide `. - Parameters ---------- {variables} @@ -89,10 +89,10 @@ class MeanNormalizationScaler(BaseNumericalTransformer): >>> import numpy as np >>> import pandas as pd - >>> from feature_engine.scaling import MeanNormalizationScaler + >>> from feature_engine.scaling import MeanNormalisationScaler >>> np.random.seed(42) >>> X = pd.DataFrame(dict(x = np.random.lognormal(size = 100))) - >>> mns = MeanNormalizationScaler() + >>> mns = MeanNormalisationScaler() >>> mns.fit(X) >>> X = mns.transform(X) >>> X.head() @@ -189,3 +189,21 @@ def inverse_transform(self, X: pd.DataFrame) -> pd.DataFrame: X[self.variables_] = X[self.variables_] * self.range_ + self.mean_ return X + + +# TODO: remove in version 2.1.0 +class MeanNormalizationScaler(MeanNormalisationScaler): + def __init__( + self, + variables: Union[None, int, str, List[Union[str, int]]] = None, + return_empty: bool = False, + ) -> None: + warnings.warn( + "MeanNormalizationScaler was deprecated in favour of " + "MeanNormalisationScaler in version 2.0.0 and will be removed in " + "version 2.1.0. To silence this warning, use MeanNormalisationScaler " + "instead.", + FutureWarning, + stacklevel=2, + ) + super().__init__(variables=variables, return_empty=return_empty) diff --git a/tests/test_scaling/test_mean_normalization.py b/tests/test_scaling/test_mean_normalization.py index 600b89275..997b396db 100644 --- a/tests/test_scaling/test_mean_normalization.py +++ b/tests/test_scaling/test_mean_normalization.py @@ -4,11 +4,37 @@ import pytest from sklearn.exceptions import NotFittedError -from feature_engine.scaling import MeanNormalizationScaler +from feature_engine.scaling import MeanNormalisationScaler, MeanNormalizationScaler from tests.estimator_checks.fit_functionality_checks import check_return_empty +DEPRECATION_WARNING = ( + "MeanNormalizationScaler was deprecated in favour of " + "MeanNormalisationScaler in version 2.0.0 and will be removed in version 2.1.0. " + "To silence this warning, use MeanNormalisationScaler instead." +) -def test_transforming_int_vars(): + +@pytest.fixture( + params=[MeanNormalisationScaler, MeanNormalizationScaler], + ids=["MeanNormalisationScaler", "MeanNormalizationScaler"], +) +def transformer_class(request): + return request.param + + +def make_transformer(transformer_class, **kwargs): + if transformer_class is MeanNormalizationScaler: + with pytest.warns(FutureWarning, match=re.escape(DEPRECATION_WARNING)): + return transformer_class(**kwargs) + return transformer_class(**kwargs) + + +def test_mean_normalization_scaler_raises_future_warning(): + with pytest.warns(FutureWarning, match=re.escape(DEPRECATION_WARNING)): + MeanNormalizationScaler() + + +def test_transforming_int_vars(transformer_class): # input test case df = pd.DataFrame( { @@ -26,7 +52,7 @@ def test_transforming_int_vars(): } ) - transformer = MeanNormalizationScaler(variables=None) + transformer = make_transformer(transformer_class, variables=None) X = transformer.fit_transform(df) pd.testing.assert_frame_equal(X, expected_df) @@ -37,9 +63,11 @@ def test_transforming_int_vars(): pd.testing.assert_frame_equal(Xit, df) -def test_mean_normalization_plus_automatically_find_variables(df_vartypes): +def test_mean_normalization_plus_automatically_find_variables( + df_vartypes, transformer_class +): # test case 1: automatically select variables - transformer = MeanNormalizationScaler(variables=None) + transformer = make_transformer(transformer_class, variables=None) X = transformer.fit_transform(df_vartypes) # expected output @@ -66,9 +94,9 @@ def test_mean_normalization_plus_automatically_find_variables(df_vartypes): pd.testing.assert_frame_equal(Xit, df_vartypes, rtol=10e-3) -def test_mean_normalization_plus_user_passes_var_list(df_vartypes): +def test_mean_normalization_plus_user_passes_var_list(df_vartypes, transformer_class): # test case 2: user passes variables - transformer = MeanNormalizationScaler(variables="Age") + transformer = make_transformer(transformer_class, variables="Age") X = transformer.fit_transform(df_vartypes) # expected output @@ -93,28 +121,28 @@ def test_mean_normalization_plus_user_passes_var_list(df_vartypes): pd.testing.assert_frame_equal(Xit, df_vartypes, rtol=10e-3) -def test_fit_raises_error_if_na_in_df(df_na): +def test_fit_raises_error_if_na_in_df(df_na, transformer_class): # test case 3: when dataset contains na, fit method - transformer = MeanNormalizationScaler() + transformer = make_transformer(transformer_class) with pytest.raises(ValueError): transformer.fit(df_na) -def test_transform_raises_error_if_na_in_df(df_vartypes, df_na): +def test_transform_raises_error_if_na_in_df(df_vartypes, df_na, transformer_class): # test case 4: when dataset contains na, transform method - transformer = MeanNormalizationScaler() + transformer = make_transformer(transformer_class) transformer.fit(df_vartypes) with pytest.raises(ValueError): transformer.transform(df_na[["Name", "City", "Age", "Marks", "dob"]]) -def test_non_fitted_error(df_vartypes): - transformer = MeanNormalizationScaler() +def test_non_fitted_error(df_vartypes, transformer_class): + transformer = make_transformer(transformer_class) with pytest.raises(NotFittedError): transformer.transform(df_vartypes) -def test_constant_columns_error(): +def test_constant_columns_error(transformer_class): # input test case df = pd.DataFrame( { @@ -124,10 +152,15 @@ def test_constant_columns_error(): } ) - transformer = MeanNormalizationScaler() + transformer = make_transformer(transformer_class) with pytest.raises(ValueError, match=re.escape("Division by zero is not allowed")): transformer.fit(df) -def test_check_return_empty(): - check_return_empty(MeanNormalizationScaler()) +def test_check_return_empty(transformer_class): + transformer = make_transformer(transformer_class) + if transformer_class is MeanNormalizationScaler: + with pytest.warns(FutureWarning, match=re.escape(DEPRECATION_WARNING)): + check_return_empty(transformer) + else: + check_return_empty(transformer)