Repository: feature-engine/feature_engine
Pull request #349 (2021-12-23 18:22:14)

Release 1.2
Hello everyone!

@dodoarg, @gverbock @thibaultbl @david-cortes @kevinkurek @Morgan-sell @ChristopherGS @NicoGalli 

**We are about to make a new release of Feature-engine, featuring all your contributions.**

ETA: 3rd January

Would you mind letting my know if you are happy with the release notes I included in this PR, if I got your names right, and if you would rather I linked your name to something other than github?

Also, if you would like me to tag you when we share on linkedin/twitter, could you either send me a linkedin/twitter message so I get your handles or add them here?

Feel free to request changes in comments or PRs to this branch

Thank you so much for your contribution!!!

I look forward to working with all of you again.

Best wishes and happy holidays to those who have them :)


Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index f9ba44b..d16bed5 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -153,4 +153,4 @@ workflows:
           filters:
             branches:
               only:
-                - 1.1.X
\ No newline at end of file
+                - 1.2.X
\ No newline at end of file
diff --git a/docs/about/about.rst b/docs/about/about.rst
index 1f7a132..8da444b 100644
--- a/docs/about/about.rst
+++ b/docs/about/about.rst
@@ -134,4 +134,4 @@ maintaining the project.
 ..........
 
 |
-|
+|
\ No newline at end of file
diff --git a/docs/contribute/contribute_other.rst b/docs/contribute/contribute_other.rst
index 215e69f..4b1a26b 100644
--- a/docs/contribute/contribute_other.rst
+++ b/docs/contribute/contribute_other.rst
@@ -38,7 +38,7 @@ Donate
 ------
 
 If Feature-engine is helping your organization save money, time, effort, and frustrations,
-and be more productive, consider `sponsoring the maintainer <https://github.com/sponsors/solegalli>`_.
+and be more productive, consider `sponsoring us <https://github.com/sponsors/solegalli>`_.
 This is a great way to show your appreciation. Thank you!
 
 For all of this, get in touch with the maintainer via `LinkedIn <https://www.linkedin.com/in/soledad-galli/>`_.
diff --git a/docs/contribute/index.rst b/docs/contribute/index.rst
index d9241c3..c42484e 100644
--- a/docs/contribute/index.rst
+++ b/docs/contribute/index.rst
@@ -31,7 +31,7 @@ There are many ways to contribute to Feature-engine:
 - Improve our documentation, i.e., fix typos, improve grammar, or add more code examples.
 - Write a blog, tweet, or share our project with others.
 - Use Feature-engine in your lectures if you teach
-- `Sponsor the maintainer <https://github.com/sponsors/solegalli>`_.
+- `Sponsor us <https://github.com/sponsors/solegalli>`_.
 
 With plenty of ways to get involved, we would be happy for you to support the project.
 You only need to abide by the principles of openness, respect, and consideration of
diff --git a/docs/index.rst b/docs/index.rst
index 2e7fefb..9dc9547 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -222,7 +222,7 @@ For more details on how to contribute check the contributing page. Click on the
 Donate
 ~~~~~~
 
-`Sponsor the maintainer <https://github.com/sponsors/solegalli>`_ to support her continue expanding
+`Sponsor us <https://github.com/sponsors/solegalli>`_ to support us continue expanding
 Feature-engine.
 
 Open Source
diff --git a/docs/user_guide/datetime/DatetimeFeatures.rst b/docs/user_guide/datetime/DatetimeFeatures.rst
index ec51e5b..4743aca 100644
--- a/docs/user_guide/datetime/DatetimeFeatures.rst
+++ b/docs/user_guide/datetime/DatetimeFeatures.rst
@@ -57,7 +57,7 @@ second datetime variable in our dataset.
 
     dtfs = DatetimeFeatures(
         variables="var_date2",
-        features_to_extract=["month", "month_end", "day_of_the_year"]
+        features_to_extract=["month", "month_end", "day_of_year"]
     )
 
     df_transf = dtfs.fit_transform(toy_df)
@@ -66,11 +66,11 @@ second datetime variable in our dataset.
 
 .. code:: python
 
-      var_date1  var_date2_month  var_date2_month_end  var_date2_doty
-    0  May-1989                6                    0             173
-    1  Dec-2020                2                    0              41
-    2  Jan-1999                8                    0             215
-    3  Feb-2002               10                    1             305
+      var_date1  var_date2_month  var_date2_month_end  var_date2_day_of_year
+    0  May-1989                6                    0                    173
+    1  Dec-2020                2                    0                     41
+    2  Jan-1999                8                    0                    215
+    3  Feb-2002               10                    1                    305
 
 
 With `transform()`, the features extracted from the datetime variable are added to the
@@ -214,7 +214,7 @@ And now we mistakenly extract only date features.
 .. code:: python
 
     dfts = DatetimeFeatures(
-        features_to_extract=["year", "month", "day_of_the_week"],
+        features_to_extract=["year", "month", "day_of_week"],
     )
     df_transf = dfts.fit_transform(toy_df)
 
@@ -222,17 +222,17 @@ And now we mistakenly extract only date features.
 
 .. code:: python
 
-      not_a_dt  var_time1_year  var_time1_month  var_time1_dotw  var_time2_year  \
-    0      not            2021               12               2            2021
-    1        a            2021               12               2            2021
-    2     date            2021               12               2            2021
-    3     time            2021               12               2            2021
+      not_a_dt  var_time1_year  var_time1_month  var_time1_day_of_week  var_time2_year \
+    0      not            2021               12                      2            2021
+    1        a            2021               12                      2            2021
+    2     date            2021               12                      2            2021
+    3     time            2021               12                      2            2021
 
-       var_time2_month  var_time2_dotw
-    0               12               2
-    1               12               2
-    2               12               2
-    3               12               2
+       var_time2_month  var_time2_day_of_week
+    0               12                      2
+    1               12                      2
+    2               12                      2
+    3               12                      2
 
 The transformer will still create features derived from today's date (the date of
 creating the docs).
@@ -313,15 +313,15 @@ To do this, we leave the parameter `features_to_extract` to `None`.
     1 2018-01-01 01:00:00  12/01/90 23:01:02  02/28/97 10:10:55              1
     2 2018-01-01 02:00:00  04/25/01 11:59:21  11/11/03 17:30:00              1
 
-       var_dt1_year  var_dt1_dotw  var_dt1_dotm  var_dt1_hour  var_dt1_minute  \
-    0          2018             0             1             0               0
-    1          2018             0             1             1               0
-    2          2018             0             1             2               0
+       var_dt1_year  var_dt1_day_of_week  var_dt1_day_of_month  var_dt1_hour  \
+    0          2018                    0                     1             0
+    1          2018                    0                                   1
+    2          2018                    0                  1                2
 
-       var_dt1_second
-    0               0
-    1               0
-    2               0
+        var_dt1_minute    var_dt1_second
+    0               0                  0
+    1               0                  0
+    2               0                  0
 
 Our new dataset contains the original features plus the new variables extracted
 from them.
@@ -336,8 +336,8 @@ We can find the group of features extracted by the transformer in its attribute.
 
     ['month',
      'year',
-     'day_of_the_week',
-     'day_of_the_month',
+     'day_of_week',
+     'day_of_month',
      'hour',
      'minute',
      'second']
@@ -368,10 +368,10 @@ We can also extract all supported features automatically.
     1                1                 1          2018
     2                1                 1          2018
 
-       var_dt1_woty  var_dt1_dotw  ...  var_dt1_month_end  var_dt1_quarter_start  \
-    0             1             0  ...                  0                      1
-    1             1             0  ...                  0                      1
-    2             1             0  ...                  0                      1
+       var_dt1_week  var_dt1_day_of_week  ...  var_dt1_month_end  var_dt1_quarter_start  \
+    0             1                    0  ...                  0                      1
+    1             1                    0  ...                  0                      1
+    2             1                    0  ...                  0                      1
 
        var_dt1_quarter_end  var_dt1_year_start  var_dt1_year_end  \
     0                    0                   1                 0
@@ -400,10 +400,10 @@ We can find the group of features extracted by the transformer in its attribute.
      'quarter',
      'semester',
      'year',
-     'week_of_the_year',
-     'day_of_the_week',
-     'day_of_the_month',
-     'day_of_the_year',
+     'week',
+     'day_of_week',
+     'day_of_month',
+     'day_of_year',
      'weekend',
      'month_start',
      'month_end',
@@ -467,11 +467,11 @@ from the dataset.
 
 .. code:: python
 
-       var_date_month  var_date_year  var_date_dotw  var_date_dotm  \
-    0               6           2012              3             21
-    1               2           1998              1             10
-    2               8           2010              1              3
-    3              10           2020              5             31
+       var_date_month  var_date_year  var_date_day_of_week  var_date_day_of_month  \
+    0               6           2012                     3                     21
+    1               2           1998                     1                     10
+    2               8           2010                     1                      3
+    3              10           2020                     5                     31
 
        var_time1_hour  var_time1_minute  var_time1_second  var_dt_month  \
     0              12                34                45             8
@@ -479,17 +479,17 @@ from the dataset.
     2              11                59                21             4
     3               8                44                23             4
 
-       var_dt_year  var_dt_dotw  var_dt_dotm  var_dt_hour  var_dt_minute  \
-    0         2000            3           31           12             34
-    1         1990            5            1           23              1
-    2         2001            2           25           11             59
-    3         2001            2           25           11             59
+       var_dt_year  var_dt_day_of_week  var_dt_day_of_month  var_dt_hour  \
+    0         2000                   3                   31           12
+    1         1990                   5                    1           23
+    2         2001                   2                   25           11
+    3         2001                   2                   25           11
 
-       var_dt_second
-    0             45
-    1              2
-    2             21
-    3             21
+       var_dt_minute   var_dt_second
+    0             34              45
+    1              1               2
+    2             59              21
+    3             59              21
 
 As you can see, we do not have the constant features in the transformed dataset.
 
@@ -562,7 +562,7 @@ timezone.
     toy_df = pd.DataFrame({"var_tz": var_tz})
 
     dfts = DatetimeFeatures(
-        features_to_extract=["day_of_the_month", "hour"],
+        features_to_extract=["day_of_month", "hour"],
         drop_original=False,
         utc=True,
     )
@@ -573,10 +573,10 @@ timezone.
 
 .. code:: python
 
-                         var_tz  var_tz_dotm  var_tz_hour
-    0 2000-08-31 12:34:45-04:00           31           16
-    1 1990-12-01 23:01:02-05:00            2            4
-    2 2001-04-25 11:59:21-04:00           25           15
+                         var_tz  var_tz_day_of_month  var_tz_hour
+    0 2000-08-31 12:34:45-04:00                   31           16
+    1 1990-12-01 23:01:02-05:00                    2            4
+    2 2001-04-25 11:59:21-04:00                   25           15
 
 
 **Case 3**: given a variable like *var_tz* in the example above, we now want
@@ -587,7 +587,7 @@ is the default option.
 .. code:: python
 
     dfts = DatetimeFeatures(
-        features_to_extract=["day_of_the_month", "hour"],
+        features_to_extract=["day_of_month", "hour"],
         drop_original=False,
         utc=None,
     )
