Repository: dirty-cat/dirty_cat
Pull request #461 (2023-01-16 15:37:36)

MAINT Prepare for beta version 0.4.0


Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index fad8b66..da0935d 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -5,8 +5,8 @@
 Release 0.5.0
 ==============
 
-Release 0.4.0 (upcoming)
-=========================
+Release 0.4.0 (beta)
+====================
 
 Major changes
 -------------
diff --git a/dirty_cat/VERSION.txt b/dirty_cat/VERSION.txt
index ca91a08..88b0e14 100644
--- a/dirty_cat/VERSION.txt
+++ b/dirty_cat/VERSION.txt
@@ -1,1 +1,1 @@
-0.4.dev0
+0.4.0b1
diff --git a/dirty_cat/_gap_encoder.py b/dirty_cat/_gap_encoder.py
index a0dc6d4..5c5298b 100644
--- a/dirty_cat/_gap_encoder.py
+++ b/dirty_cat/_gap_encoder.py
@@ -30,6 +30,7 @@ from sklearn.neighbors import NearestNeighbors
 from sklearn.utils import check_random_state, gen_batches
 from sklearn.utils.extmath import row_norms, safe_sparse_dot
 from sklearn.utils.fixes import _object_dtype_isnan
+from sklearn.utils.validation import check_is_fitted
 
 from ._utils import check_input, parse_version
 
@@ -512,6 +513,7 @@ class GapEncoderColumn(BaseEstimator, TransformerMixin):
         H : 2-d array, shape (n_samples, n_topics)
             Transformed input.
         """
+        check_is_fitted(self, "H_dict_")
         # Check if first item has str or np.str_ type
         assert isinstance(X[0], str), "Input data is not string. "
         unq_X = np.unique(X)
@@ -810,7 +812,7 @@ class GapEncoder(BaseEstimator, TransformerMixin):
         H : 2-d array, shape (n_samples, n_topics * n_features)
             Transformed input.
         """
-
+        check_is_fitted(self, "fitted_models_")
         # Check input data shape
         X = check_input(X)
         X = self._handle_missing(X)
diff --git a/dirty_cat/_minhash_encoder.py b/dirty_cat/_minhash_encoder.py
index f45d4b8..4a56562 100644
--- a/dirty_cat/_minhash_encoder.py
+++ b/dirty_cat/_minhash_encoder.py
@@ -22,6 +22,7 @@ import numpy as np
 from joblib import Parallel, delayed, effective_n_jobs
 from sklearn.base import BaseEstimator, TransformerMixin
 from sklearn.utils import gen_even_slices, murmurhash3_32
+from sklearn.utils.validation import check_is_fitted
 
 from ._fast_hash import ngram_min_hash
 from ._string_distances import get_unique_ngrams
@@ -261,6 +262,7 @@ class MinHashEncoder(BaseEstimator, TransformerMixin):
         :obj:`~numpy.ndarray` of shape (n_samples, n_components)
             Transformed input.
         """
+        check_is_fitted(self, "hash_dict_")
         X = check_input(X)
         if self.minmax_hash:
             if self.n_components % 2 != 0:
diff --git a/dirty_cat/_similarity_encoder.py b/dirty_cat/_similarity_encoder.py
index 6367981..b6d37a9 100644
--- a/dirty_cat/_similarity_encoder.py
+++ b/dirty_cat/_similarity_encoder.py
@@ -27,6 +27,7 @@ from sklearn.neighbors import NearestNeighbors
 from sklearn.preprocessing import OneHotEncoder
 from sklearn.utils import check_random_state
 from sklearn.utils.fixes import _object_dtype_isnan
+from sklearn.utils.validation import check_is_fitted
 
 from ._string_distances import get_ngram_count, preprocess
 from ._utils import parse_version
@@ -521,7 +522,7 @@ class SimilarityEncoder(OneHotEncoder):
         X_new : 2-d array, shape [n_samples, n_features_new]
             Transformed input.
         """
