Repository: feature-engine/feature_engine
Pull request #544 (2022-10-20 03:26:20)

dataset module
Hi @solegalli.
This is my first try on the `load_titanic` function. I just created a function trying to emulate sklearn datasets API.

* I created a `return_X_y_frame`: Scikit-learn allows to return Dataframes or Numpy arrays, but I don´t think that works for feature-engine. I think it should always return a DataFrame/Series object, but one whole Dataset if `False` or X and y if `True`.
* I created a `raw=False`: raw means as is... With nulls as `?`. If True it returns proper `np.nan`.
* `predictors_only=False`: I'm not quite convinced about the name of this parameter. If `False` it returns all the features. If `True` it removes features that normally do not help for predictions such as: `name`,`ticket`, `home.dest`, `boat`, `body` and `cabin`. Normally these features have a high number of nulls, except for `name`.

Also the name of the python file is data.py. Not sure if it's ok, or probably another name should be given.

I created also initial set of tests to check these functionalities. Please provide some feedback especially in the naming convention.

Looking forward to your response,

Alfonso

codecov[bot]: # [Codecov](https://codecov.io/gh/feature-engine/feature_engine/pull/544?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=feature-engine) Report
> Merging [#544](https://codecov.io/gh/feature-engine/feature_engine/pull/544?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=feature-engine) (004e16e) into [main](https://codecov.io/gh/feature-engine/feature_engine/commit/50cecc54183d2282817783324cbed41bc6729a30?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=feature-engine) (50cecc5) will **increase** coverage by `0.01%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##             main     #544      +/-   ##
==========================================
+ Coverage   97.47%   97.48%   +0.01%     
==========================================
  Files          90       92       +2     
  Lines        3480     3495      +15     
  Branches      681      684       +3     
==========================================
+ Hits         3392     3407      +15     
  Misses         32       32              
  Partials       56       56              
```


| [Impacted Files](https://codecov.io/gh/feature-engine/feature_engine/pull/544?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=feature-engine) | Coverage Δ | |
|---|---|---|
| [feature\_engine/datasets/\_\_init\_\_.py](https://codecov.io/gh/feature-engine/feature_engine/pull/544/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=feature-engine#diff-ZmVhdHVyZV9lbmdpbmUvZGF0YXNldHMvX19pbml0X18ucHk=) | `100.00% <100.00%> (ø)` | |
| [feature\_engine/datasets/data.py](https://codecov.io/gh/feature-engine/feature_engine/pull/544/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=feature-engine#diff-ZmVhdHVyZV9lbmdpbmUvZGF0YXNldHMvZGF0YS5weQ==) | `100.00% <100.00%> (ø)` | |

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=feature-engine)

solegalli: Hi @datacubeR 

This looks good to go to me. I'd change the file name from `data.py` to `titanic.py`, because we'll probably add more data loading files.

Then, the only bit remaining would be to go over all the user_guide files, check which ones use the titanic dataset, and change the loading function. Would you be up for it?

Thank you!
datacubeR: @solegalli I made the requested changes. On fixing the User guide, they are a lot of changes. After checking some examples like this:

![image](https://user-images.githubusercontent.com/48638337/199159039-0f12d5d2-be2c-40a3-9992-9de44a3d9039.png)

There are several references to features like `cabin`, that we ended up removing from predictors. Is it OK to fix the examples in this same PR? or should I raise different ones for every module, after merging this one?
solegalli: Hi @datacubeR 

Great thank you! I think it would be best to make all changes related to the titanic dataset in this PR :)
datacubeR: Hi @solegalli,

I'll be adding updates for this PR during this week. So far I just modified Categorical encoders in the user guide. As soon as I have more updates I'll let you know...

solegalli: Awesome, thank you!
solegalli: Hey @datacubeR 

How are you doing? I've read you are doing well over Linkedin. I am happy to hear that.

Could you give me a shout when this PR is ready? So that I don't miss it like I did with the selection module?

Any chance we could have this ready by the end of the week? I'd like to release a new version of feature-engine, and want to check if we should wait for this PR or we leave it for the next release.

Thank you!
datacubeR: Hi @solegalli 
Just wanted to let you know that it seems I almost finished the PR.

I have a little issue with the test_docs tests. Not sure why Python code is failing. I have checked the same line the test is pointing as failing and it works. Would you help me with that?

Regarding the dataset module. I added one more test and made some modifications to ensure dtypes for some variables. Working on the examples I noticed sometimes incorrect data types were inferred so I preferred to force it to the correct one. 

Please tell me how it looks to you and let me know if I need to make any modifications, specially for the failing test.

Kind Regards,

Alfonso
solegalli: Hey @datacubeR 

Thank you so much for the incredible amount of work on this module.

I made a PR to your repo with further changes: https://github.com/datacubeR/feature_engine/pull/2

Could you have a look and merge so we can close this PR?

Thanks a lot!
datacubeR: @solegalli 
Really nice changes.
I should have come up with something like this seeing a lot of the cabin letter was being used.

Thanks for the feedback and hope this module could be useful for the release.

Thanks, Alfonso 

Patch:
diff --git a/docs/api_doc/datasets/index.rst b/docs/api_doc/datasets/index.rst
new file mode 100644
index 0000000..e1b38e1
--- /dev/null
+++ b/docs/api_doc/datasets/index.rst
@@ -0,0 +1,15 @@
+.. -*- mode: rst -*-
+
+Datasets
+========
+
+We are starting to build a library of functions that allow you and us to quickly load
+datasets to demonstrate and test the functionality of Feature-engine (and, why not,
+other Python libraries).
+
+At the moment, we support the following functions:
+
+.. toctree::
+   :maxdepth: 1
+
+   titanic
diff --git a/docs/api_doc/datasets/titanic.rst b/docs/api_doc/datasets/titanic.rst
new file mode 100644
index 0000000..98910c0
--- /dev/null
+++ b/docs/api_doc/datasets/titanic.rst
@@ -0,0 +1,6 @@
+﻿load__titanic
+=============
+
+.. currentmodule:: feature_engine.datasets
+
+.. autofunction:: load_titanic
\ No newline at end of file
diff --git a/docs/api_doc/index.rst b/docs/api_doc/index.rst
index fce823e..ff0ed73 100644
--- a/docs/api_doc/index.rst
+++ b/docs/api_doc/index.rst
@@ -42,7 +42,6 @@ Time series
 
    timeseries/index
 
-
 Other
 -----
 .. toctree::
@@ -51,6 +50,12 @@ Other
    preprocessing/index
    wrappers/index
 
+Datasets
+--------
+.. toctree::
+   :maxdepth: 1
+
+   datasets/index
 
 Tools
 -----
diff --git a/docs/user_guide/encoding/CountFrequencyEncoder.rst b/docs/user_guide/encoding/CountFrequencyEncoder.rst
index 8f6fbfd..146e21b 100644
--- a/docs/user_guide/encoding/CountFrequencyEncoder.rst
+++ b/docs/user_guide/encoding/CountFrequencyEncoder.rst
@@ -16,41 +16,46 @@ First, let's load the data and separate it into train and test:
 
 .. 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.encoding import CountFrequencyEncoder
-
-	# Load dataset
-	def load_titanic():
-		data = pd.read_csv('https://www.openml.org/data/get_csv/16826755/phpMYEkMl')
-		data = data.replace('?', np.nan)
-		data['cabin'] = data['cabin'].astype(str).str[0]
-		data['pclass'] = data['pclass'].astype('O')
-		data['embarked'].fillna('C', inplace=True)
-		return data
-	
-	data = load_titanic()
-
-	# Separate into train and test sets
-	X_train, X_test, y_train, y_test = train_test_split(
-			data.drop(['survived', 'name', 'ticket'], axis=1),
-			data['survived'], test_size=0.3, random_state=0)
+    from sklearn.model_selection import train_test_split
+    from feature_engine.datasets import load_titanic
+    from feature_engine.encoding import CountFrequencyEncoder
 
+    X, y = load_titanic(
+        return_X_y_frame=True,
+        handle_missing=True,
+        predictors_only=True,
+        cabin="letter_only",
+    )
+
+    X_train, X_test, y_train, y_test = train_test_split(
+        X, y, test_size=0.3, random_state=0,
+    )
+
+    print(X_train.head())
+
+We see the resulting data below:
+
+.. code:: python
+
+          pclass     sex        age  sibsp  parch     fare cabin embarked
+    501        2  female  13.000000      0      1  19.5000     M        S
+    588        2  female   4.000000      1      1  23.0000     M        S
+    402        2  female  30.000000      1      0  13.8583     M        C
+    1193       3    male  29.881135      0      0   7.7250     M        Q
+    686        3  female  22.000000      0      0   7.7250     M        Q
 
 Now, we set up the :class:`CountFrequencyEncoder()` to replace the categories by their
 frequencies, only in the 3 indicated variables:
 
 .. code:: python
 
-	# set up the encoder
-	encoder = CountFrequencyEncoder(encoding_method='frequency',
-				 variables=['cabin', 'pclass', 'embarked'])
+    encoder = CountFrequencyEncoder(
+    encoding_method='frequency',
+    variables=['cabin', 'pclass', 'embarked'],
+    ignore_format=True,
+    )
 
-	# fit the encoder
-	encoder.fit(X_train)
+    encoder.fit(X_train)
 
 With `fit()` the encoder learns the frequencies of each category, which are stored in
 its `encoder_dict_` parameter:
@@ -65,30 +70,42 @@ value.
 
 .. code:: python
 
-	{'cabin': {'n': 0.7663755458515283,
-	  'C': 0.07751091703056769,
-	  'B': 0.04585152838427948,
-	  'E': 0.034934497816593885,
-	  'D': 0.034934497816593885,
-	  'A': 0.018558951965065504,
-	  'F': 0.016375545851528384,
-	  'G': 0.004366812227074236,
-	  'T': 0.001091703056768559},
-	 'pclass': {3: 0.5436681222707423,
-	  1: 0.25109170305676853,
-	  2: 0.2052401746724891},
-	 'embarked': {'S': 0.7117903930131004,
-	  'C': 0.19759825327510916,
-	  'Q': 0.0906113537117904}}
+	{'cabin': {'M': 0.7663755458515283,
+		'C': 0.07751091703056769,
+		'B': 0.04585152838427948,
+		'E': 0.034934497816593885,
+		'D': 0.034934497816593885,
+		'A': 0.018558951965065504,
+		'F': 0.016375545851528384,
+		'G': 0.004366812227074236,
+		'T': 0.001091703056768559},
+	'pclass': {3: 0.5436681222707423,
+		1: 0.25109170305676853,
+		2: 0.2052401746724891},
+	'embarked': {'S': 0.7117903930131004,
+		'C': 0.19541484716157206,
+		'Q': 0.0906113537117904,
+		'Missing': 0.002183406113537118}}
 
 We can now go ahead and replace the original strings with the numbers:
 
 .. code:: python
 
-	# transform the data
-	train_t= encoder.transform(X_train)
-	test_t= encoder.transform(X_test)
+    train_t = encoder.transform(X_train)
+    test_t = encoder.transform(X_test)
+
+    print(train_t.head())
+
+Below we see the that the original variables were replaced with the frequencies:
+
+.. code:: python
 
+            pclass     sex        age  sibsp  parch     fare     cabin  embarked
+    501   0.205240  female  13.000000      0      1  19.5000  0.766376  0.711790
+    588   0.205240  female   4.000000      1      1  23.0000  0.766376  0.711790
+    402   0.205240  female  30.000000      1      0  13.8583  0.766376  0.195415
+    1193  0.543668    male  29.881135      0      0   7.7250  0.766376  0.090611
+    686   0.543668  female  22.000000      0      0   7.7250  0.766376  0.090611
 
 More details
 ^^^^^^^^^^^^
diff --git a/docs/user_guide/encoding/DecisionTreeEncoder.rst b/docs/user_guide/encoding/DecisionTreeEncoder.rst
index 91c06a3..9c2d3c6 100644
--- a/docs/user_guide/encoding/DecisionTreeEncoder.rst
+++ b/docs/user_guide/encoding/DecisionTreeEncoder.rst
@@ -28,46 +28,38 @@ First, let's load the data and separate it into train and test:
 
 .. 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.datasets import load_titanic
     from feature_engine.encoding import DecisionTreeEncoder
 
-    # Load dataset
-    def load_titanic():
-            data = pd.read_csv('https://www.openml.org/data/get_csv/16826755/phpMYEkMl')
-            data = data.replace('?', np.nan)
-            data['cabin'] = data['cabin'].astype(str).str[0]
-            data['pclass'] = data['pclass'].astype('O')
-            data['embarked'].fillna('C', inplace=True)
-            return data
-
-    data = load_titanic()
+    X, y = load_titanic(
+        return_X_y_frame=True,
+        handle_missing=True,
+        predictors_only=True,
+        cabin="letter_only",
+    )
 
-    # Separate into train and test sets
     X_train, X_test, y_train, y_test = train_test_split(
-                    data.drop(['survived', 'name', 'ticket'], axis=1),
-                    data['survived'], test_size=0.3, random_state=0)
+        X, y, test_size=0.3, random_state=0,
+    )
 
-    X_train[['cabin', 'pclass', 'embarked']].head(10)
+    print(X_train[['cabin', 'pclass', 'embarked']].head(10))
 
 We will encode the following categorical variables:
 
 .. code:: python
 
-         cabin pclass embarked
-   501      n      2        S
-   588      n      2        S
-   402      n      2        C
-   1193     n      3        Q
-   686      n      3        Q
-   971      n      3        Q
-   117      E      1        C
-   540      n      2        S
-   294      C      1        C
-   261      E      1        S
+        cabin  pclass embarked
+    501      M       2        S
+    588      M       2        S
+    402      M       2        C
+    1193     M       3        Q
+    686      M       3        Q
+    971      M       3        Q
+    117      E       1        C
+    540      M       2        S
+    294      C       1        C
+    261      E       1        S
 
 We set up the encoder to encode the variables above with 3 fold cross-validation, using
 a grid search to find the optimal depth of the decision tree (this is the default
@@ -76,15 +68,14 @@ tree using the roc-auc metric.
 
 .. code:: python
 
-    # set up the encoder
     encoder = DecisionTreeEncoder(
-         variables=['cabin', 'pclass', 'embarked'],
-         regression=False,
-         scoring='roc_auc',
-         cv=3,
-         random_state=0)
+        variables=['cabin', 'pclass', 'embarked'],
+        regression=False,
+        scoring='roc_auc',
+        cv=3,
+        random_state=0,
+        ignore_format=True)
 
-    # fit the encoder
     encoder.fit(X_train, y_train)
 
 With `fit()` the :class:`DecisionTreeEncoder()` fits 1 decision tree per variable. Now we can go ahead and
@@ -92,7 +83,6 @@ transform the categorical variables into numbers, using the predictions of these
 
 .. code:: python
 
-    # transform the data
     train_t = encoder.transform(X_train)
     test_t = encoder.transform(X_test)
 
@@ -102,16 +92,16 @@ We can see the encoded variables below:
 
 .. code:: python
 
-             cabin    pclass  embarked
+            cabin    pclass  embarked
     501   0.304843  0.436170  0.338957
     588   0.304843  0.436170  0.338957
-    402   0.304843  0.436170  0.558011
+    402   0.304843  0.436170  0.553073
     1193  0.304843  0.259036  0.373494
     686   0.304843  0.259036  0.373494
     971   0.304843  0.259036  0.373494
-    117   0.611650  0.617391  0.558011
+    117   0.611650  0.617391  0.553073
     540   0.304843  0.436170  0.338957
-    294   0.611650  0.617391  0.558011
+    294   0.611650  0.617391  0.553073
     261   0.611650  0.617391  0.338957
 
 
diff --git a/docs/user_guide/encoding/MeanEncoder.rst b/docs/user_guide/encoding/MeanEncoder.rst
index bcd0592..0b01133 100644
--- a/docs/user_guide/encoding/MeanEncoder.rst
+++ b/docs/user_guide/encoding/MeanEncoder.rst
@@ -20,39 +20,45 @@ First, let's load the data and separate it into train and test:
 
 .. 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.encoding import MeanEncoder
-
-	# Load dataset
-	def load_titanic():
-		data = pd.read_csv('https://www.openml.org/data/get_csv/16826755/phpMYEkMl')
-		data = data.replace('?', np.nan)
-		data['cabin'] = data['cabin'].astype(str).str[0]
-		data['pclass'] = data['pclass'].astype('O')
-		data['embarked'].fillna('C', inplace=True)
-		return data
-	
-	data = load_titanic()
-
-	# Separate into train and test sets
-	X_train, X_test, y_train, y_test = train_test_split(
-			data.drop(['survived', 'name', 'ticket'], axis=1),
-			data['survived'], test_size=0.3, random_state=0)
+    from sklearn.model_selection import train_test_split
+    from feature_engine.datasets import load_titanic
+    from feature_engine.encoding import MeanEncoder
+
+    X, y = load_titanic(
+        return_X_y_frame=True,
+        handle_missing=True,
+        predictors_only=True,
+        cabin="letter_only",
+    )
+
+    X_train, X_test, y_train, y_test = train_test_split(
+        X, y, test_size=0.3, random_state=0,
+    )
+
+    print(X_train.head())
+
+We see the resulting data below:
+
+.. code:: python
+
+          pclass     sex        age  sibsp  parch     fare cabin embarked
+    501        2  female  13.000000      0      1  19.5000     M        S
+    588        2  female   4.000000      1      1  23.0000     M        S
+    402        2  female  30.000000      1      0  13.8583     M        C
+    1193       3    male  29.881135      0      0   7.7250     M        Q
+    686        3  female  22.000000      0      0   7.7250     M        Q
 
 Now, we set up the :class:`MeanEncoder()` to replace the categories only in the 3
 indicated variables:
 
 .. code:: python
 
-	# set up the encoder
-	encoder = MeanEncoder(variables=['cabin', 'pclass', 'embarked'])
+    encoder = MeanEncoder(
+        variables=['cabin', 'pclass', 'embarked'],
+        ignore_format=True,
+    )
 
-	# fit the encoder
-	encoder.fit(X_train, y_train)
+    encoder.fit(X_train, y_train)
 
 With `fit()` the encoder learns the target mean value for each category, which are stored in
 its `encoder_dict_` parameter:
@@ -67,29 +73,42 @@ So we can easily use this dictionary to map the numbers to the original labels.
 .. code:: python
 
 	{'cabin': {'A': 0.5294117647058824,
-	  'B': 0.7619047619047619,
-	  'C': 0.5633802816901409,
-	  'D': 0.71875,
-	  'E': 0.71875,
-	  'F': 0.6666666666666666,
-	  'G': 0.5,
-	  'T': 0.0,
-	  'n': 0.30484330484330485},
-	 'pclass': {1: 0.6173913043478261,
-	  2: 0.43617021276595747,
-	  3: 0.25903614457831325},
-	 'embarked': {'C': 0.5580110497237569,
-	  'Q': 0.37349397590361444,
-	  'S': 0.3389570552147239}}
+		'B': 0.7619047619047619,
+		'C': 0.5633802816901409,
+		'D': 0.71875,
+		'E': 0.71875,
+		'F': 0.6666666666666666,
+		'G': 0.5,
+		'M': 0.30484330484330485,
+		'T': 0.0},
+	'pclass': {1: 0.6173913043478261,
+		2: 0.43617021276595747,
+		3: 0.25903614457831325},
+	'embarked': {'C': 0.553072625698324,
+		'Missing': 1.0,
+		'Q': 0.37349397590361444,
+		'S': 0.3389570552147239}}
 
 We can now go ahead and replace the original strings with the numbers:
 
 .. code:: python
 
-	# transform the data
-	train_t= encoder.transform(X_train)
-	test_t= encoder.transform(X_test)
+    train_t = encoder.transform(X_train)
+    test_t = encoder.transform(X_test)
+
+    print(train_t.head())
+
+Below we see the resulting dataframe, where the original variable values are now replaced
+with the target mean:
+
+.. code:: python
 
+            pclass     sex        age  sibsp  parch     fare     cabin  embarked
+    501   0.436170  female  13.000000      0      1  19.5000  0.304843  0.338957
+    588   0.436170  female   4.000000      1      1  23.0000  0.304843  0.338957
+    402   0.436170  female  30.000000      1      0  13.8583  0.304843  0.553073
+    1193  0.259036    male  29.881135      0      0   7.7250  0.304843  0.373494
+    686   0.259036  female  22.000000      0      0   7.7250  0.304843  0.373494
 
 Handling Cardinality
 ^^^^^^^^^^^^^^^^^^^^
diff --git a/docs/user_guide/encoding/OneHotEncoder.rst b/docs/user_guide/encoding/OneHotEncoder.rst
index 8d16532..0e712d3 100644
--- a/docs/user_guide/encoding/OneHotEncoder.rst
+++ b/docs/user_guide/encoding/OneHotEncoder.rst
@@ -68,39 +68,47 @@ into a train and a test set:
 
 .. 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.encoding import OneHotEncoder
-
-	# Load dataset
-	def load_titanic():
-		data = pd.read_csv('https://www.openml.org/data/get_csv/16826755/phpMYEkMl')
-		data = data.replace('?', np.nan)
-		data['cabin'] = data['cabin'].astype(str).str[0]
-		data['pclass'] = data['pclass'].astype('O')
-		data['embarked'].fillna('C', inplace=True)
-		return data
-	
-	data = load_titanic()
-
-	# Separate into train and test sets
-	X_train, X_test, y_train, y_test = train_test_split(
-				data.drop(['survived', 'name', 'ticket'], axis=1),
-				data['survived'], test_size=0.3, random_state=0)
-
-Now, we set up the encoder to encode only the 2 most frequent categories of each of the
-3 indicated categorical variables:
+    from sklearn.model_selection import train_test_split
+    from feature_engine.datasets import load_titanic
+    from feature_engine.encoding import OneHotEncoder
+
+    X, y = load_titanic(
+        return_X_y_frame=True,
+        handle_missing=True,
+        predictors_only=True,
+        cabin="letter_only",
+    )
+
+    X_train, X_test, y_train, y_test = train_test_split(
+        X, y, test_size=0.3, random_state=0,
+    )
+
+    print(X_train.head())
+
+We see the resulting data below:
+
+.. code:: python
+
+          pclass     sex        age  sibsp  parch     fare cabin embarked
+    501        2  female  13.000000      0      1  19.5000     M        S
+    588        2  female   4.000000      1      1  23.0000     M        S
+    402        2  female  30.000000      1      0  13.8583     M        C
+    1193       3    male  29.881135      0      0   7.7250     M        Q
+    686        3  female  22.000000      0      0   7.7250     M        Q
+
+Now, we set up the encoder to encode only the 2 most frequent categories of 3
+categorical variables:
 
 .. code:: python
 
-	# set up the encoder
-	encoder = OneHotEncoder(top_categories=2, variables=['pclass', 'cabin', 'embarked'])
+    encoder = OneHotEncoder(
+        top_categories=2,
+        variables=['pclass', 'cabin', 'embarked'],
+        ignore_format=True,
+        )
 
-	# fit the encoder
-	encoder.fit(X_train)
+    # fit the encoder
+    encoder.fit(X_train)
 
 With `fit()` the encoder will learn the most popular categories of the variables, which
 are stored in the attribute `encoder_dict_`.
@@ -111,7 +119,7 @@ are stored in the attribute `encoder_dict_`.
 
 .. code:: python
 
-	{'pclass': [3, 1], 'cabin': ['n', 'C'], 'embarked': ['S', 'C']}
+	{'pclass': [3, 1], 'cabin': ['M', 'C'], 'embarked': ['S', 'C']}
 
 The `encoder_dict_` contains the categories that will derive dummy variables for each
 categorical variable.
@@ -121,12 +129,32 @@ With transform, we go ahead and encode the variables. Note that by default, the
 
 .. code:: python
 
-	# transform the data
-	train_t= encoder.transform(X_train)
-	test_t= encoder.transform(X_test)
+    train_t = encoder.transform(X_train)
+    test_t = encoder.transform(X_test)
+
+    print(train_t.head())
+
+Below we see the one hot dummy variables added to the dataset, and the original
+variables were removed:
+
+.. code:: python
+
+             sex        age  sibsp  parch     fare  pclass_3  pclass_1  cabin_M  \
+    501   female  13.000000      0      1  19.5000         0         0        1
+    588   female   4.000000      1      1  23.0000         0         0        1
+    402   female  30.000000      1      0  13.8583         0         0        1
+    1193    male  29.881135      0      0   7.7250         1         0        1
+    686   female  22.000000      0      0   7.7250         1         0        1
+
+          cabin_C  embarked_S  embarked_C
+    501         0           1           0
+    588         0           1           0
+    402         0           0           1
+    1193        0           0           0
+    686         0           0           0
 
 If you do not want to drop the original variables, consider using the OneHotEncoder
-from Scikit-learn and wrap it with the :ref:`SklearnTransformerWrapper <sklearn_wrapper>`.
+from Scikit-learn.
 
 **Feature space and duplication**
 
diff --git a/docs/user_guide/encoding/OrdinalEncoder.rst b/docs/user_guide/encoding/OrdinalEncoder.rst
index cdc8299..e93cc97 100644
--- a/docs/user_guide/encoding/OrdinalEncoder.rst
+++ b/docs/user_guide/encoding/OrdinalEncoder.rst
@@ -33,28 +33,33 @@ First, let's load the data and separate it into train and test:
 
 .. 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.encoding import OrdinalEncoder
-
-	# Load dataset
-	def load_titanic():
-		data = pd.read_csv('https://www.openml.org/data/get_csv/16826755/phpMYEkMl')
-		data = data.replace('?', np.nan)
-		data['cabin'] = data['cabin'].astype(str).str[0]
-		data['pclass'] = data['pclass'].astype('O')
-		data['embarked'].fillna('C', inplace=True)
-		return data
-	
-	data = load_titanic()
-
-	# Separate into train and test sets
-	X_train, X_test, y_train, y_test = train_test_split(
-			data.drop(['survived', 'name', 'ticket'], axis=1),
-			data['survived'], test_size=0.3, random_state=0)
+    from sklearn.model_selection import train_test_split
+    from feature_engine.datasets import load_titanic
+    from feature_engine.encoding import OrdinalEncoder
+
+    X, y = load_titanic(
+        return_X_y_frame=True,
+        handle_missing=True,
+        predictors_only=True,
+        cabin="letter_only",
+    )
+
+    X_train, X_test, y_train, y_test = train_test_split(
+        X, y, test_size=0.3, random_state=0,
+    )
+
+    print(X_train.head())
+
+We see the resulting data below:
+
+.. code:: python
+
+          pclass     sex        age  sibsp  parch     fare cabin embarked
+    501        2  female  13.000000      0      1  19.5000     M        S
+    588        2  female   4.000000      1      1  23.0000     M        S
+    402        2  female  30.000000      1      0  13.8583     M        C
+    1193       3    male  29.881135      0      0   7.7250     M        Q
+    686        3  female  22.000000      0      0   7.7250     M        Q
 
 
 Now, we set up the :class:`OrdinalEncoder()` to replace the categories by strings based
@@ -62,11 +67,12 @@ on the target mean value and only in the 3 indicated variables:
 
 .. code:: python
 
-	# set up the encoder
-	encoder = OrdinalEncoder(encoding_method='ordered', variables=['pclass', 'cabin', 'embarked'])
+    encoder = OrdinalEncoder(
+        encoding_method='ordered',
+        variables=['pclass', 'cabin', 'embarked'],
+        ignore_format=True)
 
-	# fit the encoder
-	encoder.fit(X_train, y_train)
+    encoder.fit(X_train, y_train)
 
 With `fit()` the encoder learns the mappings for each category, which are stored in
 its `encoder_dict_` parameter:
@@ -82,26 +88,37 @@ variable to the new value.
 .. code:: python
 
 	{'pclass': {3: 0, 2: 1, 1: 2},
-	 'cabin': {'T': 0,
-	  'n': 1,
-	  'G': 2,
-	  'A': 3,
-	  'C': 4,
-	  'F': 5,
-	  'D': 6,
-	  'E': 7,
-	  'B': 8},
-	 'embarked': {'S': 0, 'Q': 1, 'C': 2}}
+	'cabin': {'T': 0,
+		'M': 1,
+		'G': 2,
+		'A': 3,
+		'C': 4,
+		'F': 5,
+		'D': 6,
+		'E': 7,
+		'B': 8},
+	'embarked': {'S': 0, 'Q': 1, 'C': 2, 'Missing': 3}}
 
 We can now go ahead and replace the original strings with the numbers:
 
 .. code:: python
 
-	# transform the data
-	train_t= encoder.transform(X_train)
-	test_t= encoder.transform(X_test)
+    train_t = encoder.transform(X_train)
+    test_t = encoder.transform(X_test)
+
+    print(train_t.head())
 
+Below we see the resulting dataframe, where the original variable values are now replaced
+with integers:
+
+.. code:: python
 
+          pclass     sex        age  sibsp  parch     fare  cabin  embarked
+    501        1  female  13.000000      0      1  19.5000      1         0
+    588        1  female   4.000000      1      1  23.0000      1         0
+    402        1  female  30.000000      1      0  13.8583      1         2
+    1193       0    male  29.881135      0      0   7.7250      1         1
+    686        0  female  22.000000      0      0   7.7250      1         1
 
 
 More details
diff --git a/docs/user_guide/encoding/RareLabelEncoder.rst b/docs/user_guide/encoding/RareLabelEncoder.rst
index 8916452..ce26f30 100644
--- a/docs/user_guide/encoding/RareLabelEncoder.rst
+++ b/docs/user_guide/encoding/RareLabelEncoder.rst
@@ -45,28 +45,33 @@ First, let's load the data and separate it into train and test:
 
 .. 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.datasets import load_titanic
     from feature_engine.encoding import RareLabelEncoder
 
-    def load_titanic():
-        data = pd.read_csv(
-            'https://www.openml.org/data/get_csv/16826755/phpMYEkMl')
-        data = data.replace('?', np.nan)
-        data['cabin'] = data['cabin'].astype(str).str[0]
-        data['pclass'] = data['pclass'].astype('O')
-        data['embarked'].fillna('C', inplace=True)
-        return data
+    X, y = load_titanic(
+        return_X_y_frame=True,
+        handle_missing=True,
+        predictors_only=True,
+        cabin="letter_only",
+    )
 
-    data = load_titanic()
-
-    # Separate into train and test sets
     X_train, X_test, y_train, y_test = train_test_split(
-        data.drop(['survived', 'name', 'ticket'], axis=1),
-        data['survived'], test_size=0.3, random_state=0)
+        X, y, test_size=0.3, random_state=0,
+    )
+
+    print(X_train.head())
+
+We see the resulting data below:
+
+.. code:: python
+
+          pclass     sex        age  sibsp  parch     fare cabin embarked
+    501        2  female  13.000000      0      1  19.5000     M        S
+    588        2  female   4.000000      1      1  23.0000     M        S
+    402        2  female  30.000000      1      0  13.8583     M        C
+    1193       3    male  29.881135      0      0   7.7250     M        Q
+    686        3  female  22.000000      0      0   7.7250     M        Q
 
 Now, we set up the :class:`RareLabelEncoder()` to group categories shown by less than 3%
 of the observations into a new group or category called 'Rare'. We will group the
@@ -75,8 +80,13 @@ categories in the indicated variables if they have more than 2 unique categories
 .. code:: python
 
     # set up the encoder
-    encoder = RareLabelEncoder(tol=0.03, n_categories=2, variables=['cabin', 'pclass', 'embarked'],
-                               replace_with='Rare')
+    encoder = RareLabelEncoder(
+        tol=0.03,
+        n_categories=2,
+        variables=['cabin', 'pclass', 'embarked'],
+        replace_with='Rare',
+        ignore_format=True,
+    )
 
     # fit the encoder
     encoder.fit(X_train)
@@ -94,9 +104,9 @@ Any category that is not in this dictionary, will be grouped.
 
 .. code:: python
 
-	{'cabin': Index(['n', 'C', 'B', 'E', 'D'], dtype='object'),
-	 'pclass': array([2, 3, 1], dtype='int64'),
-	 'embarked': array(['S', 'C', 'Q'], dtype=object)}
+    {'cabin': ['M', 'C', 'B', 'E', 'D'],
+    'pclass': [3, 1, 2],
+    'embarked': ['S', 'C', 'Q']}
 
 Now we can go ahead and transform the variables:
 
diff --git a/docs/user_guide/encoding/StringSimilarityEncoder.rst b/docs/user_guide/encoding/StringSimilarityEncoder.rst
index 1c64554..fc1234d 100644
--- a/docs/user_guide/encoding/StringSimilarityEncoder.rst
+++ b/docs/user_guide/encoding/StringSimilarityEncoder.rst
@@ -127,17 +127,13 @@ into a train and a test set:
 .. code:: python
 
     import string
-    import numpy as np
-    import pandas as pd
     from sklearn.model_selection import train_test_split
-
+    from feature_engine.datasets import load_titanic
     from feature_engine.encoding import StringSimilarityEncoder
 
-    # Helper function for loading and preprocessing data
-    def load_titanic() -> pd.DataFrame:
+    def clean_titanic():
         translate_table = str.maketrans('' , '', string.punctuation)
-        data = pd.read_csv('https://www.openml.org/data/get_csv/16826755/phpMYEkMl')
-        data = data.replace('?', np.nan)
+        data = load_titanic()
         data['home.dest'] = (
         data['home.dest']
         .str.strip()
@@ -161,8 +157,7 @@ into a train and a test set:
         )
         return data
 
-    data = load_titanic()
-
+    data = clean_titanic()
     # Separate into train and test sets
     X_train, X_test, y_train, y_test = train_test_split(
         data.drop(['survived', 'sex', 'cabin', 'embarked'], axis=1),
@@ -177,26 +172,26 @@ Below, we see the first rows of the dataset:
 
 .. code:: python
 
-          pclass                             name  age  sibsp  parch  \
-    501        2  mellinger miss madeleine violet   13      0      1
-    588        2                  wells miss joan    4      1      1
-    402        2     duran y more miss florentina   30      1      0
-    1193       3                 scanlan mr james  NaN      0      0
-    686        3       bradley miss bridget delia   22      0      0
+        pclass                             name  age  sibsp  parch  \
+    501        2  mellinger miss madeleine violet   13      0      1   
+    588        2                  wells miss joan    4      1      1   
+    402        2     duran y more miss florentina   30      1      0   
+    1193       3                 scanlan mr james  NaN      0      0   
+    686        3       bradley miss bridget delia   22      0      0   
 
                 ticket     fare boat body  \
-    501         250644     19.5   14  NaN
-    588          29103       23   14  NaN
-    402   scparis 2148  13.8583   12  NaN
-    1193         36209    7.725  NaN  NaN
-    686         334914    7.725   13  NaN
+    501         250644     19.5   14  NaN   
+    588          29103       23   14  NaN   
+    402   scparis 2148  13.8583   12  NaN   
+    1193         36209    7.725  NaN  NaN   
+    686         334914    7.725   13  NaN   
 
-                                                home.dest
-    501                             england bennington vt
-    588                                 cornwall akron oh
-    402                       barcelona spain havana cuba
-    1193                                              NaN
-    686   kingwilliamstown co cork ireland glens falls ny
+                                                home.dest  
+    501                             england bennington vt  
+    588                                 cornwall akron oh  
+    402                       barcelona spain havana cuba  
+    1193                                              NaN  
+    686   kingwilliamstown co cork ireland glens falls ny 
 
 
 Now, we set up the encoder to encode only the 2 most frequent categories of each of the
@@ -204,14 +199,15 @@ Now, we set up the encoder to encode only the 2 most frequent categories of each
 
 .. code:: python
 
-	# set up the encoder
-	encoder = StringSimilarityEncoder(
-            top_categories=2,
-            variables=['name', 'home.dest', 'ticket'],
-            )
+    # set up the encoder
+    encoder = StringSimilarityEncoder(
+        top_categories=2,
+        variables=['name', 'home.dest', 'ticket'],
+        ignore_format=True
+        )
 
-	# fit the encoder
-	encoder.fit(X_train)
+    # fit the encoder
+    encoder.fit(X_train)
 
 With `fit()` the encoder will learn the most popular categories of the variables, which
 are stored in the attribute `encoder_dict_`.
@@ -246,25 +242,25 @@ Below, we see the resulting dataframe:
 
 .. code:: python
 
-          pclass  age  sibsp  parch    fare boat body  \
-    1139       3   38      0      0  7.8958  NaN  NaN
-    533        2   21      0      1      21   12  NaN
-    459        2   42      1      0      27  NaN  NaN
-    1150       3  NaN      0      0    14.5  NaN  NaN
-    393        2   25      0      0    31.5  NaN  NaN
-
-          name_mellinger miss madeleine violet  name_barbara mrs catherine david  \
-    1139                              0.454545                          0.550000
-    533                               0.615385                          0.524590
-    459                               0.596491                          0.603774
-    1150                              0.641509                          0.693878
-    393                               0.408163                          0.666667
-
-          home.dest_nan  home.dest_new york ny  ticket_ca 2343  ticket_ca 2144
-    1139            1.0               0.000000        0.461538        0.461538
-    533             0.0               0.370370        0.307692        0.307692
-    459             0.0               0.352941        0.461538        0.461538
-    1150            1.0               0.000000        0.307692        0.307692
+        pclass  age  sibsp  parch    fare boat body  \
+    1139       3   38      0      0  7.8958  NaN  NaN   
+    533        2   21      0      1      21   12  NaN   
+    459        2   42      1      0      27  NaN  NaN   
+    1150       3  NaN      0      0    14.5  NaN  NaN   
+    393        2   25      0      0    31.5  NaN  NaN   
+
+        name_mellinger miss madeleine violet  name_barbara mrs catherine david  \
+    1139                              0.454545                          0.550000   
+    533                               0.615385                          0.524590   
+    459                               0.596491                          0.603774   
+    1150                              0.641509                          0.693878   
+    393                               0.408163                          0.666667   
+
+        home.dest_nan  home.dest_new york ny  ticket_ca 2343  ticket_ca 2144  
+    1139            1.0               0.000000        0.461538        0.461538  
+    533             0.0               0.370370        0.307692        0.307692  
+    459             0.0               0.352941        0.461538        0.461538  
+    1150            1.0               0.000000        0.307692        0.307692  
     393             0.0               0.437500        0.666667        0.666667
 
 
diff --git a/docs/user_guide/encoding/WoEEncoder.rst b/docs/user_guide/encoding/WoEEncoder.rst
index 65fbd26..61f950b 100644
--- a/docs/user_guide/encoding/WoEEncoder.rst
+++ b/docs/user_guide/encoding/WoEEncoder.rst
@@ -50,51 +50,64 @@ First, let's load the data and separate it into train and test:
 
 .. 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.encoding import WoEEncoder, RareLabelEncoder
-
-	# Load dataset
-	def load_titanic():
-		data = pd.read_csv('https://www.openml.org/data/get_csv/16826755/phpMYEkMl')
-		data = data.replace('?', np.nan)
-		data['cabin'] = data['cabin'].astype(str).str[0]
-		data['pclass'] = data['pclass'].astype('O')
-		data['embarked'].fillna('C', inplace=True)
-		return data
-	
-	data = load_titanic()
-
-	# Separate into train and test sets
-	X_train, X_test, y_train, y_test = train_test_split(
-			data.drop(['survived', 'name', 'ticket'], axis=1),
-			data['survived'], test_size=0.3, random_state=0)
+    from sklearn.model_selection import train_test_split
+    from feature_engine.datasets import load_titanic
+    from feature_engine.encoding import WoEEncoder, RareLabelEncoder
+
+    X, y = load_titanic(
+        return_X_y_frame=True,
+        handle_missing=True,
+        predictors_only=True,
+        cabin="letter_only",
+    )
+
+    X_train, X_test, y_train, y_test = train_test_split(
+        X, y, test_size=0.3, random_state=0,
+    )
+
+    print(X_train.head())
+
+We see the resulting data below:
+
+.. code:: python
+
+          pclass     sex        age  sibsp  parch     fare cabin embarked
+    501        2  female  13.000000      0      1  19.5000     M        S
+    588        2  female   4.000000      1      1  23.0000     M        S
+    402        2  female  30.000000      1      0  13.8583     M        C
+    1193       3    male  29.881135      0      0   7.7250     M        Q
+    686        3  female  22.000000      0      0   7.7250     M        Q
 
 Before we encode the variables, I would like to group infrequent categories into one
 category, called 'Rare'. For this, I will use the :class:`RareLabelEncoder()` as follows:
 
 .. code:: python
 
-	# set up a rare label encoder
-	rare_encoder = RareLabelEncoder(tol=0.03, n_categories=2, variables=['cabin', 'pclass', 'embarked'])
+    # set up a rare label encoder
+    rare_encoder = RareLabelEncoder(
+        tol=0.1,
+        n_categories=2,
+        variables=['cabin', 'pclass', 'embarked'],
+        ignore_format=True,
+    )
 
-	# fit and transform data
-	train_t = rare_encoder.fit_transform(X_train)
-	test_t = rare_encoder.transform(X_train)
+    # fit and transform data
+    train_t = rare_encoder.fit_transform(X_train)
+    test_t = rare_encoder.transform(X_train)
 
 Now, we set up the :class:`WoEEncoder()` to replace the categories by the weight of the
 evidence, only in the 3 indicated variables:
 
 .. code:: python
 
-	# set up a weight of evidence encoder
-	woe_encoder = WoEEncoder(variables=['cabin', 'pclass', 'embarked'])
+    # set up a weight of evidence encoder
+    woe_encoder = WoEEncoder(
+        variables=['cabin', 'pclass', 'embarked'],
+        ignore_format=True,
+    )
 
-	# fit the encoder
-	woe_encoder.fit(train_t, y_train)
+    # fit the encoder
+    woe_encoder.fit(train_t, y_train)
 
 With `fit()` the encoder learns the weight of the evidence for each category, which are stored in
 its `encoder_dict_` parameter:
@@ -108,26 +121,33 @@ variables to encode. This way, we can map the original values to the new value.
 
 .. code:: python
 
-    {'cabin': {'B': 1.6299623810120747,
-    'C': 0.7217038208351837,
-    'D': 1.405081209799324,
-    'E': 1.405081209799324,
-    'Rare': 0.7387452866900354,
-    'n': -0.35752781962490193},
-    'pclass': {1: 0.9453018143294478,
-    2: 0.21009172435857942,
-    3: -0.5841726684724614},
-    'embarked': {'C': 0.6999054533737715,
-    'Q': -0.05044494288988759,
-    'S': -0.20113381737960143}}
+    {'cabin': {'M': -0.35752781962490193, 'Rare': 1.083797390800775},
+     'pclass': {'1': 0.9453018143294478,
+      '2': 0.21009172435857942,
+      '3': -0.5841726684724614},
+     'embarked': {'C': 0.679904786667102,
+      'Rare': 0.012075414091446468,
+      'S': -0.20113381737960143}}
 
 Now, we can go ahead and encode the variables:
 
 .. code:: python
 
-	# transform
-	train_t = woe_encoder.transform(train_t)
-	test_t = woe_encoder.transform(test_t)
+    train_t = woe_encoder.transform(train_t)
+    test_t = woe_encoder.transform(test_t)
+
+    print(train_t.head())
+
+Below we see the resulting dataset with the weight of the evidence:
+
+.. code:: python
+
+            pclass     sex        age  sibsp  parch     fare     cabin  embarked
+    501   0.210092  female  13.000000      0      1  19.5000 -0.357528 -0.201134
+    588   0.210092  female   4.000000      1      1  23.0000 -0.357528 -0.201134
+    402   0.210092  female  30.000000      1      0  13.8583 -0.357528  0.679905
+    1193 -0.584173    male  29.881135      0      0   7.7250 -0.357528  0.012075
+    686  -0.584173  female  22.000000      0      0   7.7250 -0.357528  0.012075
 
 
 **WoE for continuous variables**
diff --git a/docs/user_guide/outliers/ArbitraryOutlierCapper.rst b/docs/user_guide/outliers/ArbitraryOutlierCapper.rst
index 1a8d828..eb7934e 100644
--- a/docs/user_guide/outliers/ArbitraryOutlierCapper.rst
+++ b/docs/user_guide/outliers/ArbitraryOutlierCapper.rst
@@ -14,32 +14,33 @@ into a train and a test set:
 
 .. 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.outliers import ArbitraryOutlierCapper
-
-	# Load dataset
-	def load_titanic():
-		data = pd.read_csv('https://www.openml.org/data/get_csv/16826755/phpMYEkMl')
-		data = data.replace('?', np.nan)
-		data['cabin'] = data['cabin'].astype(str).str[0]
-		data['pclass'] = data['pclass'].astype('O')
-		data['embarked'].fillna('C', inplace=True)
-		data['fare'] = data['fare'].astype('float')
-		data['fare'].fillna(data['fare'].median(), inplace=True)
-		data['age'] = data['age'].astype('float')
-		data['age'].fillna(data['age'].median(), inplace=True)
-		return data
-	
-	data = load_titanic()
-
-	# Separate into train and test sets
-	X_train, X_test, y_train, y_test = train_test_split(
-			data.drop(['survived', 'name', 'ticket'], axis=1),
-			data['survived'], test_size=0.3, random_state=0)
+    from sklearn.model_selection import train_test_split
+    from feature_engine.datasets import load_titanic
+    from feature_engine.outliers import ArbitraryOutlierCapper
+
+    X, y = load_titanic(
+        return_X_y_frame=True,
+        predictors_only=True,
+        handle_missing=True,
+    )
+
+
+    X_train, X_test, y_train, y_test = train_test_split(
+        X, y, test_size=0.3, random_state=0,
+    )
+
+    print(X_train.head())
+
+We see the resulting data below:
+
+.. code:: python
+
+          pclass     sex        age  sibsp  parch     fare    cabin embarked
+    501        2  female  13.000000      0      1  19.5000  Missing        S
+    588        2  female   4.000000      1      1  23.0000  Missing        S
+    402        2  female  30.000000      1      0  13.8583  Missing        C
+    1193       3    male  29.881135      0      0   7.7250  Missing        Q
+    686        3  female  22.000000      0      0   7.7250  Missing        Q
 
 Now, we set up the :class:`ArbitraryOutlierCapper()` indicating that we want to cap the
 variable 'age' at 50 and the variable 'Fare' at 200. We do not want to cap these variables
@@ -47,11 +48,12 @@ on the left side of their distribution.
 
 .. code:: python
 
-	# set up the capper
-	capper = ArbitraryOutlierCapper(max_capping_dict={'age': 50, 'fare': 200}, min_capping_dict=None)
+    capper = ArbitraryOutlierCapper(
+        max_capping_dict={'age': 50, 'fare': 200},
+        min_capping_dict=None,
+    )
 
-	# fit the capper
-	capper.fit(X_train)
+    capper.fit(X_train)
 
 With `fit()` the transformer does not learn any parameter. It just reassigns the entered
 dictionary to the attribute that will be used in the transformation:
@@ -68,9 +70,8 @@ Now, we can go ahead and cap the variables:
 
 .. code:: python
 
-	# transform the data
-	train_t= capper.transform(X_train)
-	test_t= capper.transform(X_test)
+	train_t = capper.transform(X_train)
+	test_t = capper.transform(X_test)
 
 If we now check the maximum values in the transformed data, they should be those entered
 in the dictionary:
@@ -81,8 +82,8 @@ in the dictionary:
 
 .. code:: python
 
-    fare    200
-    age      50
+    fare    200.0
+    age      50.0
     dtype: float64
 
 
diff --git a/docs/user_guide/outliers/OutlierTrimmer.rst b/docs/user_guide/outliers/OutlierTrimmer.rst
index 7143842..3e92985 100644
--- a/docs/user_guide/outliers/OutlierTrimmer.rst
+++ b/docs/user_guide/outliers/OutlierTrimmer.rst
@@ -40,32 +40,33 @@ it into train and test:
 
 .. 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.datasets import load_titanic
     from feature_engine.outliers import OutlierTrimmer
 
-    # Load dataset
-    def load_titanic():
-        data = pd.read_csv('https://www.openml.org/data/get_csv/16826755/phpMYEkMl')
-        data = data.replace('?', np.nan)
-        data['cabin'] = data['cabin'].astype(str).str[0]
-        data['pclass'] = data['pclass'].astype('O')
-        data['embarked'].fillna('C', inplace=True)
-        data['fare'] = data['fare'].astype('float')
-        data['fare'].fillna(data['fare'].median(), inplace=True)
-        data['age'] = data['age'].astype('float')
-        data['age'].fillna(data['age'].median(), inplace=True)
-        return data
-
-    data = load_titanic()
-
-    # Separate into train and test sets
+    X, y = load_titanic(
+        return_X_y_frame=True,
+        predictors_only=True,
+        handle_missing=True,
+    )
+
+
     X_train, X_test, y_train, y_test = train_test_split(
-		data.drop(['survived', 'name', 'ticket'], axis=1),
-		data['survived'], test_size=0.3, random_state=0)
+        X, y, test_size=0.3, random_state=0,
+    )
+
+    print(X_train.head())
+
+We see the resulting data below:
+
+.. code:: python
+
+          pclass     sex        age  sibsp  parch     fare    cabin embarked
+    501        2  female  13.000000      0      1  19.5000  Missing        S
+    588        2  female   4.000000      1      1  23.0000  Missing        S
+    402        2  female  30.000000      1      0  13.8583  Missing        C
+    1193       3    male  29.881135      0      0   7.7250  Missing        Q
+    686        3  female  22.000000      0      0   7.7250  Missing        Q
 
 Now, we will set the :class:`OutlierTrimmer()` to remove outliers at the right side of the
 distribution only (param `tail`). We want the maximum values to be determined using the
@@ -75,10 +76,11 @@ list.
 
 .. code:: python
 
-    # set up the capper
-    capper = OutlierTrimmer(capping_method='iqr', tail='right', fold=1.5, variables=['age', 'fare'])
+    capper = OutlierTrimmer(capping_method='iqr',
+                            tail='right', 
+                            fold=1.5, 
+                            variables=['age', 'fare'])
 
-    # fit the capper
     capper.fit(X_train)
 
 With `fit()`, the :class:`OutlierTrimmer()` finds the values at which it should cap the variables.
@@ -96,9 +98,8 @@ We can now go ahead and remove the outliers:
 
 .. code:: python
 
-    # transform the data
-    train_t= capper.transform(X_train)
-    test_t= capper.transform(X_test)
+    train_t = capper.transform(X_train)
+    test_t = capper.transform(X_test)
 
 If we evaluate now the maximum of the variables in the transformed datasets, they should
 be <= the values observed in the attribute `right_tail_caps_`:
diff --git a/docs/user_guide/outliers/Winsorizer.rst b/docs/user_guide/outliers/Winsorizer.rst
index c15249c..6fa3b39 100644
--- a/docs/user_guide/outliers/Winsorizer.rst
+++ b/docs/user_guide/outliers/Winsorizer.rst
@@ -25,7 +25,7 @@ MAD limits:
     - right tail: median + 3* MAD
     - left tail:  median - 3* MAD
 
-where MAD is the median absoulte deviation from the median.
+where MAD is the median absolute deviation from the median.
 
 percentiles or quantiles:
 
@@ -39,32 +39,33 @@ it into train and test:
 
 .. 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.datasets import load_titanic
     from feature_engine.outliers import Winsorizer
 
-    # Load dataset
-    def load_titanic():
-        data = pd.read_csv('https://www.openml.org/data/get_csv/16826755/phpMYEkMl')
-        data = data.replace('?', np.nan)
-        data['cabin'] = data['cabin'].astype(str).str[0]
-        data['pclass'] = data['pclass'].astype('O')
-        data['embarked'].fillna('C', inplace=True)
-        data['fare'] = data['fare'].astype('float')
-        data['fare'].fillna(data['fare'].median(), inplace=True)
-        data['age'] = data['age'].astype('float')
-        data['age'].fillna(data['age'].median(), inplace=True)
-        return data
-	
-    data = load_titanic()
-
-    # Separate into train and test sets
+    X, y = load_titanic(
+        return_X_y_frame=True,
+        predictors_only=True,
+        handle_missing=True,
+    )
+
+
     X_train, X_test, y_train, y_test = train_test_split(
-		data.drop(['survived', 'name', 'ticket'], axis=1),
-		data['survived'], test_size=0.3, random_state=0)
+        X, y, test_size=0.3, random_state=0,
+    )
+
+    print(X_train.head())
+
+We see the resulting data below:
+
+.. code:: python
+
+          pclass     sex        age  sibsp  parch     fare    cabin embarked
+    501        2  female  13.000000      0      1  19.5000  Missing        S
+    588        2  female   4.000000      1      1  23.0000  Missing        S
+    402        2  female  30.000000      1      0  13.8583  Missing        C
+    1193       3    male  29.881135      0      0   7.7250  Missing        Q
+    686        3  female  22.000000      0      0   7.7250  Missing        Q
 
 Now, we will set the :class:`Winsorizer()` to cap outliers at the right side of the
 distribution only (param `tail`). We want the maximum values to be determined using the
@@ -74,10 +75,11 @@ list.
 
 .. code:: python
 
-    # set up the capper
-    capper = Winsorizer(capping_method='gaussian', tail='right', fold=3, variables=['age', 'fare'])
+    capper = Winsorizer(capping_method='gaussian',
+                        tail='right', 
+                        fold=3, 
+                        variables=['age', 'fare'])
 
-    # fit the capper
     capper.fit(X_train)
 
 With `fit()`, the :class:`Winsorizer()` finds the values at which it should cap the variables.
@@ -89,15 +91,15 @@ These values are stored in its attribute:
 
 .. code:: python
 
-	{'age': 67.49048447470315, 'fare': 174.78162171790441}
+	{'age': 67.73951212364803, 'fare': 174.70395336846678}
 
 We can now go ahead and censor the outliers:
 
 .. code:: python
 
     # transform the data
-    train_t= capper.transform(X_train)
-    test_t= capper.transform(X_test)
+    train_t = capper.transform(X_train)
+    test_t = capper.transform(X_test)
     
 If we evaluate now the maximum of the variables in the transformed datasets, they should
 coincide with the values observed in the attribute `right_tail_caps_`:
@@ -108,8 +110,8 @@ coincide with the values observed in the attribute `right_tail_caps_`:
 
 .. code:: python
 
-    fare    174.781622
-    age      67.490484
+    fare    174.703953
+    age      67.739512
     dtype: float64
 
 More details
diff --git a/docs/user_guide/preprocessing/MatchCategories.rst b/docs/user_guide/preprocessing/MatchCategories.rst
index 712a74f..5b480da 100644
--- a/docs/user_guide/preprocessing/MatchCategories.rst
+++ b/docs/user_guide/preprocessing/MatchCategories.rst
@@ -24,29 +24,17 @@ a train and a test sets:
 
 .. code:: python
 
-    import numpy as np
-    import pandas as pd
-
     from feature_engine.preprocessing import MatchCategories
-
+    from feature_engine.datasets import load_titanic
 
     # Load dataset
-    def load_titanic():
-        data = pd.read_csv('https://www.openml.org/data/get_csv/16826755/phpMYEkMl')
-        data = data.replace('?', np.nan)
-        data['cabin'] = data['cabin'].astype(str).str[0]
-        data['pclass'] = data['pclass'].astype('O')
-        data['age'] = data['age'].astype('float')
-        data['fare'] = data['fare'].astype('float')
-        data['embarked'].fillna('C', inplace=True)
-        data.drop(
-            labels=['name', 'ticket', 'boat', 'body', 'home.dest'],
-            axis=1, inplace=True,
-        )
-        return data
-
-    # load data as pandas dataframe
-    data = load_titanic()
+    data = load_titanic(
+        predictors_only=True,
+        handle_missing=True,
+        cabin="letter_only",
+    )
+
+    data['pclass'] = data['pclass'].astype('O')
 
     # Split test and train
     train = data.iloc[0:1000, :]
@@ -73,9 +61,8 @@ Now, we set up :class:`MatchCategories()` and fit it to the train set.
 
     {'pclass': Int64Index([1, 2, 3], dtype='int64'),
      'sex': Index(['female', 'male'], dtype='object'),
-     'cabin': Index(['A', 'B', 'C', 'D', 'E', 'F', 'T', 'n'], dtype='object'),
-     'embarked': Index(['C', 'Q', 'S'], dtype='object')}
-
+     'cabin': Index(['A', 'B', 'C', 'D', 'E', 'F', 'M', 'T'], dtype='object'),
+     'embarked': Index(['C', 'Missing', 'Q', 'S'], dtype='object')}
 
 If we transform the test dataframe using the same `match_categories` object,
 categorical variables will be converted to a 'category' dtype with the same
@@ -89,7 +76,7 @@ dataset:
 
 .. code:: python
 
-    array(['S', 'C', 'Q'], dtype=object)
+    array(['S', 'C', 'Missing', 'Q'], dtype=object)
 
 .. code:: python
     
@@ -107,7 +94,7 @@ dataset:
 
 .. code:: python
 
-    Index(['C', 'Q', 'S'], dtype='object')
+    Index(['C', 'Missing', 'Q', 'S'], dtype='object')
 
 .. code:: python
 
@@ -116,8 +103,7 @@ dataset:
 
 .. code:: python
 
-    Index(['C', 'Q', 'S'], dtype='object')
-
+    Index(['C', 'Missing', 'Q', 'S'], dtype='object')
 
 If some category was not present in the training data, it will not mapped
 to any integer and will thus not get encoded. This behavior can be modified through the
@@ -130,7 +116,7 @@ parameter `errors`:
 
 .. code:: python
 
-    array(['B', 'C', 'E', 'D', 'A', 'n', 'T', 'F'], dtype=object)
+    array(['B', 'C', 'E', 'D', 'A', 'M', 'T', 'F'], dtype=object)
 
 .. code:: python
     
@@ -139,7 +125,7 @@ parameter `errors`:
 
 .. code:: python
 
-    array(['n', 'F', 'E', 'G'], dtype=object)
+    array(['M', 'F', 'E', 'G'], dtype=object)
 
 .. code:: python
 
@@ -147,8 +133,8 @@ parameter `errors`:
 
 .. code:: python
 
-    ['B', 'C', 'E', 'D', 'A', 'n', 'T', 'F']
-    Categories (8, object): ['A', 'B', 'C', 'D', 'E', 'F', 'T', 'n']
+    ['B', 'C', 'E', 'D', 'A', 'M', 'T', 'F']
+    Categories (8, object): ['A', 'B', 'C', 'D', 'E', 'F', 'M', 'T']
 
 .. code:: python
     
@@ -157,8 +143,8 @@ parameter `errors`:
 
 .. code:: python
 
-    ['n', 'F', 'E', NaN]
-    Categories (8, object): ['A', 'B', 'C', 'D', 'E', 'F', 'T', 'n']
+    ['M', 'F', 'E', NaN]
+    Categories (8, object): ['A', 'B', 'C', 'D', 'E', 'F', 'M', 'T']
 
 
 When to use the transformer
diff --git a/docs/user_guide/preprocessing/MatchVariables.rst b/docs/user_guide/preprocessing/MatchVariables.rst
index 18667f5..53251e6 100644
--- a/docs/user_guide/preprocessing/MatchVariables.rst
+++ b/docs/user_guide/preprocessing/MatchVariables.rst
@@ -18,29 +18,16 @@ a train and a test set:
 
 .. code:: python
 
-    import numpy as np
-    import pandas as pd
-
     from feature_engine.preprocessing import MatchVariables
-
+    from feature_engine.datasets import load_titanic
 
     # Load dataset
-    def load_titanic():
-        data = pd.read_csv('https://www.openml.org/data/get_csv/16826755/phpMYEkMl')
-        data = data.replace('?', np.nan)
-        data['cabin'] = data['cabin'].astype(str).str[0]
-        data['pclass'] = data['pclass'].astype('O')
-        data['age'] = data['age'].astype('float')
-        data['fare'] = data['fare'].astype('float')
-        data['embarked'].fillna('C', inplace=True)
-        data.drop(
-            labels=['name', 'ticket', 'boat', 'body', 'home.dest'],
-            axis=1, inplace=True,
-        )
-        return data
-
-    # load data as pandas dataframe
-    data = load_titanic()
+    data = load_titanic(
+        predictors_only=True,
+        cabin="letter_only",
+    )
+
+    data['pclass'] = data['pclass'].astype('O')
 
     # Split test and train
     train = data.iloc[0:1000, :]
@@ -61,7 +48,7 @@ Now, we set up :class:`MatchVariables()` and fit it to the train set.
 .. code:: python
 
     # the transformer stores the input variables
-    match_cols.input_features_
+    match_cols.feature_names_in_
 
 .. code:: python
 
@@ -106,8 +93,7 @@ are now back in the data, with np.nan values as default.
 
 .. code:: python
 
-    The following variables are added to the DataFrame: ['sex', 'age']
-
+    The following variables are added to the DataFrame: ['age', 'sex']
          pclass  survived  sex  age  sibsp  parch     fare cabin embarked
     1000      3         1  NaN  NaN      0      0   7.7500     n        Q
     1001      3         1  NaN  NaN      2      0  23.2500     n        Q
@@ -115,8 +101,6 @@ are now back in the data, with np.nan values as default.
     1003      3         1  NaN  NaN      2      0  23.2500     n        Q
     1004      3         1  NaN  NaN      0      0   7.7875     n        Q
 
-
-
 Note how the missing columns were added back to the transformed test set, with
 missing values, in the position (i.e., order) in which they were in the train set.
 
@@ -139,7 +123,6 @@ test that, let's add some extra columns to the test set:
     1003      3         1      2      0  23.2500     n        Q      0      0
     1004      3         1      0      0   7.7875     n        Q      0      0
 
-
 And now, we transform the data with :class:`MatchVariables()`:
 
 .. code:: python
@@ -151,8 +134,7 @@ And now, we transform the data with :class:`MatchVariables()`:
 .. code:: python
 
     The following variables are added to the DataFrame: ['age', 'sex']
-    The following variables are dropped from the DataFrame: ['var_a', 'var_b']
-
+    The following variables are dropped from the DataFrame: ['var_b', 'var_a']
          pclass  survived  sex  age  sibsp  parch     fare cabin embarked
     1000      3         1  NaN  NaN      0      0   7.7500     n        Q
     1001      3         1  NaN  NaN      2      0  23.2500     n        Q
@@ -160,7 +142,6 @@ And now, we transform the data with :class:`MatchVariables()`:
     1003      3         1  NaN  NaN      2      0  23.2500     n        Q
     1004      3         1  NaN  NaN      0      0   7.7875     n        Q
 
-
 Now, the transformer simultaneously added the missing columns with NA as values and
 removed the additional columns from the resulting dataset.
 
diff --git a/docs/user_guide/selection/DropConstantFeatures.rst b/docs/user_guide/selection/DropConstantFeatures.rst
index 06b8f0e..dccbb54 100644
--- a/docs/user_guide/selection/DropConstantFeatures.rst
+++ b/docs/user_guide/selection/DropConstantFeatures.rst
@@ -21,28 +21,19 @@ first load the data and separate it into train and test:
 
 .. code:: python
 
-    import numpy as np
-    import pandas as pd
     from sklearn.model_selection import train_test_split
-
+    from feature_engine.datasets import load_titanic
     from feature_engine.selection import DropConstantFeatures
 
-    # Load dataset
-    def load_titanic():
-            data = pd.read_csv('https://www.openml.org/data/get_csv/16826755/phpMYEkMl')
-            data = data.replace('?', np.nan)
-            data['cabin'] = data['cabin'].astype(str).str[0]
-            data['pclass'] = data['pclass'].astype('O')
-            data['embarked'].fillna('C', inplace=True)
-            return data
+    X, y = load_titanic(
+        return_X_y_frame=True,
+        handle_missing=True,
+    )
 
-    # load data as pandas dataframe
-    data = load_titanic()
 
-    # Separate into train and test sets
     X_train, X_test, y_train, y_test = train_test_split(
-                data.drop(['survived', 'name', 'ticket'], axis=1),
-                data['survived'], test_size=0.3, random_state=0)
+        X, y, test_size=0.3, random_state=0,
+    )
 
 Now, we set up the :class:`DropConstantFeatures()` to remove features that show the same
 value in more than 70% of the observations:
@@ -50,7 +41,7 @@ value in more than 70% of the observations:
 .. code:: python
 
     # set up the transformer
-    transformer = DropConstantFeatures(tol=0.7, missing_values='ignore')
+    transformer = DropConstantFeatures(tol=0.7)
 
 
 With `fit()` the transformer finds the variables to drop:
@@ -68,7 +59,7 @@ The variables to drop are stored in the attribute `features_to_drop_`:
 
 .. code:: python
 
-    ['parch', 'cabin', 'embarked']
+    ['parch', 'cabin', 'embarked', 'body']
 
 
 We see in the following code snippets that for the variables parch and embarked, more
@@ -76,13 +67,14 @@ than 70% of the observations displayed the same value:
 
 .. code:: python
 
-    X_train['embarked'].value_counts() / len(X_train)
+    X_train['embarked'].value_counts(normalize = True)
 
 .. code:: python
 
-    S    0.711790
-    C    0.197598
-    Q    0.090611
+    S          0.711790
+    C          0.195415
+    Q          0.090611
+    Missing    0.002183
     Name: embarked, dtype: float64
 
 
@@ -90,7 +82,7 @@ than 70% of the observations displayed the same value:
 
 .. code:: python
 
-    X_train['parch'].value_counts() / len(X_train)
+    X_train['parch'].value_counts(normalize = True)
 
 .. code:: python
 
@@ -111,8 +103,36 @@ With `transform()`, we can go ahead and drop the variables from the data:
 
 .. code:: python
 
-    # transform the data
     train_t = transformer.transform(X_train)
+    test_t = transformer.transform(X_test)
+
+    print(train_t.head())
+
+We see the resulting dataframe below:
+
+.. code:: python
+
+          pclass                               name     sex        age  sibsp  \
+    501        2  Mellinger, Miss. Madeleine Violet  female  13.000000      0
+    588        2                  Wells, Miss. Joan  female   4.000000      1
+    402        2     Duran y More, Miss. Florentina  female  30.000000      1
+    1193       3                 Scanlan, Mr. James    male  29.881135      0
+    686        3       Bradley, Miss. Bridget Delia  female  22.000000      0
+
+                 ticket     fare     boat  \
+    501          250644  19.5000       14
+    588           29103  23.0000       14
+    402   SC/PARIS 2148  13.8583       12
+    1193          36209   7.7250  Missing
+    686          334914   7.7250       13
+
+                                                  home.dest
+    501                            England / Bennington, VT
+    588                                Cornwall / Akron, OH
+    402                     Barcelona, Spain / Havana, Cuba
+    1193                                            Missing
+    686   Kingwilliamstown, Co Cork, Ireland Glens Falls...
+
 
 More details
 ^^^^^^^^^^^^
diff --git a/docs/user_guide/selection/DropDuplicateFeatures.rst b/docs/user_guide/selection/DropDuplicateFeatures.rst
index 873e0b5..ba8876a 100644
--- a/docs/user_guide/selection/DropDuplicateFeatures.rst
+++ b/docs/user_guide/selection/DropDuplicateFeatures.rst
@@ -20,70 +20,89 @@ These dataset does not have duplicated features, so we will add a few manually:
 
 .. 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.datasets import load_titanic
     from feature_engine.selection import DropDuplicateFeatures
 
-    def load_titanic():
-            data = pd.read_csv('https://www.openml.org/data/get_csv/16826755/phpMYEkMl')
-            data = data.replace('?', np.nan)
-            data['cabin'] = data['cabin'].astype(str).str[0]
-            data = data[['pclass', 'survived', 'sex', 'age', 'sibsp', 'parch', 'cabin', 'embarked']]
-            data = pd.concat([data, data[['sex', 'age', 'sibsp']]], axis=1)
-            data.columns = ['pclass', 'survived', 'sex', 'age', 'sibsp', 'parch', 'cabin', 'embarked',
-                            'sex_dup', 'age_dup', 'sibsp_dup']
-            return data
+    data = load_titanic(
+        handle_missing=True,
+        predictors_only=True,
+    )
 
-    # load data as pandas dataframe
-    data = load_titanic()
-    data.head()
+    # Lets duplicate some columns
+    data = pd.concat([data, data[['sex', 'age', 'sibsp']]], axis=1)
+    data.columns = ['pclass', 'survived', 'sex', 'age',
+                    'sibsp', 'parch', 'fare','cabin', 'embarked',
+                    'sex_dup', 'age_dup', 'sibsp_dup']
 
     # Separate into train and test sets
     X_train, X_test, y_train, y_test = train_test_split(
-                data.drop(['survived'], axis=1),
-                data['survived'], test_size=0.3, random_state=0)
+        data.drop(['survived'], axis=1),
+        data['survived'],
+        test_size=0.3,
+        random_state=0,
+    )
+
+    print(X_train.head())
+
+Below we see the resulting data:
+
+.. code:: python
+
+          pclass     sex        age  sibsp  parch     fare    cabin embarked  \
+    501        2  female  13.000000      0      1  19.5000  Missing        S
+    588        2  female   4.000000      1      1  23.0000  Missing        S
+    402        2  female  30.000000      1      0  13.8583  Missing        C
+    1193       3    male  29.881135      0      0   7.7250  Missing        Q
+    686        3  female  22.000000      0      0   7.7250  Missing        Q
+
+         sex_dup    age_dup  sibsp_dup
+    501   female  13.000000          0
+    588   female   4.000000          1
+    402   female  30.000000          1
+    1193    male  29.881135          0
+    686   female  22.000000          0
 
 Now, we set up :class:`DropDuplicateFeatures()` to find the duplications:
 
 .. code:: python
 
-    # set up the transformer
     transformer = DropDuplicateFeatures()
 
-With `fit()` the transformer finds the duplicated features, With `transform()` it removes
-them:
+With `fit()` the transformer finds the duplicated features:
 
 .. code:: python
 
-    # fit the transformer
     transformer.fit(X_train)
 
-    # transform the data
-    train_t = transformer.transform(X_train)
+The features that are duplicated and will be removed are stored by the transformer:
 
-If we examine the variable names of the transformed dataset, we see that the duplicated
-features are not present:
+..  code:: python
+
+    transformer.features_to_drop_
 
 .. code:: python
 
-    train_t.columns
+    {'age_dup', 'sex_dup', 'sibsp_dup'}
+
+With `transform()` we remove the duplicated variables:
 
 .. code:: python
 
-    Index(['pclass', 'sex', 'age', 'sibsp', 'parch', 'cabin', 'embarked'], dtype='object')
+    train_t = transformer.transform(X_train)
+    test_t = transformer.transform(X_test)
 
-The features that are removed are stored in the transformer's attribute:
+If we examine the variable names of the transformed dataset, we see that the duplicated
+features are not present:
 
-..  code:: python
+.. code:: python
 
-    transformer.features_to_drop_
+    train_t.columns
 
 .. code:: python
 
-    {'age_dup', 'sex_dup', 'sibsp_dup'}
+    Index(['pclass', 'sex', 'age', 'sibsp', 'parch', 'fare', 'cabin', 'embarked'], dtype='object')
 
 And the transformer also stores the groups of duplicated features, which could be useful
 if we have groups where more than 2 features are identical.
diff --git a/docs/user_guide/selection/DropFeatures.rst b/docs/user_guide/selection/DropFeatures.rst
index 6d9b648..c99aaf9 100644
--- a/docs/user_guide/selection/DropFeatures.rst
+++ b/docs/user_guide/selection/DropFeatures.rst
@@ -29,41 +29,32 @@ first load the data and separate it into train and test:
 
 .. 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.datasets import load_titanic
     from feature_engine.selection import DropFeatures
 
-    # Load dataset
-    def load_titanic():
-            data = pd.read_csv('https://www.openml.org/data/get_csv/16826755/phpMYEkMl')
-            data = data.replace('?', np.nan)
-            data['cabin'] = data['cabin'].astype(str).str[0]
-            data['pclass'] = data['pclass'].astype('O')
-            data['embarked'].fillna('C', inplace=True)
-            return data
+    X, y = load_titanic(
+        return_X_y_frame=True,
+        handle_missing=True,
+    )
 
-    # load data as pandas dataframe
-    data = load_titanic()
 
-    # Separate into train and test sets
     X_train, X_test, y_train, y_test = train_test_split(
-                data.drop(['survived', 'name'], axis=1),
-                data['survived'], test_size=0.3, random_state=0)
+        X, y, test_size=0.3, random_state=0,
+    )
+
+    print(X_train.head())
 
 Now, we go ahead and print the dataset column names:
 
 .. code:: python
 
-    # original columns
     X_train.columns
 
 .. code:: python
 
-    Index(['pclass', 'sex', 'age', 'sibsp', 'parch', 'ticket', 'fare', 'cabin',
-           'embarked', 'boat', 'body', 'home.dest'],
+    Index(['pclass', 'name', 'sex', 'age', 'sibsp', 'parch', 'ticket', 'fare',
+           'cabin', 'embarked', 'boat', 'body', 'home.dest'],
           dtype='object')
 
 Now, with :class:`DropFeatures()` we can very easily drop a group of variables. Below
@@ -84,8 +75,9 @@ the variables as follows:
 
 .. code:: python
 
-    # transform the data
     train_t = transformer.transform(X_train)
+    test_t = transformer.transform(X_test)
+
 
 And now, if we print the variable names of the transformed dataset, we see that it has
 been reduced:
@@ -96,8 +88,7 @@ been reduced:
 
 .. code:: python
 
-    Index(['pclass', 'sex', 'age', 'cabin', 'embarked' 'boat'],
-          dtype='object')
+    Index(['pclass', 'name', 'sex', 'age', 'cabin', 'embarked', 'boat'], dtype='object')
 
 
 More details
diff --git a/docs/user_guide/selection/SelectByTargetMeanPerformance.rst b/docs/user_guide/selection/SelectByTargetMeanPerformance.rst
index 27d6540..14316cc 100644
--- a/docs/user_guide/selection/SelectByTargetMeanPerformance.rst
+++ b/docs/user_guide/selection/SelectByTargetMeanPerformance.rst
@@ -106,40 +106,23 @@ Let's see how to use this method to select variables in the Titanic dataset. Thi
 has a mix of numerical and categorical variables, then it is a good option to showcase
 this selector.
 
-Let's import the required libraries and classes
+Let's import the required libraries and classes, and prepare the titanic dataset:
 
 .. code:: python
 
-    import pandas as pd
     import numpy as np
+    import pandas as pd
     from sklearn.model_selection import train_test_split
-    from feature_engine.selection import SelectByTargetMeanPerformance:
-
-Let's now load and prepare the Titanic dataset:
-
-.. code:: python
 
-    # load data
-    data = pd.read_csv('https://www.openml.org/data/get_csv/16826755/phpMYEkMl')
-    data.drop(labels = ['name','boat', 'ticket','body', 'home.dest'], axis=1, inplace=True)
-    data = data.replace('?', np.nan)
+    from feature_engine.datasets import load_titanic
+    from feature_engine.encoding import RareLabelEncoder
+    from feature_engine.selection import SelectByTargetMeanPerformance
 
-    data.dropna(subset=['embarked', 'fare'], inplace=True)
-    data['fare'] = data['fare'].astype('float')
-
-    data['age'] = data['age'].astype('float')
-    data['age'] = data['age'].fillna(data['age'].mean())
-
-    def get_first_cabin(row):
-        try:
-            return row.split()[0]
-        except:
-            return 'N'
-
-    data['cabin'] = data['cabin'].apply(get_first_cabin)
-
-    # extract cabin letter
-    data['cabin'] = data['cabin'].str[0]
+    data = load_titanic(
+        handle_missing=True,
+        predictors_only=True,
+        cabin="letter_only",
+    )
 
     # replace infrequent cabins by N
     data['cabin'] = np.where(data['cabin'].isin(['T', 'G']), 'N', data['cabin'])
@@ -151,7 +134,7 @@ Let's now load and prepare the Titanic dataset:
     # cast variables as object to treat as categorical
     data[['pclass','sibsp','parch']] = data[['pclass','sibsp','parch']].astype('O')
 
-    data.head()
+    print(data.head())
 
 We can see the first 5 rows of data below:
 
@@ -164,7 +147,6 @@ We can see the first 5 rows of data below:
     3      1         0    male  30.0000     1     2  151.5500     C        S
     4      1         0  female  25.0000     1     2  151.5500     C        S
 
-
 Let's now go ahead and split the data into train and test sets:
 
 .. code:: python
@@ -182,7 +164,7 @@ We see the sizes of the datasets below:
 
 .. code:: python
 
-    ((1175, 8), (131, 8))
+    ((1178, 8), (131, 8))
 
 Now, we set up :class:`SelectByTargetMeanPerformance()`. We will examine the roc-auc
 using 3 fold cross-validation. We will separate numerical variables into equal-frequency
@@ -217,6 +199,8 @@ In the attribute `variables_` we find the variables that were evaluated:
 
     sel.variables_
 
+.. code:: python
+
     ['pclass', 'sex', 'age', 'sibsp', 'parch', 'fare', 'cabin', 'embarked']
 
 In the attribute `features_to_drop_` we find the variables that were not selected:
@@ -225,6 +209,8 @@ In the attribute `features_to_drop_` we find the variables that were not selecte
 
     sel.features_to_drop_
 
+.. code:: python
+
     ['age', 'sibsp', 'parch', 'embarked']
 
 In the attribute `feature_performance_` we find the ROC-AUC for each feature. Remember
@@ -234,14 +220,16 @@ that this is the average ROC-AUC in each cross-validation fold:
 
     sel.feature_performance_
 
-    {'pclass': 0.6692917241015676,
-     'sex': 0.7624307804352095,
-     'age': 0.5406671434172395,
-     'sibsp': 0.5669758659668949,
-     'parch': 0.5741629417199435,
-     'fare': 0.6555307060922498,
-     'cabin': 0.6323342342595275,
-     'embarked': 0.57348024722553}
+.. code:: python
+
+    {'pclass': 0.668151138112005,
+    'sex': 0.764831274819234,
+    'age': 0.535490029737471,
+    'sibsp': 0.5815934176199077,
+    'parch': 0.5721327969642238,
+    'fare': 0.6545985745474006,
+    'cabin': 0.630092526712033,
+    'embarked': 0.5765961846034091}
 
 The mean ROC-AUC of all features is 0.62, we can calculate it as follows:
 
@@ -249,7 +237,7 @@ The mean ROC-AUC of all features is 0.62, we can calculate it as follows:
 
     pd.Series(sel.feature_performance_).mean()
 
-    0.6218592054022702
+    0.6229357428894605
 
 So we can see that the transformer correclty selected the features with ROC-AUC above
 that value.
@@ -264,12 +252,12 @@ With `transform()` we can go ahead and drop the features:
 
 .. code:: python
 
-             pclass     sex     fare cabin
-    611       3    male   9.3500     N
-    414       2    male  21.0000     N
-    530       2    male  10.5000     N
-    1149      3  female   7.7208     N
-    944       3    male   7.8958     N
+         pclass     sex     fare cabin
+    1139      3    male   7.8958     M
+    533       2  female  21.0000     M
+    459       2    male  27.0000     M
+    1150      3    male  14.5000     M
+    393       2    male  31.5000     M
 
 And finally, we can also obtain the names of the features in the final transformed
 data:
@@ -278,6 +266,8 @@ data:
 
     sel.get_feature_names_out()
 
+.. code:: python
+
     ['pclass', 'sex', 'fare', 'cabin']
 
 
diff --git a/docs/user_guide/wrappers/Wrapper.rst b/docs/user_guide/wrappers/Wrapper.rst
index 3e1df4b..48e9c91 100644
--- a/docs/user_guide/wrappers/Wrapper.rst
+++ b/docs/user_guide/wrappers/Wrapper.rst
@@ -189,16 +189,19 @@ Scikit-Learn's PolynomialFeatures.
     from sklearn.model_selection import train_test_split
     from sklearn.preprocessing import PolynomialFeatures
     from sklearn.pipeline import Pipeline
+    from feature_engine.datasets import load_titanic
     from feature_engine.imputation import CategoricalImputer, MeanMedianImputer
     from feature_engine.encoding import OrdinalEncoder
     from feature_engine.wrappers import SklearnTransformerWrapper
 
-    df = pd.read_csv('https://www.openml.org/data/get_csv/16826755/phpMYEkMl')
-    X = df[['pclass','sex','age','fare','embarked']].replace('?',np.nan)
-    X[['age', 'fare']] = X[['age', 'fare']].astype('float64')
-    y = df.survived
+    X, y = load_titanic(
+        return_X_y_frame=True,
+        predictors_only=True,
+        cabin="letter_only",
+    )
 
     X_train, X_test, y_train, y_test= train_test_split(X, y, test_size=0.2, random_state=42)
+
     pipeline = Pipeline(steps = [
         ('ci', CategoricalImputer(imputation_method='frequent')),
         ('mmi', MeanMedianImputer(imputation_method='mean')),
@@ -207,25 +210,29 @@ Scikit-Learn's PolynomialFeatures.
             PolynomialFeatures(interaction_only = True, include_bias=False),
             variables=['pclass','sex']))
     ])
+
     pipeline.fit(X_train)
     X_train_transformed = pipeline.transform(X_train)
     X_test_transformed = pipeline.transform(X_test)
 
     print(X_train_transformed.head())
-            age        fare      embarked  pclass  sex  pclass sex
-    772  17.000000    7.8958         0     3.0     0.0      0.0
-    543  36.000000   10.5000         0     2.0     0.0      0.0
-    289  18.000000   79.6500         0     1.0     1.0      1.0
-    10   47.000000  227.5250         1     1.0     0.0      0.0
-    147  29.532738   42.4000         0     1.0     0.0      0.0
-
-    print(X_test_transformed.head())
-            age        fare      embarked  pclass  sex  pclass sex
-    1148  35.000000   7.1250         0     3.0     0.0      0.0
-    1049  20.000000  15.7417         1     3.0     0.0      0.0
-    982   29.532738   7.8958         0     3.0     0.0      0.0
-    808   29.532738   8.0500         0     3.0     0.0      0.0
-    1195  29.532738   7.7500         2     3.0     0.0      0.0
+
+.. code:: python
+
+               age  sibsp  parch      fare  cabin  embarked  pclass  sex  \
+    772  17.000000      0      0    7.8958      0         0     3.0  0.0
+    543  36.000000      0      0   10.5000      0         0     2.0  0.0
+    289  18.000000      0      2   79.6500      1         0     1.0  1.0
+    10   47.000000      1      0  227.5250      2         1     1.0  0.0
+    147  29.532738      0      0   42.4000      0         0     1.0  0.0
+
+         pclass sex
+    772         0.0
+    543         0.0
+    289         1.0
+    10          0.0
+    147         0.0
+
 
 More details
 ^^^^^^^^^^^^
diff --git a/feature_engine/datasets/__init__.py b/feature_engine/datasets/__init__.py
new file mode 100644
index 0000000..93a2c62
--- /dev/null
+++ b/feature_engine/datasets/__init__.py
@@ -0,0 +1,3 @@
+from .titanic import load_titanic
+
+__all__ = ["load_titanic"]
diff --git a/feature_engine/datasets/titanic.py b/feature_engine/datasets/titanic.py
new file mode 100644
index 0000000..2e9e6df
--- /dev/null
+++ b/feature_engine/datasets/titanic.py
@@ -0,0 +1,113 @@
+import numpy as np
+import pandas as pd
+from sklearn.pipeline import Pipeline
+
+from feature_engine.imputation import CategoricalImputer, MeanMedianImputer
+
+
+def load_titanic(
+    return_X_y_frame=False, predictors_only=False, handle_missing=False, cabin=None
+):
+    """
+    The load_titanic() function returns the well-known titanic dataset.
+
+    Note that you need to have an internet connection for this function to work, as we
+    are calling the dataset stored in `openML <https://www.openml.org/d/40945>`_ which
+    can be downloaded from
+    `here <https://www.openml.org/data/get_csv/16826755/phpMYEkMl>`_.
+
+    Parameters
+    ----------
+    return_X_y_frame: bool, default=False
+        If `True`, it returns a DataFrame (X) with the predictors and a Series (y) with
+        the target variable. If `False`, it returns a single DataFrame with predictors
+        and target.
+
+    predictors_only: bool, default=False
+        If `False`, it returns all the variables from the original Titanic Dataset. If
+        `True`, it reurns only relevant predictors.
+
+    handle_missing: bool, default=False
+        If `False`, it returns the original dataset with missing values. If `True`,
+        missing data is replaced with the string "Missing" in categorical variables and
+        the mean in numerical variables.
+
+    cabin: str, default=None
+        If `None`, it returns the variable cabin as in the original data. If 'drop', it
+        removes the variable from the data. If 'letter_only' it returns just the first
+        letter of the cabin, without the number.
+
+    Examples
+    --------
+
+    >>> from feature_engine.datasets import load_titanic
+    >>> data = load_titanic(predictors_only=True, cabin="drop")
+    >>> print(data.head())
+       pclass  survived     sex      age  sibsp  parch      fare embarked
+    0       1         1  female  29.0000      0      0  211.3375        S
+    1       1         1    male   0.9167      1      2  151.5500        S
+    2       1         0  female   2.0000      1      2  151.5500        S
+    3       1         0    male  30.0000      1      2  151.5500        S
+    4       1         0  female  25.0000      1      2  151.5500        S
+    """
+    # param checks
+    if not isinstance(return_X_y_frame, bool):
+        raise ValueError(
+            "return_X_y_frame takes only booleans True and False. "
+            f"Got {return_X_y_frame} instead."
+        )
+
+    if not isinstance(predictors_only, bool):
+        raise ValueError(
+            "predictors_only takes only booleans True and False. "
+            f"Got {predictors_only} instead."
+        )
+
+    if not isinstance(handle_missing, bool):
+        raise ValueError(
+            "handle_missing takes only booleans True and False. "
+            f"Got {handle_missing} instead."
+        )
+
+    if cabin is not None:
+        if not isinstance(cabin, str) or cabin not in ["letter_only", "drop"]:
+            raise ValueError(
+                "the parameter 'cabin' takes only values None, 'letter_only' and "
+                f"'drop'. Got {cabin} instead."
+            )
+
+    # load and prepare data
+    df = pd.read_csv("https://www.openml.org/data/get_csv/16826755/phpMYEkMl")
+    df = df.replace("?", np.nan)
+    df["age"] = df["age"].astype("float64")
+    df["fare"] = df["fare"].astype("float64")
+
+    if predictors_only:
+        df = df.drop(
+            columns=["name", "ticket", "home.dest", "boat", "body"],
+        )
+
+    if handle_missing:
+        pipeline = Pipeline(
+            steps=[
+                (
+                    "categorical_imputer",
+                    CategoricalImputer(imputation_method="missing"),
+                ),
+                ("mean_median_imputer", MeanMedianImputer(imputation_method="mean")),
+            ]
+        )
+
+        df = pipeline.fit_transform(df)
+
+    if cabin == "letter_only":
+        df["cabin"] = df["cabin"].astype(str).str[0]
+    elif cabin == "drop":
+        df = df.drop(columns=["cabin"])
+
+    if return_X_y_frame:
+        X = df.drop(columns="survived")
+        y = df["survived"]
+        return X, y
+    else:
+        return df


Test patch:
diff --git a/tests/test_datasets/__init__().py b/tests/test_datasets/__init__().py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_datasets/test_datasets.py b/tests/test_datasets/test_datasets.py
new file mode 100644
index 0000000..6e98264
--- /dev/null
+++ b/tests/test_datasets/test_datasets.py
@@ -0,0 +1,91 @@
+import pytest
+from pandas import DataFrame
+
+from feature_engine.datasets import load_titanic
+
+
+def test_load_titanic():
+    data = load_titanic()
+    variables = [
+        "pclass",
+        "survived",
+        "name",
+        "sex",
+        "age",
+        "sibsp",
+        "parch",
+        "ticket",
+        "fare",
+        "cabin",
+        "embarked",
+        "boat",
+        "body",
+        "home.dest",
+    ]
+    assert isinstance(data, DataFrame)
+    assert data.shape == (1309, 14)
+    assert list(data.columns) == variables
+
+
+@pytest.mark.parametrize(
+    "handle_missing",
+    [True, False],
+)
+def test_titanic_datatypes(handle_missing):
+    data = load_titanic(handle_missing=handle_missing)
+    num_columns = ["pclass", "survived", "age", "sibsp", "parch", "fare"]
+    assert data[num_columns].dtypes.astype(str).isin(["int64", "float64"]).all()
+
+
+@pytest.mark.parametrize(
+    "predictors_only,expected_X,expected_y",
+    [(True, (1309, 8), (1309,)), (False, (1309, 13), (1309,))],
+)
+def test_return_X_y(predictors_only, expected_X, expected_y):
+    X, y = load_titanic(return_X_y_frame=True, predictors_only=predictors_only)
+    assert X.shape == expected_X
+    assert y.shape == expected_y
+
+
+@pytest.mark.parametrize(
+    "handle_missing,predictors_only, null_sum",
+    [(True, True, 0), (True, False, 0), (False, True, 1280), (False, False, 3855)],
+)
+def test_load_titanic_raw(handle_missing, predictors_only, null_sum):
+    X, y = load_titanic(
+        return_X_y_frame=True,
+        predictors_only=predictors_only,
+        handle_missing=handle_missing,
+    )
+
+    assert X.isnull().sum().sum() == null_sum
+
+
+@pytest.mark.parametrize("cabin", [None, "letter_only", "drop"])
+def test_cabin(cabin):
+
+    data = load_titanic(cabin=None)
+    assert "cabin" in data.columns
+    assert list(data["cabin"].head(4).values) == ["B5", "C22 C26", "C22 C26", "C22 C26"]
+
+    data = load_titanic(cabin="letter_only")
+    assert list(data["cabin"].head(4).values) == ["B", "C", "C", "C"]
+
+    data = load_titanic(cabin="drop")
+    assert "cabin" not in data.columns
+
+
+@pytest.mark.parametrize("param", ["Hello", [True], 1, 2.5])
+def test_raise_value_error_if_not_boolean(param):
+    with pytest.raises(ValueError):
+        load_titanic(return_X_y_frame=param)
+    with pytest.raises(ValueError):
+        load_titanic(predictors_only=param)
+    with pytest.raises(ValueError):
+        load_titanic(handle_missing=param)
+
+
+@pytest.mark.parametrize("cabin", ["Hello", True, 1, 2.5])
+def test_cabin_raise_value_error(cabin):
+    with pytest.raises(ValueError):
+        load_titanic(cabin=cabin)