Repository: dirty-cat/dirty_cat

Issue:
Better error message when SuperVectorizer's transform method is called before fit
Scikit-learn has a mechanism to check that an estimator is fitted, and if not raise a good error message. We should use it.

Comments:

Patch:
diff --git a/dirty_cat/super_vectorizer.py b/dirty_cat/super_vectorizer.py
index 84b39c9..420d00b 100644
--- a/dirty_cat/super_vectorizer.py
+++ b/dirty_cat/super_vectorizer.py
@@ -106,10 +106,9 @@ class SuperVectorizer(ColumnTransformer):
         None to apply `remainder`, 'drop' for dropping the columns,
         or 'passthrough' to return the unencoded columns.
 
-    auto_cast: bool, default=False
+    auto_cast: bool, default=True
         If set to `True`, will try to convert each column to the best possible
         data type (dtype).
-        Experimental. It is advised to cast them beforehand, and leave this false.
 
     handle_missing: str, default=''
         One of the following values: 'error' or '' (empty).
@@ -144,7 +143,7 @@ class SuperVectorizer(ColumnTransformer):
                  high_card_cat_transformer: Optional[Union[BaseEstimator, str]] = GapEncoder(),
                  numerical_transformer: Optional[Union[BaseEstimator, str]] = None,
                  datetime_transformer: Optional[Union[BaseEstimator, str]] = None,
-                 auto_cast: bool = False,
+                 auto_cast: bool = True,
                  # Following parameters are inherited from ColumnTransformer
                  handle_missing: str = '',
                  remainder='passthrough',
@@ -171,8 +170,6 @@ class SuperVectorizer(ColumnTransformer):
         self.transformer_weights = transformer_weights
         self.verbose = verbose
 
-        self.columns_ = []
-
     def _auto_cast_array(self, X):
         """
         Takes an array and tries to convert its columns to the best possible
diff --git a/examples/03_automatic_preprocessing_with_the_supervectorizer.py b/examples/03_automatic_preprocessing_with_the_supervectorizer.py
index 69ed18a..b9f6d88 100644
--- a/examples/03_automatic_preprocessing_with_the_supervectorizer.py
+++ b/examples/03_automatic_preprocessing_with_the_supervectorizer.py
@@ -82,7 +82,7 @@ from sklearn.pipeline import Pipeline
 from dirty_cat import SuperVectorizer
 
 pipeline = Pipeline([
-    ('vectorizer', SuperVectorizer(auto_cast=True)),
+    ('vectorizer', SuperVectorizer()),
     ('clf', HistGradientBoostingRegressor(random_state=42))
 ])
 
@@ -111,7 +111,6 @@ print(f'std={np.std(scores)}')
 # Let us perform the same workflow, but without the `Pipeline`, so we can
 # analyze its mechanisms along the way.
 sup_vec = SuperVectorizer(
-    auto_cast=True,
     high_card_str_transformer=GapEncoder(n_components=50),
     high_card_cat_transformer=GapEncoder(n_components=50)
 )


Test patch:
diff --git a/dirty_cat/test/test_super_vectorizer.py b/dirty_cat/test/test_super_vectorizer.py
index 2f34d8e..b70a26b 100644
--- a/dirty_cat/test/test_super_vectorizer.py
+++ b/dirty_cat/test/test_super_vectorizer.py
@@ -3,6 +3,8 @@ import sklearn
 import pandas as pd
 
 from sklearn.preprocessing import StandardScaler
+from sklearn.utils.validation import check_is_fitted
+from sklearn.exceptions import NotFittedError
 
 from distutils.version import LooseVersion
 
@@ -79,7 +81,6 @@ def test_super_vectorizer():
     # Test casting values
     vectorizer_cast = SuperVectorizer(
         cardinality_threshold=3,
-        auto_cast=True,
         # we must have n_samples = 5 >= n_components
         high_card_str_transformer=GapEncoder(n_components=2),
         high_card_cat_transformer=GapEncoder(n_components=2),
@@ -130,6 +131,18 @@ def test_get_feature_names():
     assert vectorizer_w_drop.get_feature_names() == expected_feature_names_drop
 
 
+def test_fit():
+    # Simply checks sklearn's `check_is_fitted` function raises an error if
+    # the SuperVectorizer is instantiated but not fitted.
+    # See GH#193
+    sup_vec = SuperVectorizer()
+    with pytest.raises(NotFittedError):
+        if LooseVersion(sklearn.__version__) >= LooseVersion('0.22'):
+            assert check_is_fitted(sup_vec)
+        else:
+            assert check_is_fitted(sup_vec, attributes=dir(sup_vec))
+
+
 if __name__ == '__main__':
     print('start test_super_vectorizer')
     test_super_vectorizer()
@@ -137,5 +150,8 @@ if __name__ == '__main__':
     print('start test_get_feature_names')
     test_get_feature_names()
     print('test_get_feature_names passed')
+    print('start test_fit')
+    test_fit()
+    print('test_fit passed')
 
     print('Done')