-
+        check_is_fitted(self, "categories_")
         if hasattr(X, "iloc") and X.isna().values.any():
             if self.handle_missing == "error":
                 raise ValueError(
diff --git a/dirty_cat/_super_vectorizer.py b/dirty_cat/_super_vectorizer.py
index 97d7d95..107e1e7 100644
--- a/dirty_cat/_super_vectorizer.py
+++ b/dirty_cat/_super_vectorizer.py
@@ -15,6 +15,7 @@ from sklearn import __version__ as sklearn_version
 from sklearn.base import TransformerMixin, clone
 from sklearn.compose import ColumnTransformer
 from sklearn.preprocessing import OneHotEncoder
+from sklearn.utils.validation import check_is_fitted
 
 from dirty_cat import DatetimeEncoder, GapEncoder
 from dirty_cat._utils import parse_version
@@ -607,6 +608,7 @@ class SuperVectorizer(ColumnTransformer):
             any result is a sparse matrix, everything will be converted to
             sparse matrices.
         """
+        check_is_fitted(self, attributes=["columns_"])
         if X.shape[1] != len(self.columns_):
             raise ValueError(
                 "Passed array does not match column count of "


Test patch:
diff --git a/dirty_cat/tests/test_datetime_encoder.py b/dirty_cat/tests/test_datetime_encoder.py
index 22705ea..124ae9d 100644
--- a/dirty_cat/tests/test_datetime_encoder.py
+++ b/dirty_cat/tests/test_datetime_encoder.py
@@ -1,5 +1,7 @@
+import pytest
 import numpy as np
 import pandas as pd
+from sklearn.exceptions import NotFittedError
 
 from dirty_cat._datetime_encoder import DatetimeEncoder
 
@@ -340,3 +342,14 @@ def test_transform():
     X = get_constant_date_array()
     enc = DatetimeEncoder(add_day_of_the_week=True)
     assert enc.fit_transform(X).shape[1] == 0
+
+def test_check_fitted_datetime_encoder():
+    """Test that calling transform before fit raises an error"""
+    X = get_datetime_array()[:, 0].reshape(-1, 1)
+    enc = DatetimeEncoder(add_day_of_the_week=True)
+    with pytest.raises(NotFittedError):
+        enc.transform(X)
+    
+    # Check that it works after fit
+    enc.fit(X)
+    enc.transform(X)
diff --git a/dirty_cat/tests/test_gap_encoder.py b/dirty_cat/tests/test_gap_encoder.py
index e9be30f..e39e798 100644
--- a/dirty_cat/tests/test_gap_encoder.py
+++ b/dirty_cat/tests/test_gap_encoder.py
@@ -2,6 +2,7 @@ import numpy as np
 import pandas as pd
 import pytest
 from sklearn import __version__ as sklearn_version
+from sklearn.exceptions import NotFittedError
 
 from dirty_cat import GapEncoder
 from dirty_cat._utils import parse_version
@@ -195,3 +196,14 @@ def test_missing_values(missing: str) -> None:
             r"'error' or 'zero_impute', got 'aaa'",
         ):
             enc.fit_transform(observations)
+
+def test_check_fitted_gap_encoder():
+    """Test that calling transform before fit raises an error"""
+    X = np.array([["alice"], ["bob"]])
+    enc = GapEncoder(n_components=2, random_state=42)
+    with pytest.raises(NotFittedError):
+        enc.transform(X)
+    
+    # Check that it works after fit
+    enc.fit(X)
+    enc.transform(X)
diff --git a/dirty_cat/tests/test_minhash_encoder.py b/dirty_cat/tests/test_minhash_encoder.py
index e88d380..95617c4 100644
--- a/dirty_cat/tests/test_minhash_encoder.py
+++ b/dirty_cat/tests/test_minhash_encoder.py
@@ -6,6 +6,7 @@ import numpy as np
 import pandas as pd
 import pytest
 from sklearn.utils._testing import assert_array_equal, skip_if_no_parallel
+from sklearn.exceptions import NotFittedError
 
 from dirty_cat import MinHashEncoder
 
@@ -242,3 +243,15 @@ def test_correct_arguments():
     with pytest.raises(ValueError, match=r"n_components should be even"):
         encoder = MinHashEncoder(n_components=3, minmax_hash=True)
         encoder.fit_transform(X)
+
+def test_check_fitted_minhash_encoder():
+    """Test that calling transform before fit raises an error"""
+    encoder = MinHashEncoder(n_components=3)
+    X = np.array(["a", "b", "c", "d", "e", "f", "g", "h"])[:, None]
+    with pytest.raises(NotFittedError):
+        encoder.transform(X)
+    
+    # Check that it works after fitting
+    encoder.fit(X)
+    encoder.transform(X)
+
diff --git a/dirty_cat/tests/test_similarity_encoder.py b/dirty_cat/tests/test_similarity_encoder.py
index 292c7c2..51bc929 100644
--- a/dirty_cat/tests/test_similarity_encoder.py
+++ b/dirty_cat/tests/test_similarity_encoder.py
@@ -4,6 +4,7 @@ import numpy as np
 import numpy.testing
 import pytest
 from sklearn import __version__ as sklearn_version
+from sklearn.exceptions import NotFittedError
 
 from dirty_cat import SimilarityEncoder
 from dirty_cat._similarity_encoder import get_kmeans_prototypes, ngram_similarity_matrix
@@ -385,3 +386,12 @@ def test_get_features() -> None:
         "x0_}",
         "x0_~",
     ]
+
+def test_check_fitted_super_vectorizer():
+    """Test that calling transform before fit raises an error"""
+    sim_enc = SimilarityEncoder()
+    X = np.array(["%s" % chr(i) for i in range(32, 127)]).reshape((-1, 1))
+    with pytest.raises(NotFittedError):
+        sim_enc.transform(X)
+    sim_enc.fit(X)
+    sim_enc.transform(X)
diff --git a/dirty_cat/tests/test_super_vectorizer.py b/dirty_cat/tests/test_super_vectorizer.py
index ce600c5..0806c0d 100644
--- a/dirty_cat/tests/test_super_vectorizer.py
+++ b/dirty_cat/tests/test_super_vectorizer.py
@@ -7,6 +7,7 @@ import sklearn
 from sklearn.exceptions import NotFittedError
 from sklearn.preprocessing import StandardScaler
 from sklearn.utils.validation import check_is_fitted
+from sklearn.exceptions import NotFittedError
 
 from dirty_cat import GapEncoder, SuperVectorizer
 from dirty_cat._utils import parse_version
@@ -445,3 +446,15 @@ def test_passthrough():
     dirty_flat_trans_df = X_enc_dirty.to_numpy().ravel().tolist()
     assert all(map(_is_equal, zip(dirty_flat_df, dirty_flat_trans_df)))
     assert (X_clean.to_numpy() == X_enc_clean.to_numpy()).all()
+
+def test_check_fitted_super_vectorizer():
+    """Test that calling transform before fit raises an error"""
+    X = _get_clean_dataframe()
+    sv = SuperVectorizer()
+    with pytest.raises(NotFittedError):
+        sv.transform(X)
+    
+    # Test that calling transform after fit works
+    sv.fit(X)
+    sv.transform(X)
+
diff --git a/dirty_cat/tests/test_target_encoder.py b/dirty_cat/tests/test_target_encoder.py
index e45b396..cf64192 100644
--- a/dirty_cat/tests/test_target_encoder.py
+++ b/dirty_cat/tests/test_target_encoder.py
@@ -1,5 +1,6 @@
 import numpy as np
 import pytest
+from sklearn.exceptions import NotFittedError
 
 from dirty_cat import _target_encoder
 
@@ -275,3 +276,15 @@ def test_errors():
         enc4.transform(X2)
     with pytest.raises(ValueError, match="Found unknown categories "):
         enc4.transform(X3)
+
+def test_check_fitted_target_encoder():
+    """Test that calling transform before fit raises an error"""
+    enc = _target_encoder.TargetEncoder()
+    X = [[1], [2], [2], [3], [4]]
+    y = np.array([1, 2, 3, 4, 5])
+
+    with pytest.raises(NotFittedError, match="This TargetEncoder instance is not"):
+        enc.transform(X)
+    
+    enc.fit(X, y)
+    enc.transform(X)