From d9c63d0da96514fce7e05fb2c6d72a1c4a9e5a15 Mon Sep 17 00:00:00 2001 From: angela97lin Date: Tue, 11 Aug 2020 17:49:17 -0400 Subject: [PATCH 1/6] init --- evalml/pipelines/components/component_base.py | 9 ++++++--- .../components/transformers/column_selectors.py | 2 ++ evalml/tests/component_tests/test_components.py | 7 +++---- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/evalml/pipelines/components/component_base.py b/evalml/pipelines/components/component_base.py index a751035310..42f0d0dbb8 100644 --- a/evalml/pipelines/components/component_base.py +++ b/evalml/pipelines/components/component_base.py @@ -22,8 +22,6 @@ class ComponentBaseMeta(ABCMeta): """Metaclass that overrides creating a new component by wrapping method with validators and setters""" from evalml.exceptions import ComponentNotYetFittedError - NO_FITTING_REQUIRED = ['DropColumns', 'SelectColumns'] - @classmethod def set_fit(cls, method): @wraps(method) @@ -41,7 +39,7 @@ def check_for_fit(cls, method): @wraps(method) def _check_for_fit(self, X=None, y=None): klass = type(self).__name__ - if not self._is_fitted and klass not in cls.NO_FITTING_REQUIRED: + if not self._is_fitted and self.needs_fitting: raise ComponentNotYetFittedError(f'This {klass} is not fitted yet. You must fit {klass} before calling {method.__name__}.') elif X is None and y is None: return method(self) @@ -91,6 +89,11 @@ def name(cls): def model_family(cls): """Returns ModelFamily of this component""" + @classproperty + def needs_fitting(self): + """Returns boolean determining if component needs fitting before making predictions""" + return True + @property def parameters(self): """Returns the parameters which were used to initialize the component""" diff --git a/evalml/pipelines/components/transformers/column_selectors.py b/evalml/pipelines/components/transformers/column_selectors.py index adbda50d70..b71cef566d 100644 --- a/evalml/pipelines/components/transformers/column_selectors.py +++ b/evalml/pipelines/components/transformers/column_selectors.py @@ -85,6 +85,7 @@ class DropColumns(ColumnSelector): """Drops specified columns in input data.""" name = "Drop Columns Transformer" hyperparameter_ranges = {} + needs_fitting = False def _modify_columns(self, cols, X, y=None): return X.drop(columns=cols, axis=1) @@ -106,6 +107,7 @@ class SelectColumns(ColumnSelector): """Selects specified columns in input data.""" name = "Select Columns Transformer" hyperparameter_ranges = {} + needs_fitting = False def _modify_columns(self, cols, X, y=None): return X[cols] diff --git a/evalml/tests/component_tests/test_components.py b/evalml/tests/component_tests/test_components.py index fffc7dae59..05acb6b2cb 100644 --- a/evalml/tests/component_tests/test_components.py +++ b/evalml/tests/component_tests/test_components.py @@ -15,7 +15,6 @@ from evalml.model_family import ModelFamily from evalml.pipelines.components import ( ComponentBase, - ComponentBaseMeta, DropColumns, ElasticNetClassifier, ElasticNetRegressor, @@ -648,7 +647,7 @@ def transform(self, X): def test_all_transformers_check_fit(X_y_binary): X, y = X_y_binary for component_class in _all_transformers: - if component_class.__name__ in ComponentBaseMeta.NO_FITTING_REQUIRED: + if not component_class.needs_fitting: continue component = component_class() @@ -666,7 +665,7 @@ def test_all_transformers_check_fit(X_y_binary): def test_all_estimators_check_fit(X_y_binary): X, y = X_y_binary for component_class in _all_estimators: - if component_class.__name__ in ComponentBaseMeta.NO_FITTING_REQUIRED: + if not component_class.needs_fitting: continue component = component_class() @@ -692,7 +691,7 @@ def test_all_estimators_check_fit(X_y_binary): def test_no_fitting_required_components(X_y_binary): X, y = X_y_binary for component_class in all_components: - if component_class.__name__ in ComponentBaseMeta.NO_FITTING_REQUIRED: + if not component_class.needs_fitting: component = component_class() if issubclass(component_class, Estimator): component.predict(X) From 0d0cb6f45f74ae8347d2ba2bbfc10b3ad20ce684 Mon Sep 17 00:00:00 2001 From: angela97lin Date: Tue, 11 Aug 2020 18:14:55 -0400 Subject: [PATCH 2/6] release notes --- docs/source/release_notes.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/release_notes.rst b/docs/source/release_notes.rst index 6da96e7001..1dbbb5a630 100644 --- a/docs/source/release_notes.rst +++ b/docs/source/release_notes.rst @@ -10,6 +10,7 @@ Release Notes * Updated TextFeaturizer component to no longer require an internet connection to run :pr:`1022` * Fixed non-deterministic element of TextFeaturizer transformations :pr:`1022` * Changes + * Added `needs_fitting` property to ComponentBase :pr:`1044` * Documentation Changes * Update setup.py URL to point to the github repo :pr:`1037` * Testing Changes From fef3b043b1ffad041e6f0bb373b6b19409a74204 Mon Sep 17 00:00:00 2001 From: Angela Lin Date: Wed, 12 Aug 2020 13:36:24 -0400 Subject: [PATCH 3/6] add test --- evalml/tests/component_tests/test_components.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/evalml/tests/component_tests/test_components.py b/evalml/tests/component_tests/test_components.py index 05acb6b2cb..e29fa74e4c 100644 --- a/evalml/tests/component_tests/test_components.py +++ b/evalml/tests/component_tests/test_components.py @@ -644,6 +644,14 @@ def transform(self, X): transformer_subclass.transform(X) +def test_all_transformers_needs_fitting(): + for component_class in _all_transformers + _all_estimators: + if component_class.__name__ in ['DropColumns', 'SelectColumns']: + assert not component_class.needs_fitting + else: + assert component_class.needs_fitting + + def test_all_transformers_check_fit(X_y_binary): X, y = X_y_binary for component_class in _all_transformers: From 64b330fdc71af3a7fc53fec45ac86d6800ced853 Mon Sep 17 00:00:00 2001 From: Angela Lin Date: Wed, 12 Aug 2020 15:48:35 -0400 Subject: [PATCH 4/6] update docstr --- evalml/pipelines/components/component_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evalml/pipelines/components/component_base.py b/evalml/pipelines/components/component_base.py index 42f0d0dbb8..d8638dc4b9 100644 --- a/evalml/pipelines/components/component_base.py +++ b/evalml/pipelines/components/component_base.py @@ -91,7 +91,7 @@ def model_family(cls): @classproperty def needs_fitting(self): - """Returns boolean determining if component needs fitting before making predictions""" + """Returns boolean determining if component needs fitting before calling predict, predict_proba, transform, or feature_importances.""" return True @property From c764fc2b3de75e081272c2696f73de3496687d90 Mon Sep 17 00:00:00 2001 From: Angela Lin Date: Wed, 12 Aug 2020 23:45:55 -0400 Subject: [PATCH 5/6] update docstr --- evalml/pipelines/components/component_base.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/evalml/pipelines/components/component_base.py b/evalml/pipelines/components/component_base.py index d8638dc4b9..1f99a597ac 100644 --- a/evalml/pipelines/components/component_base.py +++ b/evalml/pipelines/components/component_base.py @@ -91,7 +91,10 @@ def model_family(cls): @classproperty def needs_fitting(self): - """Returns boolean determining if component needs fitting before calling predict, predict_proba, transform, or feature_importances.""" + """Returns boolean determining if component needs fitting before + calling predict, predict_proba, transform, or feature_importances. + This can be overridden to False for components that do not need to be fit + or whose fit methods do nothing.""" return True @property From 1088a1bada9f1dbc4fdab697f3a48d3df7cca1ef Mon Sep 17 00:00:00 2001 From: Angela Lin Date: Thu, 13 Aug 2020 00:14:13 -0400 Subject: [PATCH 6/6] empty for codecov