diff --git a/README.md b/README.md index 19ccf3416..6b9cdb24d 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ Please share your story by answering 1 quick question * PowerTransformer * BoxCoxTransformer * YeoJohnsonTransformer +* ArcSinhTransformer ### Variable Scaling methods * MeanNormalizationScaler diff --git a/docs/api_doc/transformation/ArcSinhTransformer.rst b/docs/api_doc/transformation/ArcSinhTransformer.rst new file mode 100644 index 000000000..69f315cbb --- /dev/null +++ b/docs/api_doc/transformation/ArcSinhTransformer.rst @@ -0,0 +1,5 @@ +ArcSinhTransformer +================== + +.. autoclass:: feature_engine.transformation.ArcSinhTransformer + :members: diff --git a/docs/api_doc/transformation/index.rst b/docs/api_doc/transformation/index.rst index 0705f4d0a..32866842d 100644 --- a/docs/api_doc/transformation/index.rst +++ b/docs/api_doc/transformation/index.rst @@ -13,6 +13,7 @@ mathematical transformations. LogCpTransformer ReciprocalTransformer ArcsinTransformer + ArcSinhTransformer PowerTransformer BoxCoxTransformer YeoJohnsonTransformer diff --git a/docs/images/arcsinh_profit_histogram.png b/docs/images/arcsinh_profit_histogram.png new file mode 100644 index 000000000..e776e30d4 Binary files /dev/null and b/docs/images/arcsinh_profit_histogram.png differ diff --git a/docs/index.rst b/docs/index.rst index d1bf049a8..a7266bdc1 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -237,6 +237,7 @@ like anova, and machine learning models, like linear regression. Feature-engine - :doc:`api_doc/transformation/BoxCoxTransformer`: performs Box-Cox transformation of numerical variables - :doc:`api_doc/transformation/YeoJohnsonTransformer`: performs Yeo-Johnson transformation of numerical variables - :doc:`api_doc/transformation/ArcsinTransformer`: performs arcsin transformation of numerical variables +- :doc:`api_doc/transformation/ArcSinhTransformer`: applies arcsinh (pseudo-logarithm) transformation of numerical variables Feature Creation: ~~~~~~~~~~~~~~~~~ diff --git a/docs/user_guide/transformation/ArcSinhTransformer.rst b/docs/user_guide/transformation/ArcSinhTransformer.rst new file mode 100644 index 000000000..e5fe01053 --- /dev/null +++ b/docs/user_guide/transformation/ArcSinhTransformer.rst @@ -0,0 +1,195 @@ +.. _arcsinh_transformer: + +.. currentmodule:: feature_engine.transformation + +ArcSinhTransformer +================== + +:class:`ArcSinhTransformer()` applies the inverse hyperbolic sine transformation +(arcsinh) to numerical variables. Also known as the pseudo-logarithm, this +transformation is useful for data that contains both positive and negative values. + +The transformation is: x → arcsinh((x - loc) / scale) + +Comparison to LogTransformer and ArcsinTransformer +-------------------------------------------------- + +- **LogTransformer**: `log(x)` requires `x > 0`. If your data contains zeros or negative values, you cannot use the standard LogTransformer directly. You would need to shift the data (e.g. `LogCpTransformer`) or remove non-positive values. +- **ArcsinTransformer**: `arcsin(sqrt(x))` is typically used for proportions/ratios bounded between 0 and 1. It is not suitable for general unbounded numerical data. +- **ArcSinhTransformer**: `arcsinh(x)` works for **all real numbers** (positive, negative, and zero). It handles zero gracefully (arcsinh(0) = 0) and is symmetric around zero. + +When to use ArcSinhTransformer: +- Your data contains zeros or negative values (e.g., profit/loss, debt, temperature). +- You want a log-like transformation to stabilize variance or compress extreme values. +- You don't want to add an arbitrary constant (shift) to make values positive. + +Intuitive Explanation of Parameters +----------------------------------- + +The transformation includes optional `loc` (location) and `scale` parameters: + +.. math:: + y = \text{arcsinh}\left(\frac{x - \text{loc}}{\text{scale}}\right) + +- **Why scale?** + The `arcsinh(x)` function is linear near zero (for small x) and logarithmic for large x. + The "linear region" is roughly between -1 and 1. + By adjusting the `scale`, you control which part of your data falls into this linear region versus the logarithmic region. + - If `scale` is large, more of your data falls in the linear region (behavior close to original data). + - If `scale` is small, more of your data falls in the logarithmic region (stronger compression of values). + Common practice is to set `scale` to 1 or usage the standard deviation of the variable. + +- **Why loc?** + The `loc` parameter centers the data. The transition from negative logarithmic behavior to positive logarithmic behavior happens around `x = loc`. + Common practice is to set `loc` to 0 or usage the mean of the variable. + +References +---------- + +For more details on the inverse hyperbolic sine transformation: + +1. `How should I transform non-negative data including zeros? `_ (StackExchange) +2. `Interpreting Treatment Effects: Inverse Hyperbolic Sine Outcome Variable `_ (World Bank Blog) +3. `Burbidge, J. B., Magee, L., & Robb, A. L. (1988). Alternative transformations to handle extreme values of the dependent variable. Journal of the American Statistical Association. `_ + +Python demo +----------- + +Unlike :class:`LogTransformer()`, :class:`ArcSinhTransformer()` can handle +zero and negative values without requiring any preprocessing. + +Let's create a dataframe with positive and negative values and apply the arcsinh +transformation: + +.. code:: python + + import numpy as np + import pandas as pd + import matplotlib.pyplot as plt + from sklearn.model_selection import train_test_split + + from feature_engine.transformation import ArcSinhTransformer + + # Create sample data with positive and negative values + np.random.seed(42) + X = pd.DataFrame({ + 'profit': np.random.randn(1000) * 10000, # Values from -30000 to 30000 + 'net_worth': np.random.randn(1000) * 50000, + }) + + # Separate into train and test + X_train, X_test = train_test_split(X, test_size=0.3, random_state=0) + + print(X.head()) + +The dataframe contains positive and negative values: + +.. code:: python + + profit net_worth + 0 4967.141530 69967.771829 + 1 -1382.643012 46231.684146 + 2 6476.885381 2981.518496 + 3 15230.298564 -32346.838885 + 4 -2341.533747 34911.165681 + +Now let's set up the ArcSinhTransformer and fit it to the training set: + +.. code:: python + + # Set up the arcsinh transformer + tf = ArcSinhTransformer(variables=['profit', 'net_worth']) + + # Fit the transformer + tf.fit(X_train) + +The transformer does not learn any parameters when applying the fit method. It does +check, however, that the variables are numerical. + +We can now transform the variables: + +.. code:: python + + # Transform the data + train_t = tf.transform(X_train) + test_t = tf.transform(X_test) + + print(train_t.head()) + +The dataframe with the transformed variables: + +.. code:: python + + profit net_worth + 105 8.997273 -11.552056 + 68 8.886371 -10.753000 + 479 10.016437 -10.686152 + 399 10.116836 -11.092693 + 434 10.310523 -9.723893 + +The arcsinh transformation compresses extreme values while preserving the sign. We can inspect the distribution of the original and transformed variables with histograms: + +.. code:: python + + # Compare original and transformed distributions + fig, axes = plt.subplots(1, 2, figsize=(12, 4)) + + X_train['profit'].hist(ax=axes[0], bins=50) + axes[0].set_title('Original profit') + + train_t['profit'].hist(ax=axes[1], bins=50) + axes[1].set_title('Transformed profit') + + plt.tight_layout() + +.. image:: ../../images/arcsinh_profit_histogram.png + +Using loc and scale parameters +------------------------------ + +:class:`ArcSinhTransformer()` supports location and scale parameters to +center and normalize data before transformation. + +In practice, it is common to standardize the variable (zero mean, unit variance) +so that the center of the distribution falls in the linear region of the arcsinh +function, while the tails are compressed logarithmically. We can achieve this +by setting ``loc`` to the mean and ``scale`` to the standard deviation: + +.. code:: python + + # Center around mean and scale by std + tf = ArcSinhTransformer( + variables=['profit'], + loc=X_train['profit'].mean(), + scale=X_train['profit'].std() + ) + + tf.fit(X_train) + train_t = tf.transform(X_train) + +Inverse transformation +---------------------- + +:class:`ArcSinhTransformer()` supports inverse transformation to recover +the original values: + +.. code:: python + + # Transform and then inverse transform + train_t = tf.transform(X_train) + train_recovered = tf.inverse_transform(train_t) + + print(train_recovered.head()) + +The recovered data: + +.. code:: python + + profit net_worth + 105 4040.508568 -51995.296356 + 68 3616.360250 -23385.060066 + 479 11195.749114 -21872.915016 + 399 12378.163120 -32844.713949 + 434 15023.570521 -8356.085689 + + diff --git a/docs/user_guide/transformation/index.rst b/docs/user_guide/transformation/index.rst index 85422c9f6..00ce20bfb 100644 --- a/docs/user_guide/transformation/index.rst +++ b/docs/user_guide/transformation/index.rst @@ -33,6 +33,7 @@ on the nature of the variable. LogCpTransformer ReciprocalTransformer ArcsinTransformer + ArcSinhTransformer PowerTransformer BoxCoxTransformer YeoJohnsonTransformer diff --git a/feature_engine/transformation/__init__.py b/feature_engine/transformation/__init__.py index 15011ac4b..9bbb62a59 100644 --- a/feature_engine/transformation/__init__.py +++ b/feature_engine/transformation/__init__.py @@ -4,6 +4,7 @@ """ from .arcsin import ArcsinTransformer +from .arcsinh import ArcSinhTransformer from .boxcox import BoxCoxTransformer from .log import LogCpTransformer, LogTransformer from .power import PowerTransformer @@ -11,11 +12,12 @@ from .yeojohnson import YeoJohnsonTransformer __all__ = [ + "ArcsinTransformer", + "ArcSinhTransformer", "BoxCoxTransformer", "LogTransformer", "LogCpTransformer", "PowerTransformer", "ReciprocalTransformer", "YeoJohnsonTransformer", - "ArcsinTransformer", ] diff --git a/feature_engine/transformation/arcsinh.py b/feature_engine/transformation/arcsinh.py new file mode 100644 index 000000000..e0020ff86 --- /dev/null +++ b/feature_engine/transformation/arcsinh.py @@ -0,0 +1,228 @@ +# Authors: Ankit Hemant Lade (contributor) +# License: BSD 3 clause + +from typing import List, Optional, Union + +import numpy as np +import pandas as pd + +from feature_engine._base_transformers.base_numerical import BaseNumericalTransformer +from feature_engine._check_init_parameters.check_variables import ( + _check_variables_input_value, +) +from feature_engine._docstrings.fit_attributes import ( + _feature_names_in_docstring, + _n_features_in_docstring, + _variables_attribute_docstring, +) +from feature_engine._docstrings.init_parameters.all_trasnformers import ( + _variables_numerical_docstring, +) +from feature_engine._docstrings.methods import ( + _fit_not_learn_docstring, + _fit_transform_docstring, + _inverse_transform_docstring, +) +from feature_engine._docstrings.substitute import Substitution +from feature_engine.tags import _return_tags + + +@Substitution( + variables=_variables_numerical_docstring, + variables_=_variables_attribute_docstring, + feature_names_in_=_feature_names_in_docstring, + n_features_in_=_n_features_in_docstring, + fit=_fit_not_learn_docstring, + fit_transform=_fit_transform_docstring, + inverse_transform=_inverse_transform_docstring, +) +class ArcSinhTransformer(BaseNumericalTransformer): + """ + The ArcSinhTransformer() applies the inverse hyperbolic sine transformation + (arcsinh) to numerical variables. Also known as the pseudo-logarithm, this + transformation is useful for data that contains both positive and negative values. + + The transformation is: x → arcsinh((x - loc) / scale) + + For large values of x, arcsinh(x) behaves like ln(x) + ln(2), providing similar + variance-stabilizing properties as the log transformation. For small values of x, + it behaves approximately linearly (i.e., arcsinh(x) ≈ x). This makes it ideal for + variables like net worth, profit/loss, or any metric that can be positive or + negative. + + A list of variables can be passed as an argument. Alternatively, the transformer + will automatically select and transform all variables of type numeric. + + More details in the :ref:`User Guide `. + + Parameters + ---------- + {variables} + + loc: float, default=0.0 + Location parameter for shifting the data before transformation. + The transformation becomes: arcsinh((x - loc) / scale) + + scale: float, default=1.0 + Scale parameter for normalizing the data before transformation. + Must be greater than 0. The transformation becomes: arcsinh((x - loc) / scale) + + Attributes + ---------- + {variables_} + + {feature_names_in_} + + {n_features_in_} + + Methods + ------- + {fit} + + {fit_transform} + + {inverse_transform} + + transform: + Transform the variables using the arcsinh function. + + See Also + -------- + feature_engine.transformation.LogTransformer : + Applies log transformation (only for positive values). + feature_engine.transformation.YeoJohnsonTransformer : + Applies Yeo-Johnson transformation. + + References + ---------- + .. [1] Burbidge, J. B., Magee, L., & Robb, A. L. (1988). Alternative + transformations to handle extreme values of the dependent variable. + Journal of the American Statistical Association, 83(401), 123-127. + + Examples + -------- + + >>> import numpy as np + >>> import pandas as pd + >>> from feature_engine.transformation import ArcSinhTransformer + >>> np.random.seed(42) + >>> X = pd.DataFrame(dict(x = np.random.randn(100) * 1000)) + >>> ast = ArcSinhTransformer() + >>> ast.fit(X) + >>> X = ast.transform(X) + >>> X.head() + x + 0 7.516076 + 1 -6.330816 + 2 7.780254 + 3 8.825252 + 4 -6.995893 + """ + + def __init__( + self, + variables: Union[None, int, str, List[Union[str, int]]] = None, + loc: float = 0.0, + scale: float = 1.0, + ) -> None: + + if not isinstance(loc, (int, float)): + raise ValueError( + f"loc must be a number (int or float). " + f"Got {type(loc).__name__} instead." + ) + + if not isinstance(scale, (int, float)) or scale <= 0: + raise ValueError( + f"scale must be a positive number (> 0). Got {scale} instead." + ) + + self.variables = _check_variables_input_value(variables) + self.loc = float(loc) + self.scale = float(scale) + + def fit(self, X: pd.DataFrame, y: Optional[pd.Series] = None): + """ + Selects the numerical variables and stores feature names. + + Parameters + ---------- + X: Pandas DataFrame of shape = [n_samples, n_features]. + The training input samples. Can be the entire dataframe, not just the + variables to transform. + + y: pandas Series, default=None + It is not needed in this transformer. You can pass y or None. + + Returns + ------- + self: ArcSinhTransformer + The fitted transformer. + """ + + # check input dataframe and find/check numerical variables + X = super().fit(X) + + return self + + def transform(self, X: pd.DataFrame) -> pd.DataFrame: + """ + Transform the variables using the arcsinh function. + + Parameters + ---------- + X: Pandas DataFrame of shape = [n_samples, n_features] + The data to be transformed. + + Returns + ------- + X_new: pandas dataframe + The dataframe with the transformed variables. + """ + + # check input dataframe and if class was fitted + X = self._check_transform_input_and_state(X) + + # Ensure float dtype for the transformation + X[self.variables_] = X[self.variables_].astype(float) + + # Apply arcsinh transformation: arcsinh((x - loc) / scale) + X.loc[:, self.variables_] = np.arcsinh( + (X.loc[:, self.variables_] - self.loc) / self.scale + ) + + return X + + def inverse_transform(self, X: pd.DataFrame) -> pd.DataFrame: + """ + Convert the data back to the original representation. + + Parameters + ---------- + X: Pandas DataFrame of shape = [n_samples, n_features] + The data to be inverse transformed. + + Returns + ------- + X_tr: pandas dataframe + The dataframe with the inverse transformed variables. + """ + + # check input dataframe and if class was fitted + X = self._check_transform_input_and_state(X) + + # Inverse transform: x = sinh(y) * scale + loc + X.loc[:, self.variables_] = ( + np.sinh(X.loc[:, self.variables_]) * self.scale + self.loc + ) + + return X + + def _more_tags(self): + tags_dict = _return_tags() + tags_dict["variables"] = "numerical" + return tags_dict + + def __sklearn_tags__(self): + tags = super().__sklearn_tags__() + return tags diff --git a/tests/test_transformation/test_arcsinh.py b/tests/test_transformation/test_arcsinh.py new file mode 100644 index 000000000..a3d8b8d4d --- /dev/null +++ b/tests/test_transformation/test_arcsinh.py @@ -0,0 +1,201 @@ +import numpy as np +import pandas as pd +import pytest + +from feature_engine.transformation import ArcSinhTransformer + + +@pytest.fixture +def df_numerical(): + """Fixture providing sample numerical data with positive and negative values.""" + return pd.DataFrame({ + "a": [-100, -10, 0, 10, 100], + "b": [1, 2, 3, 4, 5], + }) + + +@pytest.fixture +def df_multi_column(): + """Fixture providing DataFrame with multiple columns.""" + return pd.DataFrame({ + "a": [1, 2, 3], + "b": [4, 5, 6], + "c": [7, 8, 9], + }) + + +def test_default_parameters(df_numerical): + """Test transformer with default parameters applies arcsinh to all columns.""" + transformer = ArcSinhTransformer() + X_tr = transformer.fit_transform(df_numerical.copy()) + + expected_a = np.arcsinh(df_numerical["a"]) + expected_b = np.arcsinh(df_numerical["b"]) + np.testing.assert_array_almost_equal(X_tr["a"], expected_a) + np.testing.assert_array_almost_equal(X_tr["b"], expected_b) + + +def test_specific_variables(df_multi_column): + """Test transformer with specific variables selected.""" + transformer = ArcSinhTransformer(variables=["a", "b"]) + X_tr = transformer.fit_transform(df_multi_column.copy()) + + np.testing.assert_array_almost_equal( + X_tr["a"], np.arcsinh(df_multi_column["a"]) + ) + np.testing.assert_array_almost_equal( + X_tr["b"], np.arcsinh(df_multi_column["b"]) + ) + np.testing.assert_array_equal(X_tr["c"], df_multi_column["c"]) + + +def test_with_loc_and_scale(): + """Test transformer with loc and scale parameters.""" + X = pd.DataFrame({"a": [10, 20, 30, 40, 50]}) + loc = 30.0 + scale = 10.0 + transformer = ArcSinhTransformer(loc=loc, scale=scale) + X_tr = transformer.fit_transform(X.copy()) + + expected = np.arcsinh((X["a"] - loc) / scale) + np.testing.assert_array_almost_equal(X_tr["a"], expected) + np.testing.assert_almost_equal(X_tr["a"].iloc[2], 0.0, decimal=10) + + +@pytest.mark.parametrize("loc", [0.0, 10.0, -10.0, 100.5]) +def test_various_loc_values(loc): + """Test that various loc values work correctly.""" + X = pd.DataFrame({"a": [1, 2, 3, 4, 5]}) + transformer = ArcSinhTransformer(loc=loc) + X_tr = transformer.fit_transform(X.copy()) + + expected = np.arcsinh((X["a"] - loc) / 1.0) + np.testing.assert_array_almost_equal(X_tr["a"], expected) + + +@pytest.mark.parametrize("scale", [0.5, 1.0, 2.0, 10.0, 100.0]) +def test_various_scale_values(scale): + """Test that various scale values work correctly.""" + X = pd.DataFrame({"a": [1, 2, 3, 4, 5]}) + transformer = ArcSinhTransformer(scale=scale) + X_tr = transformer.fit_transform(X.copy()) + + expected = np.arcsinh((X["a"] - 0.0) / scale) + np.testing.assert_array_almost_equal(X_tr["a"], expected) + + +def test_inverse_transform(df_numerical): + """Test inverse_transform returns original values.""" + X_original = df_numerical.copy() + transformer = ArcSinhTransformer() + X_tr = transformer.fit_transform(df_numerical.copy()) + X_inv = transformer.inverse_transform(X_tr) + + np.testing.assert_array_almost_equal(X_inv["a"], X_original["a"], decimal=10) + np.testing.assert_array_almost_equal(X_inv["b"], X_original["b"], decimal=10) + + +def test_inverse_transform_with_loc_scale(): + """Test inverse_transform with loc and scale parameters.""" + X = pd.DataFrame({"a": [10, 20, 30, 40, 50]}) + X_original = X.copy() + transformer = ArcSinhTransformer(loc=25.0, scale=5.0) + X_tr = transformer.fit_transform(X.copy()) + X_inv = transformer.inverse_transform(X_tr) + + np.testing.assert_array_almost_equal(X_inv["a"], X_original["a"], decimal=10) + + +def test_negative_values(): + """Test that transformer handles negative values correctly.""" + X = pd.DataFrame({"a": [-1000, -500, 0, 500, 1000]}) + transformer = ArcSinhTransformer() + X_tr = transformer.fit_transform(X.copy()) + + # Expected values: arcsinh([ -1000, -500, 0, 500, 1000 ]) + expected = [-7.600902, -6.907755, 0.0, 6.907755, 7.600902] + np.testing.assert_array_almost_equal(X_tr["a"], expected, decimal=5) + + # Verify symmetry property: arcsinh(-x) = -arcsinh(x) + np.testing.assert_almost_equal( + X_tr["a"].iloc[0], -X_tr["a"].iloc[4], decimal=10 + ) + np.testing.assert_almost_equal( + X_tr["a"].iloc[1], -X_tr["a"].iloc[3], decimal=10 + ) + + +@pytest.mark.parametrize("invalid_scale", [0, -1, -0.5, -100, "string", False]) +def test_invalid_scale_raises_error(invalid_scale): + """Test that non-positive scale values raise ValueError.""" + with pytest.raises(ValueError, match="scale must be a positive number"): + ArcSinhTransformer(scale=invalid_scale) + + +@pytest.mark.parametrize("invalid_loc", ["invalid", [1, 2], {"a": 1}, None]) +def test_invalid_loc_raises_error(invalid_loc): + """Test that non-numeric loc values raise ValueError.""" + with pytest.raises(ValueError, match="loc must be a number"): + ArcSinhTransformer(loc=invalid_loc) + + +def test_fit_stores_attributes(): + """Test that fit stores expected attributes with correct values.""" + X = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]}) + transformer = ArcSinhTransformer() + transformer.fit(X) + + assert hasattr(transformer, "variables_") + assert hasattr(transformer, "feature_names_in_") + assert hasattr(transformer, "n_features_in_") + assert transformer.n_features_in_ == 2 + assert set(transformer.variables_) == {"a", "b"} + assert transformer.feature_names_in_ == ["a", "b"] + + +def test_get_feature_names_out(): + """Test get_feature_names_out returns correct feature names.""" + X = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]}) + transformer = ArcSinhTransformer() + transformer.fit(X) + + feature_names = transformer.get_feature_names_out() + assert feature_names == ["a", "b"] + + +def test_get_feature_names_out_with_subset(): + """Test get_feature_names_out with subset of variables.""" + X = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}) + transformer = ArcSinhTransformer(variables=["a"]) + transformer.fit(X) + + feature_names = transformer.get_feature_names_out() + assert feature_names == ["a", "b", "c"] + + +def test_behavior_like_log_for_large_values(): + """Test that arcsinh behaves like log for large positive values.""" + X = pd.DataFrame({"a": [1000, 10000, 100000]}) + transformer = ArcSinhTransformer() + X_tr = transformer.fit_transform(X.copy()) + + log_approx = np.log(2 * X["a"]) + np.testing.assert_array_almost_equal(X_tr["a"], log_approx, decimal=1) + + +def test_behavior_like_identity_for_small_values(): + """Test that arcsinh behaves like identity for small values.""" + X = pd.DataFrame({"a": [0.001, 0.01, 0.1]}) + transformer = ArcSinhTransformer() + X_tr = transformer.fit_transform(X.copy()) + + np.testing.assert_array_almost_equal(X_tr["a"], X["a"], decimal=2) + + +def test_zero_input_returns_zero(): + """Test that arcsinh(0) = 0.""" + X = pd.DataFrame({"a": [0.0]}) + transformer = ArcSinhTransformer() + X_tr = transformer.fit_transform(X.copy()) + + assert X_tr["a"].iloc[0] == 0.0 diff --git a/tests/test_transformation/test_check_estimator_transformers.py b/tests/test_transformation/test_check_estimator_transformers.py index 7db0088f8..812cbbbaf 100644 --- a/tests/test_transformation/test_check_estimator_transformers.py +++ b/tests/test_transformation/test_check_estimator_transformers.py @@ -7,6 +7,7 @@ from feature_engine.transformation import ( ArcsinTransformer, + ArcSinhTransformer, BoxCoxTransformer, LogCpTransformer, LogTransformer, @@ -21,6 +22,7 @@ LogTransformer(), LogCpTransformer(), ArcsinTransformer(), + ArcSinhTransformer(), PowerTransformer(), ReciprocalTransformer(), YeoJohnsonTransformer(),