@@ -598,10 +598,10 @@ is the default option.
 
 .. code:: python
 
-                         var_tz  var_tz_dotm  var_tz_hour
-    0 2000-08-31 12:34:45-04:00           31           12
-    1 1990-12-01 23:01:02-05:00            1           23
-    2 2001-04-25 11:59:21-04:00           25           11
+                         var_tz  var_tz_day_of_month  var_tz_hour
+    0 2000-08-31 12:34:45-04:00                   31           12
+    1 1990-12-01 23:01:02-05:00                    1           23
+    2 2001-04-25 11:59:21-04:00                   25           11
 
 Note that the hour extracted from the variable differ in this dataframe respect to the
 one obtained in **Case 2**.
diff --git a/docs/user_guide/selection/DropHighPSIFeatures.rst b/docs/user_guide/selection/DropHighPSIFeatures.rst
index 3276bc8..65ccb78 100644
--- a/docs/user_guide/selection/DropHighPSIFeatures.rst
+++ b/docs/user_guide/selection/DropHighPSIFeatures.rst
@@ -505,6 +505,9 @@ will be done comparing the periods up to the French revolution and after.
     transformer = DropHighPSIFeatures(split_col='time', cut_off=date(1789, 7, 14))
     transformer.fit(X)
 
+**Important**: if the date variable is in pandas or NumPy datetime format, you may need
+to pass the cut_off value as `pd.to_datetime(1789-07-14)`.
+
 The PSI values shows the *century* variables in unstable as its value is above
 the 0.25 threshold.
 
@@ -847,3 +850,14 @@ and a non-shifted variable (for example *var_4*)
 
 
 .. image:: ../../images/PSI_distribution_case5.png
+
+
+More details
+^^^^^^^^^^^^
+
+In this notebook, we show how to use :class:`DropHighPSIFeatures` on a real dataset and
+give more detail about the underlying base and reference sub-dataframes used to
+determine the PSI.
+
+- `Jupyter notebook <https://nbviewer.org/github/feature-engine/feature-engine-examples/blob/main/selection/Drop-High-PSI-Features.ipynb>`_
+
diff --git a/docs/whats_new/index.rst b/docs/whats_new/index.rst
index e97dac4..914f5a6 100644
--- a/docs/whats_new/index.rst
+++ b/docs/whats_new/index.rst
@@ -8,5 +8,6 @@ Find out what's new in each new version release.
 .. toctree::
    :maxdepth: 2
 
+   v_120
    v_1
    v_06
\ No newline at end of file
diff --git a/docs/whats_new/v_06.rst b/docs/whats_new/v_06.rst
index ec157b6..80abbe4 100644
--- a/docs/whats_new/v_06.rst
+++ b/docs/whats_new/v_06.rst
@@ -1,7 +1,7 @@
 .. -*- mode: rst -*-
 
-Version 0.6.1 and previous
-==========================
+Version 0.6.X
+=============
 
 Version 0.6.1
 -------------
diff --git a/docs/whats_new/v_1.rst b/docs/whats_new/v_1.rst
index 8a520cd..1a2535c 100644
--- a/docs/whats_new/v_1.rst
+++ b/docs/whats_new/v_1.rst
@@ -1,5 +1,5 @@
-Version 1.1.2 and previous
-==========================
+Version 1.1.X
+=============
 
 Version 1.1.2
 -------------
diff --git a/docs/whats_new/v_120.rst b/docs/whats_new/v_120.rst
new file mode 100644
index 0000000..126aee8
--- /dev/null
+++ b/docs/whats_new/v_120.rst
@@ -0,0 +1,68 @@
+Version 1.2.X
+=============
+
+Version 1.2.0
+-------------
+
+Deployed: 4th January 2022
+
+Contributors
+~~~~~~~~~~~~
+
+    - `Edoardo Argiolas <https://github.com/dodoarg>`_
+    - `gverbock <https://github.com/gverbock>`_
+    - `Thibault Blanc <https://github.com/thibaultbl>`_
+    - `David Cortes <https://github.com/david-cortes>`_
+    - `Morgan Sell <https://github.com/Morgan-Sell>`_
+    - `Kevin Kurek <https://github.com/kevinkurek>`_
+    - `Soledad Galli <https://github.com/solegalli>`_
+
+In this big release, we add 3 new transformers, we expand the functionality of existing
+classes, we add information about citing Feature-engine and we expand the documentation
+with a new look, extended user guide with examples, and more details on how to
+contribute to the project.
+
+Thank you so much to the contributors for making this massive release possible!
+
+Thank you to reviewers `Nicolas Galli <https://github.com/nicogalli>`_ and
+`Chris Samiullah <https://github.com/christophergs>`_ for useful advice on
+various PRs.
+
+New transformers
+~~~~~~~~~~~~~~~~
+
+    - **DatetimeFeatures**: extracts date and time features from datetime variables (`Edoardo Argiolas <https://github.com/dodoarg>`_)
+    - **DropHishPSIFeatures**: finds and drops features with high population stability index (`gverbock <https://github.com/gverbock>`_)
+    - **Matchvariables**: ensures that the same variables observed in the train set are present in the test set (`Thibault Blanc <https://github.com/thibaultbl>`_)
+
+Enhancements
+~~~~~~~~~~~~
+
+    - The **Winsorizer** can now add binary variable indicators to flag outlier values (`David Cortes <https://github.com/david-cortes>`_)
+    - The **DropMissingData** now allows to drop rows based on % of missing data (`Kevin Kurek <https://github.com/kevinkurek>`_)
+    - **Categorical encoders** can now either raise a warning or an error when encoding categories not seen in the train set (`Morgan Sell <https://github.com/Morgan-Sell>`_)
+    - The **ArbitraryDiscretiser** can now either raise a warning or an error when values fall outside the limits entered by the user (`Morgan Sell <https://github.com/Morgan-Sell>`_)
+    - **CombineWithReferenceFeature** and **MathematicalCombination** have now the option to drop the original input variables after the feature creation (`Edoardo Argiolas <https://github.com/dodoarg>`_)
+
+Bug fixes
+~~~~~~~~~
+
+    - All **Encoders** are now able to exclude datetime variables cast as object or categorical when searching for categorical variables automatically (`Edoardo Argiolas <https://github.com/dodoarg>`_)
+    - All transformers will now raise an error when users pass an empty list to the variables parameter (`Edoardo Argiolas <https://github.com/dodoarg>`_)
+    - All transformers now check the variable type when user passes a single variable to the variables parameter (`Edoardo Argiolas <https://github.com/dodoarg>`_)
+
+
+Documentation
+~~~~~~~~~~~~~
+    - We changed the template to pydata (`Soledad Galli <https://github.com/solegalli>`_)
+    - We split the information about transformers into a user guide and an API (`Soledad Galli <https://github.com/solegalli>`_)
+    - The API documentation shows how to use the transformers (`Soledad Galli <https://github.com/solegalli>`_)
+    - The user guide expands the API docs with plenty of examples and tips on when and how to use the transformers (`Soledad Galli <https://github.com/solegalli>`_)
+    - We expanded the contribute section with plenty of details on how to make a contribution and how to check your code is top notch (`Soledad Galli <https://github.com/solegalli>`_)
+    - You can now sponsor Feature-engine (`Soledad Galli <https://github.com/solegalli>`_)
+    - You can now cite our JOSS article when using Feature-engine (`Soledad Galli <https://github.com/solegalli>`_)
+    - We added plenty of examples on how to use the new class DropHighPSIFeatures (`gverbock <https://github.com/gverbock>`_)
+    - We included various examples on how to extract date and time features using the new DatetimeFeatures class (`Edoardo Argiolas <https://github.com/dodoarg>`_)
+    - We included examples on how to use the new class MatchVariables (`Thibault Blanc <https://github.com/thibaultbl>`_)
+    - We added a Jupyter notebook with a demo of the new DatetimeFeatures class (`Edoardo Argiolas <https://github.com/dodoarg>`_)
+    - We added a Jupyter notebook with a demo of the new DropHighPSIFeatures class (`Soledad Galli <https://github.com/solegalli>`_)
diff --git a/feature_engine/VERSION b/feature_engine/VERSION
index 45a1b3f..26aaba0 100644
--- a/feature_engine/VERSION
+++ b/feature_engine/VERSION
@@ -1,1 +1,1 @@
-1.1.2
+1.2.0
diff --git a/feature_engine/creation/combine_with_reference_feature.py b/feature_engine/creation/combine_with_reference_feature.py
index 3d6c79c..b3d2e47 100644
--- a/feature_engine/creation/combine_with_reference_feature.py
+++ b/feature_engine/creation/combine_with_reference_feature.py
@@ -65,6 +65,9 @@ class CombineWithReferenceFeature(BaseEstimator, TransformerMixin):
         transformer will return an error if the training or the datasets to transform
         contain missing values.
 
+    drop_original: bool, default=False
+        If True, the original variables will be dropped from the dataframe
+        after their combination.
 
     Attributes
     ----------
@@ -98,6 +101,7 @@ class CombineWithReferenceFeature(BaseEstimator, TransformerMixin):
         operations: List[str] = ["sub"],
         new_variables_names: Optional[List[str]] = None,
         missing_values: str = "ignore",
+        drop_original: bool = False,
     ) -> None:
 
         # check input types
@@ -158,11 +162,18 @@ class CombineWithReferenceFeature(BaseEstimator, TransformerMixin):
         if missing_values not in ["raise", "ignore"]:
             raise ValueError("missing_values takes only values 'raise' or 'ignore'")
 
+        if not isinstance(drop_original, bool):
+            raise TypeError(
+                "drop_original takes only boolean values True and False. "
+                f"Got {drop_original} instead."
+            )
+
         self.reference_variables = reference_variables
         self.variables_to_combine = variables_to_combine
         self.new_variables_names = new_variables_names
         self.operations = operations
         self.missing_values = missing_values
+        self.drop_original = drop_original
 
     def fit(self, X: pd.DataFrame, y: Optional[pd.Series] = None):
         """
@@ -286,6 +297,12 @@ class CombineWithReferenceFeature(BaseEstimator, TransformerMixin):
         if self.new_variables_names:
             X.columns = original_col_names + self.new_variables_names
 
+        if self.drop_original:
+            X.drop(
+                columns=set(self.variables_to_combine + self.reference_variables),
+                inplace=True,
+            )
+
         return X
 
     def _more_tags(self):
diff --git a/feature_engine/creation/mathematical_combination.py b/feature_engine/creation/mathematical_combination.py
index 2cb8ebe..69d28b4 100644
--- a/feature_engine/creation/mathematical_combination.py
+++ b/feature_engine/creation/mathematical_combination.py
@@ -103,6 +103,7 @@ class MathematicalCombination(BaseEstimator, TransformerMixin):
         math_operations: Optional[List[str]] = None,
         new_variables_names: Optional[List[str]] = None,
         missing_values: str = "raise",
+        drop_original: bool = False,
     ) -> None:
 
         # check input types
@@ -158,10 +159,17 @@ class MathematicalCombination(BaseEstimator, TransformerMixin):
                     "combine."
                 )
 
+        if not isinstance(drop_original, bool):
+            raise TypeError(
+                "drop_original takes only boolean values True and False. "
+                f"Got {drop_original} instead."
+            )
+
         self.variables_to_combine = variables_to_combine
         self.new_variables_names = new_variables_names
         self.math_operations = math_operations
         self.missing_values = missing_values
+        self.drop_original = drop_original
 
     def fit(self, X: pd.DataFrame, y: Optional[pd.Series] = None):
         """
