88from sklearn .base import BaseEstimator
99from sklearn .datasets import make_classification , make_regression
1010from sklearn .dummy import DummyRegressor
11- from sklearn .linear_model import LogisticRegression
11+ from sklearn .linear_model import LogisticRegression , Ridge
1212from sklearn .model_selection import train_test_split
1313
1414import skrub
@@ -625,12 +625,6 @@ def test_concat_non_str_colname():
625625 )
626626
627627
628- def test_class_skb ():
629- from skrub ._data_ops ._skrub_namespace import SkrubNamespace
630-
631- assert skrub .DataOp .skb is SkrubNamespace
632-
633-
634628def test_get_vars ():
635629 a = skrub .var ("a" )
636630 b = skrub .var ("b" )
@@ -639,3 +633,98 @@ def test_get_vars():
639633 assert list (d .skb .get_vars ().keys ()) == ["a" , "b" ]
640634 assert d .skb .get_vars ()["a" ] is a
641635 assert list (d .skb .get_vars (all_named_ops = True ).keys ()) == ["a" , "b" , "c" ]
636+
637+
638+ @pytest .mark .parametrize ("needs_data" , [False , True ])
639+ @pytest .mark .parametrize ("has_preview" , [False , True ])
640+ @pytest .mark .parametrize ("regression" , [False , True ])
641+ @pytest .mark .parametrize ("with_scoring" , [False , True ])
642+ def test_estimator_is_a_data_op (needs_data , has_preview , regression , with_scoring ):
643+ # Check that the data_op, learner and search estimators behave well when
644+ # the estimator passed to apply is a data op
645+ if regression :
646+ X_a , y_a = make_regression (random_state = 0 )
647+ else :
648+ X_a , y_a = make_classification (random_state = 0 )
649+ X_df = pd .DataFrame (X_a ).rename (columns = str )
650+ if has_preview :
651+ X , y = skrub .X (X_df ), skrub .y (y_a )
652+ else :
653+ X , y = skrub .X (), skrub .y ()
654+ if needs_data :
655+ # In this case the estimator's automated preview cannot be computed
656+ # because it needs a value from the environment, the value is not known
657+ # until we fit the learner.
658+
659+ def get_vectorizer (X ):
660+ return skrub .TableVectorizer ()
661+
662+ vectorizer = X .skb .apply_func (get_vectorizer )
663+
664+ def get_predictor (X ):
665+ return Ridge () if regression else LogisticRegression ()
666+
667+ predictor = X .skb .apply_func (get_predictor )
668+ else :
669+ # In this case the estimator can be evaluated in the automated preview
670+ # when the data op is created.
671+ vectorizer = skrub .as_data_op (skrub .TableVectorizer ())
672+ predictor = skrub .as_data_op (Ridge () if regression else LogisticRegression ())
673+ pred = X .skb .apply (vectorizer ).skb .apply (predictor , y = y )
674+ # no information about the estimator: we expose all methods and default to
675+ # 'transformer' estimator type.
676+ learner = pred .skb .make_learner ()
677+ assert learner .__skrub_to_Xy_pipeline__ ({})._estimator_type == "transformer"
678+ assert hasattr (learner , "predict" )
679+ assert hasattr (pred .skb .make_randomized_search (), "predict" )
680+ if has_preview :
681+ assert pred .skb .preview ().shape == y_a .shape
682+ env = {"X" : X_df , "y" : y_a }
683+ assert pred .skb .eval (env ).shape == y_a .shape
684+ search = pred .skb .make_grid_search (cv = 2 ).fit (env )
685+ min_score = 0.3 if regression else 0.7
686+ assert search .best_score_ > min_score
687+ if with_scoring :
688+ scoring = "r2" if regression else "accuracy"
689+ else :
690+ scoring = None
691+ res = skrub .cross_validate (
692+ pred .skb .make_grid_search (cv = 2 , scoring = scoring ),
693+ environment = env ,
694+ cv = 2 ,
695+ scoring = scoring ,
696+ )
697+ assert res ["test_score" ].mean () > min_score
698+
699+
700+ def test_apply_no_sklearn_tags ():
701+ # applying an estimator that does not define __sklearn_tags__
702+ class Twice :
703+ def fit (self , X , y = None ):
704+ return self
705+
706+ def fit_transform (self , X , y = None ):
707+ return X * 2
708+
709+ def transform (self , X ):
710+ return X * 2
711+
712+ def get_params (self , deep = True ):
713+ return {}
714+
715+ def set_params (self ):
716+ return self
717+
718+ learner = skrub .var ("a" ).skb .apply (Twice ()).skb .make_learner ()
719+ assert learner .fit_transform ({"a" : 1 }) == 2
720+ xy_learner = learner .__skrub_to_Xy_pipeline__ ({})
721+ assert xy_learner ._estimator_type == "transformer"
722+ if hasattr (xy_learner , "__sklearn_tags__" ):
723+ # Old scikit-learn versiond don't have __sklearn_tags__
724+ assert xy_learner .__sklearn_tags__ ().estimator_type is None
725+
726+
727+ def test_class_skb ():
728+ from skrub ._data_ops ._skrub_namespace import SkrubNamespace
729+
730+ assert skrub .DataOp .skb is SkrubNamespace
0 commit comments