@@ -251,6 +259,9 @@ class MathematicalCombination(BaseEstimator, TransformerMixin):
         for new_variable_name, operation in self.combination_dict_.items():
             X[new_variable_name] = X[self.variables_to_combine].agg(operation, axis=1)
 
+        if self.drop_original:
+            X.drop(columns=self.variables_to_combine, inplace=True)
+
         return X
 
     def _more_tags(self):
diff --git a/feature_engine/datetime/datetime.py b/feature_engine/datetime/datetime.py
index de73f22..8e25bc8 100644
--- a/feature_engine/datetime/datetime.py
+++ b/feature_engine/datetime/datetime.py
@@ -39,11 +39,10 @@ class DatetimeFeatures(BaseEstimator, TransformerMixin):
     - "quarter"
     - "semester"
     - "year"
-    - "week_of_the_month"
-    - "week_of_the_year"
-    - "day_of_the_week"
-    - "day_of_the_month"
-    - "day_of_the_year"
+    - "week"
+    - "day_of_week"
+    - "day_of_month"
+    - "day_of_year"
     - "weekend"
     - "month_start"
     - "month_end"
@@ -68,7 +67,7 @@ class DatetimeFeatures(BaseEstimator, TransformerMixin):
 
     features_to_extract: list, default=None
         The list of date features to extract. If None, the following features will be
-        extracted: "month", "year", "day_of_the_week", "day_of_the_month", "hour",
+        extracted: "month", "year", "day_of_week", "day_of_month", "hour",
         "minute" and "second". If "all", all supported features will be extracted.
         Alternatively, you can pass a list with the names of the supported features
         you want to extract.
diff --git a/feature_engine/datetime/datetime_constants.py b/feature_engine/datetime/datetime_constants.py
index 123f3d5..273247f 100644
--- a/feature_engine/datetime/datetime_constants.py
+++ b/feature_engine/datetime/datetime_constants.py
@@ -5,10 +5,10 @@ FEATURES_SUPPORTED = [
     "quarter",
     "semester",
     "year",
-    "week_of_the_year",
-    "day_of_the_week",
-    "day_of_the_month",
-    "day_of_the_year",
+    "week",
+    "day_of_week",
+    "day_of_month",
+    "day_of_year",
     "weekend",
     "month_start",
     "month_end",
@@ -28,10 +28,10 @@ FEATURES_SUFFIXES = {
     "quarter": "_quarter",
     "semester": "_semester",
     "year": "_year",
-    "week_of_the_year": "_woty",
-    "day_of_the_week": "_dotw",
-    "day_of_the_month": "_dotm",
-    "day_of_the_year": "_doty",
+    "week": "_week",
+    "day_of_week": "_day_of_week",
+    "day_of_month": "_day_of_month",
+    "day_of_year": "_day_of_year",
     "weekend": "_weekend",
     "month_start": "_month_start",
     "month_end": "_month_end",
@@ -51,10 +51,10 @@ FEATURES_FUNCTIONS = {
     "quarter": lambda x: x.dt.quarter,
     "semester": lambda x: np.where(x.dt.month <= 6, 1, 2).astype(np.int64),
     "year": lambda x: x.dt.year,
-    "week_of_the_year": lambda x: x.dt.isocalendar().week.astype(np.int64),
-    "day_of_the_week": lambda x: x.dt.dayofweek,
-    "day_of_the_month": lambda x: x.dt.day,
-    "day_of_the_year": lambda x: x.dt.dayofyear,
+    "week": lambda x: x.dt.isocalendar().week.astype(np.int64),
+    "day_of_week": lambda x: x.dt.dayofweek,
+    "day_of_month": lambda x: x.dt.day,
+    "day_of_year": lambda x: x.dt.dayofyear,
     "weekend": lambda x: np.where(x.dt.dayofweek <= 4, 0, 1).astype(np.int64),
     "month_start": lambda x: x.dt.is_month_start.astype(np.int64),
     "month_end": lambda x: x.dt.is_month_end.astype(np.int64),
@@ -72,8 +72,8 @@ FEATURES_FUNCTIONS = {
 FEATURES_DEFAULT = [
     "month",
     "year",
-    "day_of_the_week",
-    "day_of_the_month",
+    "day_of_week",
+    "day_of_month",
     "hour",
     "minute",
     "second",
diff --git a/feature_engine/discretisation/arbitrary.py b/feature_engine/discretisation/arbitrary.py
index 4a26dd2..02a2274 100644
--- a/feature_engine/discretisation/arbitrary.py
+++ b/feature_engine/discretisation/arbitrary.py
@@ -1,15 +1,16 @@
 # Authors: Soledad Galli <solegalli@protonmail.com>
 # License: BSD 3 clause
 
+import warnings
 from typing import Dict, List, Optional, Union
 
 import pandas as pd
 
-from feature_engine.base_transformers import BaseNumericalTransformer
+from feature_engine.discretisation.base_discretiser import BaseDiscretiser
 from feature_engine.validation import _return_tags
 
 
-class ArbitraryDiscretiser(BaseNumericalTransformer):
+class ArbitraryDiscretiser(BaseDiscretiser):
     """
     The ArbitraryDiscretiser() divides numerical variables into intervals which limits
     are determined by the user. Thus, it works only with numerical variables.
@@ -39,6 +40,12 @@ class ArbitraryDiscretiser(BaseNumericalTransformer):
         Whether the output, that is the bins, should be the interval boundaries. If
         True, it returns the interval boundaries. If False, it returns integers.
 
+    errors: string, default='ignore'
+        Indicates what to do when a value is outside the limits indicated in the
+        'binning_dict'. If 'raise', the transformation will raise an error.
+        If 'ignore', values outside the limits are returned as NaN
+        and a warning will be raised instead.
+
     Attributes
     ----------
     binner_dict_:
@@ -69,19 +76,25 @@ class ArbitraryDiscretiser(BaseNumericalTransformer):
         binning_dict: Dict[Union[str, int], List[Union[str, int]]],
         return_object: bool = False,
         return_boundaries: bool = False,
+        errors: str = "ignore",
     ) -> None:
 
         if not isinstance(binning_dict, dict):
             raise ValueError(
-                "Please provide at a dictionary with the interval limits per variable"
+                "binning_dict must be a dictionary with the interval limits per "
+                f"variable. Got {binning_dict} instead."
+            )
+
+        if errors not in ["ignore", "raise"]:
+            raise ValueError(
+                "errors only takes values 'ignore' and 'raise'. "
+                f"Got {errors} instead."
             )
 
-        if not isinstance(return_object, bool):
-            raise ValueError("return_object must be True or False")
+        super().__init__(return_object, return_boundaries)
 
         self.binning_dict = binning_dict
-        self.return_object = return_object
-        self.return_boundaries = return_boundaries
+        self.errors = errors
 
     def fit(self, X: pd.DataFrame, y: Optional[pd.Series] = None):
         """
@@ -109,34 +122,42 @@ class ArbitraryDiscretiser(BaseNumericalTransformer):
     def transform(self, X: pd.DataFrame) -> pd.DataFrame:
         """Sort the variable values into the intervals.
 
-        Parameters
-        ----------
-        X: pandas dataframe of shape = [n_samples, n_features]
-            The dataframe to be transformed.
+       Parameters
+       ----------
+       X: pandas dataframe of shape = [n_samples, n_features]
+           The dataframe to be transformed.
 
-        Returns
-        -------
-        X_new: pandas dataframe of shape = [n_samples, n_features]
-            The transformed data with the discrete variables.
-        """
+       Returns
+       -------
+       X_new: pandas dataframe of shape = [n_samples, n_features]
+           The transformed data with the discrete variables.
+       """
 
-        # check input dataframe and if class was fitted
         X = super().transform(X)
+        # check if NaN values were introduced by the discretisation procedure.
+        if X[self.variables_].isnull().sum().sum() > 0:
 
-        # transform variables
-        if self.return_boundaries:
-            for feature in self.variables_:
-                X[feature] = pd.cut(X[feature], self.binner_dict_[feature])
+            # obtain the name(s) of the columns with null values
+            nan_columns = (
+                X[self.variables_].columns[X[self.variables_].isnull().any()].tolist()
+            )
 
-        else:
-            for feature in self.variables_:
-                X[feature] = pd.cut(
-                    X[feature], self.binner_dict_[feature], labels=False
+            if len(nan_columns) > 1:
+                nan_columns_str = ", ".join(nan_columns)
+            else:
+                nan_columns_str = nan_columns[0]
+
+            if self.errors == "ignore":
+                warnings.warn(
+                    f"During the discretisation, NaN values were introduced in "
+                    f"the feature(s) {nan_columns_str}."
                 )
 
-            # return object
-            if self.return_object:
-                X[self.variables_] = X[self.variables_].astype("O")
+            elif self.errors == "raise":
+                raise ValueError(
+                    "During the discretisation, NaN values were introduced in "
+                    f"the feature(s) {nan_columns_str}."
+                )
 
         return X
 
diff --git a/feature_engine/discretisation/base_discretiser.py b/feature_engine/discretisation/base_discretiser.py
new file mode 100644
index 0000000..4db2e0b
--- /dev/null
+++ b/feature_engine/discretisation/base_discretiser.py
@@ -0,0 +1,82 @@
+# Authors: Morgan Sell <morganpsell@gmail.com>
+# License: BSD 3 clause
+
+import pandas as pd
+
+from feature_engine.base_transformers import BaseNumericalTransformer
+
+
+class BaseDiscretiser(BaseNumericalTransformer):
+    """
+    Shared set-up checks and methods across numerical discretisers.
+
+    Parameters
+    ----------
+    return_object: bool, default=False
+        Whether the the discrete variable should be returned as numeric or as
+        object. If you would like to proceed with the engineering of the variable as if
+        it was categorical, use True. Alternatively, keep the default to False.
+
+    return_boundaries: bool, default=False
+        Whether the output should be the interval boundaries. If True, it returns
+        the interval boundaries. If False, it returns integers.
+
+    Methods
+    -------
+    transform:
+        Sort continuous variable values into the intervals.
+    """
+
+    def __init__(
+        self,
+        return_object: bool = False,
+        return_boundaries: bool = False,
+    ) -> None:
+
+        if not isinstance(return_object, bool):
+            raise ValueError(
+                "return_object must be True or False. " f"Got {return_object} instead."
+            )
+
+        if not isinstance(return_boundaries, bool):
+            raise ValueError(
+                "return_boundaries must be True or False. "
+                f"Got {return_boundaries} instead."
+            )
+
+        self.return_object = return_object
+        self.return_boundaries = return_boundaries
+
+    def transform(self, X: pd.DataFrame) -> pd.DataFrame:
+        """Sort the variable values into the intervals.
+
+        Parameters
+        ----------
+        X: pandas dataframe of shape = [n_samples, n_features]
+            The data to transform.
+
+        Returns
+        -------
+        X_new: pandas dataframe of shape = [n_samples, n_features]
+            The transformed data with the discrete variables.
+        """
+
+        # check input dataframe and if class was fitted
+        X = super().transform(X)
+
+        # transform variables
+        if self.return_boundaries:
+            for feature in self.variables_:
+                X[feature] = pd.cut(X[feature], self.binner_dict_[feature])
+
+        else:
+            for feature in self.variables_:
+                X[feature] = pd.cut(
+                    X[feature], self.binner_dict_[feature], labels=False
+                )
+
+            # return object
+            if self.return_object:
+                X[self.variables_] = X[self.variables_].astype("O")
+
+        return X
diff --git a/feature_engine/discretisation/equal_frequency.py b/feature_engine/discretisation/equal_frequency.py
index fe59fff..331cc87 100644
--- a/feature_engine/discretisation/equal_frequency.py
+++ b/feature_engine/discretisation/equal_frequency.py
@@ -5,11 +5,11 @@ from typing import List, Optional, Union
 
 import pandas as pd
 
-from feature_engine.base_transformers import BaseNumericalTransformer
+from feature_engine.discretisation.base_discretiser import BaseDiscretiser
 from feature_engine.variable_manipulation import _check_input_parameter_variables
 
 
-class EqualFrequencyDiscretiser(BaseNumericalTransformer):
+class EqualFrequencyDiscretiser(BaseDiscretiser):
     """
     The EqualFrequencyDiscretiser() divides continuous numerical variables
     into contiguous equal frequency intervals, that is, intervals that contain
@@ -86,18 +86,12 @@ class EqualFrequencyDiscretiser(BaseNumericalTransformer):
     ) -> None:
 
         if not isinstance(q, int):
-            raise ValueError("q must be an integer")
+            raise ValueError(f"q must be an integer. Got {q} instead.")
 
-        if not isinstance(return_object, bool):
-            raise ValueError("return_object must be True or False")
-
-        if not isinstance(return_boundaries, bool):
-            raise ValueError("return_boundaries must be True or False")
+        super().__init__(return_object, return_boundaries)
 
         self.q = q
         self.variables = _check_input_parameter_variables(variables)
-        self.return_object = return_object
-        self.return_boundaries = return_boundaries
 
     def fit(self, X: pd.DataFrame, y: Optional[pd.Series] = None):
         """
@@ -129,37 +123,3 @@ class EqualFrequencyDiscretiser(BaseNumericalTransformer):
         self.n_features_in_ = X.shape[1]
 
         return self
-
-    def transform(self, X: pd.DataFrame) -> pd.DataFrame:
-        """Sort the variable values into the intervals.
-
-        Parameters
-        ----------
-        X: pandas dataframe of shape = [n_samples, n_features]
-            The data to transform.
-
-        Returns
-        -------
-        X_new: pandas dataframe of shape = [n_samples, n_features]
-            The transformed data with the discrete variables.
-        """
-
-        # check input dataframe and if class was fitted
-        X = super().transform(X)
-
-        # transform variables
-        if self.return_boundaries:
-            for feature in self.variables_:
-                X[feature] = pd.cut(X[feature], self.binner_dict_[feature])
-
-        else:
-            for feature in self.variables_:
-                X[feature] = pd.cut(
-                    X[feature], self.binner_dict_[feature], labels=False
-                )
-
-            # return object
-            if self.return_object:
-                X[self.variables_] = X[self.variables_].astype("O")
-
-        return X
diff --git a/feature_engine/discretisation/equal_width.py b/feature_engine/discretisation/equal_width.py
index fbc7bc6..6d6560e 100644
--- a/feature_engine/discretisation/equal_width.py
+++ b/feature_engine/discretisation/equal_width.py
@@ -5,11 +5,11 @@ from typing import List, Optional, Union
 
 import pandas as pd
 
-from feature_engine.base_transformers import BaseNumericalTransformer
+from feature_engine.discretisation.base_discretiser import BaseDiscretiser
 from feature_engine.variable_manipulation import _check_input_parameter_variables
 
 
-class EqualWidthDiscretiser(BaseNumericalTransformer):
+class EqualWidthDiscretiser(BaseDiscretiser):
     """
     The EqualWidthDiscretiser() divides continuous numerical variables into
     intervals of the same width, that is, equidistant intervals. Note that the
@@ -95,18 +95,12 @@ class EqualWidthDiscretiser(BaseNumericalTransformer):
     ) -> None:
 
         if not isinstance(bins, int):
-            raise ValueError("q must be an integer")
+            raise ValueError(f"bins must be an integer. Got {bins} instead.")
 
-        if not isinstance(return_object, bool):
-            raise ValueError("return_object must be True or False")
-
-        if not isinstance(return_boundaries, bool):
-            raise ValueError("return_boundaries must be True or False")
+        super().__init__(return_object, return_boundaries)
 
         self.bins = bins
         self.variables = _check_input_parameter_variables(variables)
-        self.return_object = return_object
-        self.return_boundaries = return_boundaries
 
     def fit(self, X: pd.DataFrame, y: Optional[pd.Series] = None):
         """
@@ -142,38 +136,3 @@ class EqualWidthDiscretiser(BaseNumericalTransformer):
         self.n_features_in_ = X.shape[1]
 
         return self
-
-    def transform(self, X: pd.DataFrame) -> pd.DataFrame:
-        """
-        Sort the variable values into the intervals.
-
-        Parameters
-        ----------
-        X: pandas dataframe of shape = [n_samples, n_features]
-            The data to transform.
-
-        Returns
-        -------
-        X_new: pandas dataframe of shape = [n_samples, n_features]
-            The transformed data with the discrete variables.
-        """
-
-        # check input dataframe and if class was fitted
-        X = super().transform(X)
-
-        # transform variables
-        if self.return_boundaries:
-            for feature in self.variables_:
-                X[feature] = pd.cut(X[feature], self.binner_dict_[feature])
-
-        else:
-            for feature in self.variables_:
-                X[feature] = pd.cut(
-                    X[feature], self.binner_dict_[feature], labels=False
-                )
-
-            # return object
-            if self.return_object:
-                X[self.variables_] = X[self.variables_].astype("O")
-
-        return X
diff --git a/feature_engine/encoding/base_encoder.py b/feature_engine/encoding/base_encoder.py
index eb5b814..b117fc7 100644
--- a/feature_engine/encoding/base_encoder.py
+++ b/feature_engine/encoding/base_encoder.py
@@ -14,11 +14,43 @@ from feature_engine.validation import _return_tags
 from feature_engine.variable_manipulation import (
     _find_all_variables,
     _find_or_check_categorical_variables,
+    _check_input_parameter_variables,
 )
 
 
 class BaseCategoricalTransformer(BaseEstimator, TransformerMixin):
-    """shared set-up checks and methods across categorical transformers"""
+    """shared set-up checks and methods across categorical transformers
+
+    Parameters
+    ----------
+    variables: list, default=None
+        The list of categorical variables that will be encoded. If None, the
+        encoder will find and transform all variables of type object or categorical by
+        default. You can also make the transformer accept numerical variables, see the
+        next parameter.
+
+    ignore_format: bool, default=False
+        Whether the format in which the categorical variables are cast should be
+        ignored. If False, the encoder will automatically select variables of type
+        object or categorical, or check that the variables entered by the user are of
+        type object or categorical. If True, the encoder will select all variables or
+        accept all variables entered by the user, including those cast as numeric.
+    """
+
+    def __init__(
+        self,
+        variables: Union[None, int, str, List[Union[str, int]]] = None,
+        ignore_format: bool = False,
+    ) -> None:
+
+        if not isinstance(ignore_format, bool):
+            raise ValueError(
+                "ignore_format takes only booleans True and False. "
+                f"Got {ignore_format} instead."
+            )
+
+        self.variables = _check_input_parameter_variables(variables)
+        self.ignore_format = ignore_format
 
     def _check_fit_input_and_variables(self, X: pd.DataFrame) -> pd.DataFrame:
         """
@@ -144,15 +176,30 @@ class BaseCategoricalTransformer(BaseEstimator, TransformerMixin):
 
         # check if NaN values were introduced by the encoding
         if X[self.encoder_dict_.keys()].isnull().sum().sum() > 0:
-            warnings.warn(
-                "NaN values were introduced in the returned dataframe by the encoder."
-                "This means that some of the categories in the input dataframe were "
-                "not present in the training set used when the fit method was called. "
-                "Thus, mappings for those categories do not exist. Try using the "
-                "RareLabelCategoricalEncoder to remove infrequent categories before "
-                "calling this encoder."
+
+            # obtain the name(s) of the columns have null values
+            nan_columns = (
+                X[self.encoder_dict_.keys()]
+                .columns[X[self.encoder_dict_.keys()].isnull().any()]
+                .tolist()
             )
 
+            if len(nan_columns) > 1:
+                nan_columns_str = ", ".join(nan_columns)
+            else:
+                nan_columns_str = nan_columns[0]
+
+            if self.errors == "ignore":
+                warnings.warn(
+                    "During the encoding, NaN values were introduced in the feature(s) "
+                    f"{nan_columns_str}."
+                )
+            elif self.errors == "raise":
+                raise ValueError(
+                    "During the encoding, NaN values were introduced in the feature(s) "
+                    f"{nan_columns_str}."
+                )
+
         return X
 
     def inverse_transform(self, X: pd.DataFrame) -> pd.DataFrame:
@@ -186,3 +233,47 @@ class BaseCategoricalTransformer(BaseEstimator, TransformerMixin):
         # so we need to leave without this test
         tags_dict["_xfail_checks"]["check_estimators_nan_inf"] = "transformer allows NA"
         return tags_dict
+
+
+class BaseCategorical(BaseCategoricalTransformer):
+    """
+    BaseCategorical() is the parent class to some of the encoders.
+    It shares set-up checks of init parameters.
+
+    Parameters
+    ----------
+    variables: list, default=None
+        The list of categorical variables that will be encoded. If None, the
+        encoder will find and transform all variables of type object or categorical by
+        default. You can also make the transformer accept numerical variables, see the
+        next parameter.
+
+    ignore_format: bool, default=False
+        Whether the format in which the categorical variables are cast should be
+        ignored. If False, the encoder will automatically select variables of type
+        object or categorical, or check that the variables entered by the user are of
+        type object or categorical. If True, the encoder will select all variables or
+        accept all variables entered by the user, including those cast as numeric.
+
+    errors: string, default='ignore'
+        Indicates what to do, when categories not present in the train set are
+        encountered during transform. If 'raise', then rare categories will raise an
+        error. If 'ignore', then rare categories will be set as NaN and a warning will
+        be raised instead.
+    """
+
+    def __init__(
+        self,
+        variables: Union[None, int, str, List[Union[str, int]]] = None,
+        ignore_format: bool = False,
+        errors: str = "ignore",
+    ) -> None:
+
+        if errors not in ["raise", "ignore"]:
+            raise ValueError(
+                "errors takes only values 'raise' and 'ignore ."
+                f"Got {errors} instead."
+            )
+
+        super().__init__(variables, ignore_format)
+        self.errors = errors
diff --git a/feature_engine/encoding/count_frequency.py b/feature_engine/encoding/count_frequency.py
index 4cd43b7..e1f4cc9 100644
--- a/feature_engine/encoding/count_frequency.py
+++ b/feature_engine/encoding/count_frequency.py
@@ -5,11 +5,10 @@ from typing import List, Optional, Union
 
 import pandas as pd
 
-from feature_engine.encoding.base_encoder import BaseCategoricalTransformer
-from feature_engine.variable_manipulation import _check_input_parameter_variables
+from feature_engine.encoding.base_encoder import BaseCategorical
 
 
-class CountFrequencyEncoder(BaseCategoricalTransformer):
+class CountFrequencyEncoder(BaseCategorical):
     """
     The CountFrequencyEncoder() replaces categories by either the count or the
     percentage of observations per category.
@@ -55,6 +54,12 @@ class CountFrequencyEncoder(BaseCategoricalTransformer):
         type object or categorical. If True, the encoder will select all variables or
         accept all variables entered by the user, including those cast as numeric.
 
+    errors: string, default='ignore'
+        Indicates what to do when categories not present in the train set are
+        encountered during transform. If 'raise', then rare categories will raise an
+        error. If 'ignore', then rare categories will be set as NaN and a warning will
+        be raised instead.
+
     Attributes
     ----------
     encoder_dict_:
@@ -97,18 +102,16 @@ class CountFrequencyEncoder(BaseCategoricalTransformer):
         encoding_method: str = "count",
         variables: Union[None, int, str, List[Union[str, int]]] = None,
         ignore_format: bool = False,
+        errors: str = "ignore"
     ) -> None:
 
         if encoding_method not in ["count", "frequency"]:
             raise ValueError(
                 "encoding_method takes only values 'count' and 'frequency'"
             )
-        if not isinstance(ignore_format, bool):
-            raise ValueError("ignore_format takes only booleans True and False")
+        super().__init__(variables, ignore_format, errors)
 
         self.encoding_method = encoding_method
-        self.variables = _check_input_parameter_variables(variables)
-        self.ignore_format = ignore_format
 
     def fit(self, X: pd.DataFrame, y: Optional[pd.Series] = None):
         """
@@ -149,11 +152,11 @@ class CountFrequencyEncoder(BaseCategoricalTransformer):
 
         return X
 
-    transform.__doc__ = BaseCategoricalTransformer.transform.__doc__
+    transform.__doc__ = BaseCategorical.transform.__doc__
 
     def inverse_transform(self, X: pd.DataFrame) -> pd.DataFrame:
         X = super().inverse_transform(X)
 
         return X
 
-    inverse_transform.__doc__ = BaseCategoricalTransformer.inverse_transform.__doc__
+    inverse_transform.__doc__ = BaseCategorical.inverse_transform.__doc__
diff --git a/feature_engine/encoding/decision_tree.py b/feature_engine/encoding/decision_tree.py
index 5a097e3..d5d288d 100644
--- a/feature_engine/encoding/decision_tree.py
+++ b/feature_engine/encoding/decision_tree.py
@@ -9,7 +9,6 @@ from sklearn.pipeline import Pipeline
 from feature_engine.discretisation import DecisionTreeDiscretiser
 from feature_engine.encoding.base_encoder import BaseCategoricalTransformer
 from feature_engine.encoding.ordinal import OrdinalEncoder
-from feature_engine.variable_manipulation import _check_input_parameter_variables
 
 
 class DecisionTreeEncoder(BaseCategoricalTransformer):
@@ -139,14 +138,13 @@ class DecisionTreeEncoder(BaseCategoricalTransformer):
         ignore_format: bool = False,
     ) -> None:
 
+        super().__init__(variables, ignore_format)
         self.encoding_method = encoding_method
         self.cv = cv
         self.scoring = scoring
         self.regression = regression
         self.param_grid = param_grid
         self.random_state = random_state
-        self.variables = _check_input_parameter_variables(variables)
-        self.ignore_format = ignore_format
 
     def fit(self, X: pd.DataFrame, y: Optional[pd.Series] = None):
         """
@@ -176,6 +174,7 @@ class DecisionTreeEncoder(BaseCategoricalTransformer):
             encoding_method=self.encoding_method,
             variables=self.variables_,
             ignore_format=self.ignore_format,
+            errors="raise",
         )
 
         # initialize decision tree discretiser
diff --git a/feature_engine/encoding/mean_encoding.py b/feature_engine/encoding/mean_encoding.py
index ba99efd..83ef8c6 100644
--- a/feature_engine/encoding/mean_encoding.py
+++ b/feature_engine/encoding/mean_encoding.py
@@ -5,11 +5,10 @@ from typing import List, Union
 
 import pandas as pd
 
-from feature_engine.encoding.base_encoder import BaseCategoricalTransformer
-from feature_engine.variable_manipulation import _check_input_parameter_variables
+from feature_engine.encoding.base_encoder import BaseCategorical
 
 
-class MeanEncoder(BaseCategoricalTransformer):
+class MeanEncoder(BaseCategorical):
     """
     The MeanEncoder() replaces categories by the mean value of the target for each
     category.
@@ -47,6 +46,12 @@ class MeanEncoder(BaseCategoricalTransformer):
         type object or categorical. If True, the encoder will select all variables or
         accept all variables entered by the user, including those cast as numeric.
 
+    errors: string, default='ignore'
+        Indicates what to do when categories not present in the train set are
+        encountered during transform. If 'raise', then rare categories will raise an
+        error. If 'ignore', then rare categories will be set as NaN and a warning will
+        be raised instead.
+
     Attributes
     ----------
     encoder_dict_:
@@ -95,13 +100,10 @@ class MeanEncoder(BaseCategoricalTransformer):
         self,
         variables: Union[None, int, str, List[Union[str, int]]] = None,
         ignore_format: bool = False,
+        errors: str = "ignore"
     ) -> None:
 
-        if not isinstance(ignore_format, bool):
-            raise ValueError("ignore_format takes only booleans True and False")
-
-        self.variables = _check_input_parameter_variables(variables)
-        self.ignore_format = ignore_format
+        super().__init__(variables, ignore_format, errors)
 
     def fit(self, X: pd.DataFrame, y: pd.Series):
         """
@@ -142,11 +144,11 @@ class MeanEncoder(BaseCategoricalTransformer):
 
         return X
 
-    transform.__doc__ = BaseCategoricalTransformer.transform.__doc__
+    transform.__doc__ = BaseCategorical.transform.__doc__
 
     def inverse_transform(self, X: pd.DataFrame) -> pd.DataFrame:
         X = super().inverse_transform(X)
 
         return X
 
-    inverse_transform.__doc__ = BaseCategoricalTransformer.inverse_transform.__doc__
+    inverse_transform.__doc__ = BaseCategorical.inverse_transform.__doc__
diff --git a/feature_engine/encoding/one_hot.py b/feature_engine/encoding/one_hot.py
index eb8bfe4..6db7f34 100644
--- a/feature_engine/encoding/one_hot.py
+++ b/feature_engine/encoding/one_hot.py
@@ -7,7 +7,6 @@ import numpy as np
 import pandas as pd
 
 from feature_engine.encoding.base_encoder import BaseCategoricalTransformer
-from feature_engine.variable_manipulation import _check_input_parameter_variables
 
 
 class OneHotEncoder(BaseCategoricalTransformer):
@@ -146,14 +145,10 @@ class OneHotEncoder(BaseCategoricalTransformer):
         if not isinstance(drop_last_binary, bool):
             raise ValueError("drop_last_binary takes only True or False")
 
-        if not isinstance(ignore_format, bool):
-            raise ValueError("ignore_format takes only booleans True and False")
-
+        super().__init__(variables, ignore_format)
         self.top_categories = top_categories
         self.drop_last = drop_last
         self.drop_last_binary = drop_last_binary
-        self.variables = _check_input_parameter_variables(variables)
-        self.ignore_format = ignore_format
 
     def fit(self, X: pd.DataFrame, y: Optional[pd.Series] = None):
         """
diff --git a/feature_engine/encoding/ordinal.py b/feature_engine/encoding/ordinal.py
index b1dd6d7..29ff95d 100644
--- a/feature_engine/encoding/ordinal.py
+++ b/feature_engine/encoding/ordinal.py
@@ -5,11 +5,10 @@ from typing import List, Optional, Union
 
 import pandas as pd
 
-from feature_engine.encoding.base_encoder import BaseCategoricalTransformer
-from feature_engine.variable_manipulation import _check_input_parameter_variables
+from feature_engine.encoding.base_encoder import BaseCategorical
 
 
-class OrdinalEncoder(BaseCategoricalTransformer):
+class OrdinalEncoder(BaseCategorical):
     """
     The OrdinalCategoricalEncoder() replaces categories by ordinal numbers
     (0, 1, 2, 3, etc). The numbers can be ordered based on the mean of the target
@@ -52,6 +51,12 @@ class OrdinalEncoder(BaseCategoricalTransformer):
         type object or categorical. If True, the encoder will select all variables or
         accept all variables entered by the user, including those cast as numeric.
 
+    errors: string, default='ignore'
+        Indicates what to do when categories not present in the train set are
+        encountered during transform. If 'raise', then rare categories will raise an
+        error. If 'ignore', then rare categories will be set as NaN and a warning will
+        be raised instead.
+
     Attributes
     ----------
     encoder_dict_:
@@ -102,6 +107,7 @@ class OrdinalEncoder(BaseCategoricalTransformer):
         encoding_method: str = "ordered",
         variables: Union[None, int, str, List[Union[str, int]]] = None,
         ignore_format: bool = False,
+        errors: str = "ignore"
     ) -> None:
 
         if encoding_method not in ["ordered", "arbitrary"]:
@@ -109,12 +115,9 @@ class OrdinalEncoder(BaseCategoricalTransformer):
                 "encoding_method takes only values 'ordered' and 'arbitrary'"
             )
 
-        if not isinstance(ignore_format, bool):
-            raise ValueError("ignore_format takes only booleans True and False")
+        super().__init__(variables, ignore_format, errors)
 
         self.encoding_method = encoding_method
-        self.variables = _check_input_parameter_variables(variables)
-        self.ignore_format = ignore_format
 
     def fit(self, X: pd.DataFrame, y: Optional[pd.Series] = None):
         """Learn the numbers to be used to replace the categories in each
@@ -174,11 +177,11 @@ class OrdinalEncoder(BaseCategoricalTransformer):
 
         return X
 
-    transform.__doc__ = BaseCategoricalTransformer.transform.__doc__
+    transform.__doc__ = BaseCategorical.transform.__doc__
 
     def inverse_transform(self, X: pd.DataFrame) -> pd.DataFrame:
         X = super().inverse_transform(X)
 
         return X
 
-    inverse_transform.__doc__ = BaseCategoricalTransformer.inverse_transform.__doc__
+    inverse_transform.__doc__ = BaseCategorical.inverse_transform.__doc__
diff --git a/feature_engine/encoding/probability_ratio.py b/feature_engine/encoding/probability_ratio.py
index c4e3d8f..24483e8 100644
--- a/feature_engine/encoding/probability_ratio.py
+++ b/feature_engine/encoding/probability_ratio.py
@@ -6,12 +6,11 @@ from typing import List, Union
 import numpy as np
 import pandas as pd
 
-from feature_engine.encoding.base_encoder import BaseCategoricalTransformer
+from feature_engine.encoding.base_encoder import BaseCategorical
 from feature_engine.validation import _return_tags
-from feature_engine.variable_manipulation import _check_input_parameter_variables
 
 
-class PRatioEncoder(BaseCategoricalTransformer):
+class PRatioEncoder(BaseCategorical):
     """
     The PRatioEncoder() replaces categories by the ratio of the probability of the
     target = 1 and the probability of the target = 0.
@@ -72,6 +71,12 @@ class PRatioEncoder(BaseCategoricalTransformer):
         type object or categorical. If True, the encoder will select all variables or
         accept all variables entered by the user, including those cast as numeric.
 
+    errors: string, default='ignore'
+        Indicates what to do when categories not present in the train set are
+        encountered during transform. If 'raise', then rare categories will raise an
+        error. If 'ignore', then rare categories will be set as NaN and a warning will
+        be raised instead.
+
     Attributes
     ----------
     encoder_dict_:
@@ -110,6 +115,7 @@ class PRatioEncoder(BaseCategoricalTransformer):
         encoding_method: str = "ratio",
         variables: Union[None, int, str, List[Union[str, int]]] = None,
         ignore_format: bool = False,
+        errors: str = "ignore"
     ) -> None:
 
         if encoding_method not in ["ratio", "log_ratio"]:
@@ -117,12 +123,9 @@ class PRatioEncoder(BaseCategoricalTransformer):
                 "encoding_method takes only values 'ratio' and 'log_ratio'"
             )
 
-        if not isinstance(ignore_format, bool):
-            raise ValueError("ignore_format takes only booleans True and False")
+        super().__init__(variables, ignore_format, errors)
 
         self.encoding_method = encoding_method
-        self.variables = _check_input_parameter_variables(variables)
-        self.ignore_format = ignore_format
 
     def fit(self, X: pd.DataFrame, y: pd.Series):
         """
@@ -198,14 +201,14 @@ class PRatioEncoder(BaseCategoricalTransformer):
 
         return X
 
-    transform.__doc__ = BaseCategoricalTransformer.transform.__doc__
+    transform.__doc__ = BaseCategorical.transform.__doc__
 
     def inverse_transform(self, X: pd.DataFrame) -> pd.DataFrame:
         X = super().inverse_transform(X)
 
         return X
 
-    inverse_transform.__doc__ = BaseCategoricalTransformer.inverse_transform.__doc__
+    inverse_transform.__doc__ = BaseCategorical.inverse_transform.__doc__
 
     def _more_tags(self):
         tags_dict = _return_tags()
diff --git a/feature_engine/encoding/rare_label.py b/feature_engine/encoding/rare_label.py
index 2e882dc..22bbced 100644
--- a/feature_engine/encoding/rare_label.py
+++ b/feature_engine/encoding/rare_label.py
@@ -8,7 +8,6 @@ import numpy as np
 import pandas as pd
 
 from feature_engine.encoding.base_encoder import BaseCategoricalTransformer
-from feature_engine.variable_manipulation import _check_input_parameter_variables
 
 
 class RareLabelEncoder(BaseCategoricalTransformer):
@@ -117,15 +116,11 @@ class RareLabelEncoder(BaseCategoricalTransformer):
             if max_n_categories < 0 or not isinstance(max_n_categories, int):
                 raise ValueError("max_n_categories takes only positive integer numbers")
 
-        if not isinstance(ignore_format, bool):
-            raise ValueError("ignore_format takes only booleans True and False")
-
+        super().__init__(variables, ignore_format)
         self.tol = tol
         self.n_categories = n_categories
         self.max_n_categories = max_n_categories
         self.replace_with = replace_with
-        self.variables = _check_input_parameter_variables(variables)
-        self.ignore_format = ignore_format
 
     def fit(self, X: pd.DataFrame, y: Optional[pd.Series] = None):
         """
diff --git a/feature_engine/encoding/woe.py b/feature_engine/encoding/woe.py
index 1186af8..1d319ab 100644
--- a/feature_engine/encoding/woe.py
+++ b/feature_engine/encoding/woe.py
@@ -6,12 +6,11 @@ from typing import List, Union
 import numpy as np
 import pandas as pd
 
-from feature_engine.encoding.base_encoder import BaseCategoricalTransformer
+from feature_engine.encoding.base_encoder import BaseCategorical
 from feature_engine.validation import _return_tags
-from feature_engine.variable_manipulation import _check_input_parameter_variables
 
 
-class WoEEncoder(BaseCategoricalTransformer):
+class WoEEncoder(BaseCategorical):
     """
     The WoEEncoder() replaces categories by the weight of evidence
     (WoE). The WoE was used primarily in the financial sector to create credit risk
@@ -55,6 +54,12 @@ class WoEEncoder(BaseCategoricalTransformer):
         type object or categorical. If True, the encoder will select all variables or
         accept all variables entered by the user, including those cast as numeric.
 
+    errors: string, default='ignore'
+        Indicates what to do when categories not present in the train set are
+        encountered during transform. If 'raise', then rare categories will raise an
+        error. If 'ignore', then rare categories will be set as NaN and a warning will
+        be raised instead.
+
     Attributes
     ----------
     encoder_dict_:
@@ -100,13 +105,10 @@ class WoEEncoder(BaseCategoricalTransformer):
         self,
         variables: Union[None, int, str, List[Union[str, int]]] = None,
         ignore_format: bool = False,
+        errors: str = "ignore"
     ) -> None:
 
-        if not isinstance(ignore_format, bool):
-            raise ValueError("ignore_format takes only booleans True and False")
-
-        self.variables = _check_input_parameter_variables(variables)
-        self.ignore_format = ignore_format
+        super().__init__(variables, ignore_format, errors)
 
     def fit(self, X: pd.DataFrame, y: pd.Series):
         """
@@ -178,14 +180,14 @@ class WoEEncoder(BaseCategoricalTransformer):
 
         return X
 
-    transform.__doc__ = BaseCategoricalTransformer.transform.__doc__
+    transform.__doc__ = BaseCategorical.transform.__doc__
 
     def inverse_transform(self, X: pd.DataFrame) -> pd.DataFrame:
         X = super().inverse_transform(X)
 
         return X
 
-    inverse_transform.__doc__ = BaseCategoricalTransformer.inverse_transform.__doc__
+    inverse_transform.__doc__ = BaseCategorical.inverse_transform.__doc__
 
     def _more_tags(self):
         tags_dict = _return_tags()


Test patch:
diff --git a/tests/test_creation/test_combine_with_reference_feature.py b/tests/test_creation/test_combine_with_reference_feature.py
index 30b8a34..63bb8d3 100644
--- a/tests/test_creation/test_combine_with_reference_feature.py
+++ b/tests/test_creation/test_combine_with_reference_feature.py
@@ -70,6 +70,15 @@ def test_error_if_new_variable_names_of_wrong_type():
         )
 
 
+def test_error_when_drop_original_not_bool():
+    with pytest.raises(TypeError):
+        CombineWithReferenceFeature(
+            variables_to_combine=["Age"],
+            reference_variables=["Marks"],
+            drop_original="not_a_bool"
+        )
+
+
 def test_error_when_variables_to_combine_not_numeric(df_vartypes):
     transformer = CombineWithReferenceFeature(
         variables_to_combine=["Name", "Age", "Marks"],
@@ -191,3 +200,27 @@ def test_user_enters_output_variable_names(df_vartypes):
 
     # transform params
     pd.testing.assert_frame_equal(X, ref)
+
+
+def test_drop_original_variables(df_vartypes):
+    transformer = CombineWithReferenceFeature(
+        variables_to_combine=["Age", "Marks"],
+        reference_variables=["Age", "Marks"],
+        drop_original=True
+    )
+
+    X = transformer.fit_transform(df_vartypes)
+
+    ref = pd.DataFrame.from_dict(
+        {
+            "Name": ["tom", "nick", "krish", "jack"],
+            "City": ["London", "Manchester", "Liverpool", "Bristol"],
+            "dob": pd.date_range("2020-02-24", periods=4, freq="T"),
+            "Age_sub_Age": [0, 0, 0, 0],
+            "Marks_sub_Age": [-19.1, -20.2, -18.3, -17.4],
+            "Age_sub_Marks": [19.1, 20.2, 18.3, 17.4],
+            "Marks_sub_Marks": [0.0, 0.0, 0.0, 0.0],
+        }
+    )
+
+    pd.testing.assert_frame_equal(X, ref)
diff --git a/tests/test_creation/test_mathematical_combination.py b/tests/test_creation/test_mathematical_combination.py
index 8f8f586..9fd14f1 100644
--- a/tests/test_creation/test_mathematical_combination.py
+++ b/tests/test_creation/test_mathematical_combination.py
@@ -119,6 +119,13 @@ def test_default_parameters(df_vartypes):
     pd.testing.assert_frame_equal(X, ref)
 
 
+def test_error_when_drop_original_not_bool():
+    with pytest.raises(TypeError):
+        MathematicalCombination(
+            variables_to_combine=["Age", "Marks"], drop_original="not_a_bool"
+        )
+
+
 def test_error_when_variables_to_combine_not_numeric(df_vartypes):
     transformer = MathematicalCombination(variables_to_combine=["Name", "Age", "Marks"])
     with pytest.raises(TypeError):
@@ -324,3 +331,25 @@ def test_no_error_when_null_values_in_variable(df_vartypes):
     )
     # transform params
     pd.testing.assert_frame_equal(X, ref)
+
+
+def test_drop_original_variables(df_vartypes):
+    transformer = MathematicalCombination(
+        variables_to_combine=["Age", "Marks"],
+        math_operations=["sum", "mean"],
+        drop_original=True,
+    )
+
+    X = transformer.fit_transform(df_vartypes)
+
+    ref = pd.DataFrame.from_dict(
+        {
+            "Name": ["tom", "nick", "krish", "jack"],
+            "City": ["London", "Manchester", "Liverpool", "Bristol"],
+            "dob": pd.date_range("2020-02-24", periods=4, freq="T"),
+            "sum(Age-Marks)": [20.9, 21.8, 19.7, 18.6],
+            "mean(Age-Marks)": [10.45, 10.9, 9.85, 9.3],
+        }
+    )
+
+    pd.testing.assert_frame_equal(X, ref)
diff --git a/tests/test_datetime/conftest.py b/tests/test_datetime/conftest.py
index 1498a9f..8a49b32 100644
--- a/tests/test_datetime/conftest.py
+++ b/tests/test_datetime/conftest.py
@@ -10,10 +10,10 @@ def df_datetime_transformed(df_datetime):
         "datetime_range_quarter": [1, 1, 1, 1],
         "datetime_range_semester": [1, 1, 1, 1],
         "datetime_range_year": [2020, 2020, 2020, 2020],
-        "datetime_range_woty": [9, 9, 9, 9],
-        "datetime_range_dotw": [0, 1, 2, 3],
-        "datetime_range_dotm": [24, 25, 26, 27],
-        "datetime_range_doty": [55, 56, 57, 58],
+        "datetime_range_week": [9, 9, 9, 9],
+        "datetime_range_day_of_week": [0, 1, 2, 3],
+        "datetime_range_day_of_month": [24, 25, 26, 27],
+        "datetime_range_day_of_year": [55, 56, 57, 58],
         "datetime_range_weekend": [0, 0, 0, 0],
         "datetime_range_month_start": [0, 0, 0, 0],
         "datetime_range_month_end": [0, 0, 0, 0],
@@ -30,10 +30,10 @@ def df_datetime_transformed(df_datetime):
         "date_obj1_quarter": [1, 1, 2, 2],
         "date_obj1_semester": [1, 1, 1, 1],
         "date_obj1_year": [2010, 1945, 2100, 1999],
-        "date_obj1_woty": [53, 8, 24, 20],
-        "date_obj1_dotw": [4, 5, 0, 0],
-        "date_obj1_dotm": [1, 24, 14, 17],
-        "date_obj1_doty": [1, 55, 165, 137],
+        "date_obj1_week": [53, 8, 24, 20],
+        "date_obj1_day_of_week": [4, 5, 0, 0],
+        "date_obj1_day_of_month": [1, 24, 14, 17],
+        "date_obj1_day_of_year": [1, 55, 165, 137],
         "date_obj1_weekend": [0, 1, 0, 0],
         "date_obj1_month_start": [1, 0, 0, 0],
         "date_obj1_month_end": [0, 0, 0, 0],
@@ -50,10 +50,10 @@ def df_datetime_transformed(df_datetime):
         "date_obj2_quarter": [4, 4, 2, 1],
         "date_obj2_semester": [2, 2, 1, 1],
         "date_obj2_year": [2012, 2009, 1995, 2004],
-        "date_obj2_woty": [41, 53, 26, 12],
-        "date_obj2_dotw": [3, 3, 4, 2],
-        "date_obj2_dotm": [11, 31, 30, 17],
-        "date_obj2_doty": [285, 365, 181, 77],
+        "date_obj2_week": [41, 53, 26, 12],
+        "date_obj2_day_of_week": [3, 3, 4, 2],
+        "date_obj2_day_of_month": [11, 31, 30, 17],
+        "date_obj2_day_of_year": [285, 365, 181, 77],
         "date_obj2_weekend": [0, 0, 0, 0],
         "date_obj2_month_start": [0, 0, 0, 0],
         "date_obj2_month_end": [0, 1, 1, 0],
@@ -70,10 +70,10 @@ def df_datetime_transformed(df_datetime):
         "time_obj_quarter": [today.quarter] * 4,
         "time_obj_semester": [1 if today.month <= 6 else 2] * 4,
         "time_obj_year": [today.year] * 4,
-        "time_obj_woty": [today.week] * 4,
-        "time_obj_dotw": [today.dayofweek] * 4,
-        "time_obj_dotm": [today.day] * 4,
-        "time_obj_doty": [today.dayofyear] * 4,
+        "time_obj_week": [today.week] * 4,
+        "time_obj_day_of_week": [today.dayofweek] * 4,
+        "time_obj_day_of_month": [today.day] * 4,
+        "time_obj_day_of_year": [today.dayofyear] * 4,
         "time_obj_weekend": [1 if today.dayofweek > 4 else 0] * 4,
         "time_obj_month_start": [int(today.is_month_start)] * 4,
         "time_obj_month_end": [int(today.is_month_end)] * 4,
diff --git a/tests/test_datetime/test_datetime_features.py b/tests/test_datetime/test_datetime_features.py
index d497a80..97a89c3 100644
--- a/tests/test_datetime/test_datetime_features.py
+++ b/tests/test_datetime/test_datetime_features.py
@@ -226,25 +226,25 @@ def test_extract_all_datetime_features(df_datetime, df_datetime_transformed):
 
 def test_extract_specified_datetime_features(df_datetime, df_datetime_transformed):
     X = DatetimeFeatures(
-        features_to_extract=["semester", "week_of_the_year"]
+        features_to_extract=["semester", "week"]
     ).fit_transform(df_datetime)
     pd.testing.assert_frame_equal(
         X,
         df_datetime_transformed[
             vars_non_dt
-            + [var + "_" + feat for var in vars_dt for feat in ["semester", "woty"]]
+            + [var + "_" + feat for var in vars_dt for feat in ["semester", "week"]]
         ],
     )
 
     # different order than they appear in the glossary
-    X = DatetimeFeatures(features_to_extract=["hour", "day_of_the_week"]).fit_transform(
+    X = DatetimeFeatures(features_to_extract=["hour", "day_of_week"]).fit_transform(
         df_datetime
     )
     pd.testing.assert_frame_equal(
         X,
         df_datetime_transformed[
             vars_non_dt
-            + [var + "_" + feat for var in vars_dt for feat in ["hour", "dotw"]]
+            + [var + "_" + feat for var in vars_dt for feat in ["hour", "day_of_week"]]
         ],
     )
 
@@ -343,7 +343,7 @@ def test_extract_features_without_dropping_original_variables(
 ):
     X = DatetimeFeatures(
         variables=["datetime_range", "date_obj2"],
-        features_to_extract=["week_of_the_year", "quarter"],
+        features_to_extract=["week", "quarter"],
         drop_original=False,
     ).fit_transform(df_datetime)
 
@@ -357,7 +357,7 @@ def test_extract_features_without_dropping_original_variables(
                 for feat in [
                     var + "_" + feat
                     for var in ["datetime_range", "date_obj2"]
-                    for feat in ["woty", "quarter"]
+                    for feat in ["week", "quarter"]
                 ]
             ],
             axis=1,
@@ -377,11 +377,11 @@ def test_extract_features_from_variables_containing_nans():
 
 def test_extract_features_with_different_datetime_parsing_options(df_datetime):
     X = DatetimeFeatures(
-        features_to_extract=["day_of_the_month"], dayfirst=True
+        features_to_extract=["day_of_month"], dayfirst=True
     ).fit_transform(df_datetime[["date_obj2"]])
     pd.testing.assert_frame_equal(
         X,
-        pd.DataFrame({"date_obj2_dotm": [10, 31, 30, 17]}),
+        pd.DataFrame({"date_obj2_day_of_month": [10, 31, 30, 17]}),
     )
 
     X = DatetimeFeatures(features_to_extract=["year"], yearfirst=True).fit_transform(
diff --git a/tests/test_discretisation/test_arbitrary_discretiser.py b/tests/test_discretisation/test_arbitrary_discretiser.py
index f100167..d721878 100644
--- a/tests/test_discretisation/test_arbitrary_discretiser.py
+++ b/tests/test_discretisation/test_arbitrary_discretiser.py
@@ -1,19 +1,28 @@
 import numpy as np
 import pandas as pd
-from sklearn.datasets import load_boston
+import pytest
+from numpy.random import default_rng
+from scipy.stats import skewnorm
+from sklearn.datasets import fetch_california_housing
 
 from feature_engine.discretisation import ArbitraryDiscretiser
 
 
 def test_arbitrary_discretiser():
-    boston_dataset = load_boston()
-    data = pd.DataFrame(boston_dataset.data, columns=boston_dataset.feature_names)
-    user_dict = {"LSTAT": [0, 10, 20, 30, np.Inf]}
+    california_dataset = fetch_california_housing()
+    data = pd.DataFrame(
+        california_dataset.data, columns=california_dataset.feature_names
+    )
+    user_dict = {"HouseAge": [0, 20, 40, 60, np.Inf]}
 
     data_t1 = data.copy()
     data_t2 = data.copy()
-    data_t1["LSTAT"] = pd.cut(data["LSTAT"], bins=[0, 10, 20, 30, np.Inf])
-    data_t2["LSTAT"] = pd.cut(data["LSTAT"], bins=[0, 10, 20, 30, np.Inf], labels=False)
+
+    # HouseAge is the median house age in the block group.
+    data_t1["HouseAge"] = pd.cut(data["HouseAge"], bins=[0, 20, 40, 60, np.Inf])
+    data_t2["HouseAge"] = pd.cut(
+        data["HouseAge"], bins=[0, 20, 40, 60, np.Inf], labels=False
+    )
 
     transformer = ArbitraryDiscretiser(
         binning_dict=user_dict, return_object=False, return_boundaries=False
@@ -24,7 +33,7 @@ def test_arbitrary_discretiser():
     assert transformer.return_object is False
     assert transformer.return_boundaries is False
     # fit params
-    assert transformer.variables_ == ["LSTAT"]
+    assert transformer.variables_ == ["HouseAge"]
     assert transformer.binner_dict_ == user_dict
     # transform params
     pd.testing.assert_frame_equal(X, data_t2)
@@ -34,3 +43,76 @@ def test_arbitrary_discretiser():
     )
     X = transformer.fit_transform(data)
     pd.testing.assert_frame_equal(X, data_t1)
+
+
+def test_error_if_input_df_contains_na_in_transform(df_vartypes, df_na):
+    # test case 1: when dataset contains na, transform method
+    age_dict = {"Age": [0, 10, 20, 30, np.Inf]}
+
+    with pytest.raises(ValueError):
+        transformer = ArbitraryDiscretiser(binning_dict=age_dict)
+        transformer.fit(df_vartypes)
+        transformer.transform(df_na[["Name", "City", "Age", "Marks", "dob"]])
+
+
+def test_error_when_nan_introduced_during_transform():
+    # test error when NA are introduced during the discretisation.
+    rng = default_rng()
+
+    # create dataframe with 2 variables, 1 normal and 1 skewed
+    random = skewnorm.rvs(a=-50, loc=4, size=100)
+    random = random - min(random)  # Shift so the minimum value is equal to zero.
+
+    train = pd.concat(
+        [
+            pd.Series(rng.standard_normal(100)),
+            pd.Series(random),
+        ],
+        axis=1,
+    )
+
+    train.columns = ["var_a", "var_b"]
+
+    # create a dataframe with 2 variables normally distributed
+    test = pd.concat(
+        [
+            pd.Series(rng.standard_normal(100)),
+            pd.Series(rng.standard_normal(100)),
+        ],
+        axis=1,
+    )
+
+    test.columns = ["var_a", "var_b"]
+
+    msg = (
+        "During the discretisation, NaN values were introduced "
+        "in the feature(s) var_b."
+    )
+
+    limits_dict = {"var_a": [-5, -2, 0, 2, 5], "var_b": [0, 2, 5]}
+
+    # check for warning when errors equals 'ignore'
+    with pytest.warns(UserWarning) as record:
+        transformer = ArbitraryDiscretiser(binning_dict=limits_dict, errors="ignore")
+        transformer.fit(train)
+        transformer.transform(test)
+
+    # check that only one warning was returned
+    assert len(record) == 1
+    # check that message matches
+    assert record[0].message.args[0] == msg
+
+    # check for error when errors equals 'raise'
+    with pytest.raises(ValueError) as record:
+        transformer = ArbitraryDiscretiser(binning_dict=limits_dict, errors="raise")
+        transformer.fit(train)
+        transformer.transform(test)
+
+    # check that error message matches
+    assert str(record.value) == msg
+
+
+def test_error_if_not_permitted_value_is_errors():
+    age_dict = {"Age": [0, 10, 20, 30, np.Inf]}
+    with pytest.raises(ValueError):
+        ArbitraryDiscretiser(binning_dict=age_dict, errors="medialuna")
diff --git a/tests/test_encoding/check_estimator_encoders.py b/tests/test_encoding/test_check_estimator_encoders.py
similarity index 100%
rename from tests/test_encoding/check_estimator_encoders.py
rename to tests/test_encoding/test_check_estimator_encoders.py
diff --git a/tests/test_encoding/test_count_frequency_encoder.py b/tests/test_encoding/test_count_frequency_encoder.py
index e8ef049..cb101ae 100644
--- a/tests/test_encoding/test_count_frequency_encoder.py
+++ b/tests/test_encoding/test_count_frequency_encoder.py
@@ -122,11 +122,30 @@ def test_error_if_input_df_contains_categories_not_present_in_fit_df(
 ):
     # test case 3: when dataset to be transformed contains categories not present in
     # training dataset
-    with pytest.warns(UserWarning):
-        encoder = CountFrequencyEncoder()
+
+    msg = "During the encoding, NaN values were introduced in the feature(s) var_A."
+
+    # check for warning when rare_labels equals 'ignore'
+    with pytest.warns(UserWarning) as record:
+        encoder = CountFrequencyEncoder(errors="ignore")
+        encoder.fit(df_enc)
+        encoder.transform(df_enc_rare)
+
+    # check that only one warning was raised
+    assert len(record) == 1
+    # check that the message matches
+    assert record[0].message.args[0] == msg
+
+    # check for error when rare_labels equals 'raise'
+    with pytest.raises(ValueError) as record:
+        encoder = CountFrequencyEncoder(errors="raise")
+
         encoder.fit(df_enc)
         encoder.transform(df_enc_rare)
 
+    # check that the error message matches
+    assert str(record.value) == msg
+
 
 def test_fit_raises_error_if_df_contains_na(df_enc_na):
     # test case 4: when dataset contains na, fit method
@@ -238,3 +257,8 @@ def test_variables_cast_as_category(df_enc_category_dtypes):
     encoder = CountFrequencyEncoder(encoding_method="frequency", variables=["var_A"])
     X = encoder.fit_transform(df_enc_category_dtypes)
     assert X["var_A"].dtypes == float
+
+
+def test_error_if_rare_labels_not_permitted_value():
+    with pytest.raises(ValueError):
+        CountFrequencyEncoder(errors="empanada")
diff --git a/tests/test_encoding/test_mean_encoder.py b/tests/test_encoding/test_mean_encoder.py
index 706997a..144883d 100644
--- a/tests/test_encoding/test_mean_encoder.py
+++ b/tests/test_encoding/test_mean_encoder.py
@@ -126,11 +126,29 @@ def test_warning_if_transform_df_contains_categories_not_present_in_fit_df(
 ):
     # test case 4: when dataset to be transformed contains categories not present
     # in training dataset
-    with pytest.warns(UserWarning):
-        encoder = MeanEncoder()
+
+    msg = "During the encoding, NaN values were introduced in the feature(s) var_A."
+
+    # check for warning when rare_labels equals 'ignore'
+    with pytest.warns(UserWarning) as record:
+        encoder = MeanEncoder(errors="ignore")
         encoder.fit(df_enc[["var_A", "var_B"]], df_enc["target"])
         encoder.transform(df_enc_rare[["var_A", "var_B"]])
 
+    # check that only one warning was raised
+    assert len(record) == 1
+    # check that the message matches
+    assert record[0].message.args[0] == msg
+
+    # check for error when rare_labels equals 'raise'
+    with pytest.raises(ValueError) as record:
+        encoder = MeanEncoder(errors="raise")
+        encoder.fit(df_enc[["var_A", "var_B"]], df_enc["target"])
+        encoder.transform(df_enc_rare[["var_A", "var_B"]])
+
+    # check that the error message matches
+    assert str(record.value) == msg
+
 
 def test_fit_raises_error_if_df_contains_na(df_enc_na):
     # test case 4: when dataset contains na, fit method
@@ -293,3 +311,8 @@ def test_variables_cast_as_category(df_enc_category_dtypes):
 
     pd.testing.assert_frame_equal(X, transf_df[["var_A", "var_B"]], check_dtype=False)
     assert X["var_A"].dtypes == float
+
+
+def test_error_if_rare_labels_not_permitted_value():
+    with pytest.raises(ValueError):
+        MeanEncoder(errors="empanada")
diff --git a/tests/test_encoding/test_ordinal_encoder.py b/tests/test_encoding/test_ordinal_encoder.py
index c1ab2e7..394cbb9 100644
--- a/tests/test_encoding/test_ordinal_encoder.py
+++ b/tests/test_encoding/test_ordinal_encoder.py
@@ -67,10 +67,27 @@ def test_error_if_input_df_contains_categories_not_present_in_training_df(
 ):
     # test case 4: when dataset to be transformed contains categories not present
     # in training dataset
-    with pytest.warns(UserWarning):
-        encoder = OrdinalEncoder(encoding_method="arbitrary")
-        encoder.fit(df_enc)
-        encoder.transform(df_enc_rare)
+    msg = "During the encoding, NaN values were introduced in the feature(s) var_A."
+
+    # check for warning when rare_labels equals 'ignore'
+    with pytest.warns(UserWarning) as record:
+        encoder = OrdinalEncoder(errors="ignore")
+        encoder.fit(df_enc[["var_A", "var_B"]], df_enc["target"])
+        encoder.transform(df_enc_rare[["var_A", "var_B"]])
+
+    # check that only one warning was raised
+    assert len(record) == 1
+    # check that the message matches
+    assert record[0].message.args[0] == msg
+
+    # check for error when rare_labels equals 'raise'
+    with pytest.raises(ValueError) as record:
+        encoder = OrdinalEncoder(errors="raise")
+        encoder.fit(df_enc[["var_A", "var_B"]], df_enc["target"])
+        encoder.transform(df_enc_rare[["var_A", "var_B"]])
+
+    # check that the error message matches
+    assert str(record.value) == msg
 
 
 def test_non_fitted_error(df_enc):
@@ -156,3 +173,8 @@ def test_variables_cast_as_category(df_enc_category_dtypes):
     # test transform output
     pd.testing.assert_frame_equal(X, transf_df[["var_A", "var_B"]], check_dtype=False)
     assert X["var_A"].dtypes == int
+
+
+def test_error_if_rare_labels_not_permitted_value():
+    with pytest.raises(ValueError):
+        OrdinalEncoder(errors="empanada")
diff --git a/tests/test_encoding/test_probability_ratio_encoder.py b/tests/test_encoding/test_probability_ratio_encoder.py
index fb98fa2..bee9bec 100644
--- a/tests/test_encoding/test_probability_ratio_encoder.py
+++ b/tests/test_encoding/test_probability_ratio_encoder.py
@@ -253,3 +253,34 @@ def test_logratio_on_numerical_variables(df_enc_numeric):
     # test error raise
     with pytest.raises(ValueError):
         PRatioEncoder(encoding_method="other")
+
+
+def test_warn_if_transform_df_contains_categories_not_seen_in_fit(df_enc, df_enc_rare):
+    # test case 3: when dataset to be transformed contains categories not present
+    # in training dataset
+    msg = "During the encoding, NaN values were introduced in the feature(s) var_A."
+
+    # check for warning when rare_labels equals 'ignore'
+    with pytest.warns(UserWarning) as record:
+        encoder = PRatioEncoder(errors="ignore")
+        encoder.fit(df_enc[["var_A", "var_B"]], df_enc["target"])
+        encoder.transform(df_enc_rare[["var_A", "var_B"]])
+
+    # check that only one warning was raised
+    assert len(record) == 1
+    # check that the message matches
+    assert record[0].message.args[0] == msg
+
+    # check for error when rare_labels equals 'raise'
+    with pytest.raises(ValueError) as record:
+        encoder = PRatioEncoder(errors="raise")
+        encoder.fit(df_enc[["var_A", "var_B"]], df_enc["target"])
+        encoder.transform(df_enc_rare[["var_A", "var_B"]])
+
+    # check that the error message matches
+    assert str(record.value) == msg
+
+
+def test_error_if_rare_labels_not_permitted_value():
+    with pytest.raises(ValueError):
+        PRatioEncoder(errors="empanada")
diff --git a/tests/test_encoding/test_woe_encoder.py b/tests/test_encoding/test_woe_encoder.py
index 535dfc2..d2df729 100644
--- a/tests/test_encoding/test_woe_encoder.py
+++ b/tests/test_encoding/test_woe_encoder.py
@@ -90,11 +90,28 @@ def test_error_target_is_not_passed(df_enc):
 def test_warn_if_transform_df_contains_categories_not_seen_in_fit(df_enc, df_enc_rare):
     # test case 3: when dataset to be transformed contains categories not present
     # in training dataset
-    encoder = WoEEncoder(variables=None)
-    with pytest.warns(UserWarning):
+    msg = "During the encoding, NaN values were introduced in the feature(s) var_A."
+
+    # check for error when rare_labels equals 'raise'
+    with pytest.warns(UserWarning) as record:
+        encoder = WoEEncoder(errors="ignore")
+        encoder.fit(df_enc[["var_A", "var_B"]], df_enc["target"])
+        encoder.transform(df_enc_rare[["var_A", "var_B"]])
+
+    # check that only one warning was raised
+    assert len(record) == 1
+    # check that the message matches
+    assert record[0].message.args[0] == msg
+
+    # check for error when rare_labels equals 'raise'
+    with pytest.raises(ValueError) as record:
+        encoder = WoEEncoder(errors="raise")
         encoder.fit(df_enc[["var_A", "var_B"]], df_enc["target"])
         encoder.transform(df_enc_rare[["var_A", "var_B"]])
 
+    # check that the error message matches
+    assert str(record.value) == msg
+
 
 def test_error_if_target_not_binary():
     # test case 4: the target is not binary
@@ -292,3 +309,8 @@ def test_variables_cast_as_category(df_enc_category_dtypes):
 
     pd.testing.assert_frame_equal(X, transf_df[["var_A", "var_B"]], check_dtype=False)
     assert X["var_A"].dtypes == float
+
+
+def test_error_if_rare_labels_not_permitted_value():
+    with pytest.raises(ValueError):
+        WoEEncoder(errors="empanada")