From fdf31e76adfe9514ea589caa2c639577cb764286 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 27 Mar 2023 11:41:53 +0200 Subject: [PATCH 1/6] feat: `fit` method of `Classifier`s returns fitted `Classifier` --- src/safeds/ml/_util_sklearn.py | 4 +++ src/safeds/ml/classification/_ada_boost.py | 25 +++++++++++++------ src/safeds/ml/classification/_classifier.py | 11 ++++++-- .../ml/classification/_decision_tree.py | 25 +++++++++++++------ .../_gradient_boosting_classification.py | 21 ++++++++++++---- .../ml/classification/_k_nearest_neighbors.py | 24 ++++++++++++------ .../ml/classification/_logistic_regression.py | 20 +++++++++++---- .../ml/classification/_random_forest.py | 20 +++++++++++---- .../ml/classification/test_ada_boost.py | 8 +++--- .../ml/classification/test_decision_tree.py | 8 +++--- .../classification/test_gradient_boosting.py | 8 +++--- .../test_k_nearest_neighbors.py | 8 +++--- .../test_logistic_regression.py | 8 +++--- .../ml/classification/test_random_forest.py | 8 +++--- 14 files changed, 136 insertions(+), 62 deletions(-) diff --git a/src/safeds/ml/_util_sklearn.py b/src/safeds/ml/_util_sklearn.py index 5de4708b0..fdf194af7 100644 --- a/src/safeds/ml/_util_sklearn.py +++ b/src/safeds/ml/_util_sklearn.py @@ -57,6 +57,10 @@ def predict(model: Any, dataset: Table, target_name: str) -> TaggedTable: PredictionError If predicting with the given dataset failed. """ + + if model is None: + raise PredictionError("The model was not trained") + dataset_df = dataset._data dataset_df.columns = dataset.schema.get_column_names() try: diff --git a/src/safeds/ml/classification/_ada_boost.py b/src/safeds/ml/classification/_ada_boost.py index 55a503b9b..1cbae2048 100644 --- a/src/safeds/ml/classification/_ada_boost.py +++ b/src/safeds/ml/classification/_ada_boost.py @@ -1,7 +1,11 @@ -from safeds.data.tabular.containers import Table, TaggedTable -from safeds.ml._util_sklearn import fit, predict +from __future__ import annotations + +from typing import Optional + from sklearn.ensemble import AdaBoostClassifier as sk_AdaBoostClassifier +from safeds.data.tabular.containers import Table, TaggedTable +from safeds.ml._util_sklearn import fit, predict from ._classifier import Classifier @@ -12,10 +16,10 @@ class AdaBoost(Classifier): """ def __init__(self) -> None: - self._wrapped_classifier = sk_AdaBoostClassifier() - self._target_name = "" + self._wrapped_classifier: Optional[sk_AdaBoostClassifier] = None + self._target_name: Optional[str] = None - def fit(self, training_set: TaggedTable) -> None: + def fit(self, training_set: TaggedTable) -> AdaBoost: """ Fit this model given a tagged table. @@ -29,8 +33,15 @@ def fit(self, training_set: TaggedTable) -> None: LearningError If the tagged table contains invalid values or if the training failed. """ - fit(self._wrapped_classifier, training_set) - self._target_name = training_set.target.name + + wrapped_classifier = sk_AdaBoostClassifier() + fit(wrapped_classifier, training_set) + + result = AdaBoost() + result._wrapped_classifier = wrapped_classifier + result._target_name = training_set.target.name + + return result def predict(self, dataset: Table) -> TaggedTable: """ diff --git a/src/safeds/ml/classification/_classifier.py b/src/safeds/ml/classification/_classifier.py index ef4c59710..a2d0af97a 100644 --- a/src/safeds/ml/classification/_classifier.py +++ b/src/safeds/ml/classification/_classifier.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from abc import ABC, abstractmethod from safeds.data.tabular.containers import Table, TaggedTable @@ -10,15 +12,20 @@ class Classifier(ABC): """ @abstractmethod - def fit(self, training_set: TaggedTable) -> None: + def fit(self, training_set: TaggedTable) -> Classifier: """ - Fit this model given a tagged table. + Fit this classifier given a tagged table and return the fitted classifier. Parameters ---------- training_set : TaggedTable The tagged table containing the feature and target vectors. + Returns + ------- + fitted_classifier : Classifier + The fitted classifier. + Raises ------ LearningError diff --git a/src/safeds/ml/classification/_decision_tree.py b/src/safeds/ml/classification/_decision_tree.py index 317549b17..b769250bc 100644 --- a/src/safeds/ml/classification/_decision_tree.py +++ b/src/safeds/ml/classification/_decision_tree.py @@ -1,7 +1,11 @@ -from safeds.data.tabular.containers import Table, TaggedTable -from safeds.ml._util_sklearn import fit, predict +from __future__ import annotations + +from typing import Optional + from sklearn.tree import DecisionTreeClassifier as sk_DecisionTreeClassifier +from safeds.data.tabular.containers import Table, TaggedTable +from safeds.ml._util_sklearn import fit, predict from ._classifier import Classifier @@ -12,10 +16,10 @@ class DecisionTree(Classifier): """ def __init__(self) -> None: - self._wrapped_classifier = sk_DecisionTreeClassifier() - self._target_name = "" + self._wrapped_classifier: Optional[sk_DecisionTreeClassifier] = None + self._target_name: Optional[str] = None - def fit(self, training_set: TaggedTable) -> None: + def fit(self, training_set: TaggedTable) -> DecisionTree: """ Fit this model given a tagged table. @@ -29,8 +33,15 @@ def fit(self, training_set: TaggedTable) -> None: LearningError If the tagged table contains invalid values or if the training failed. """ - fit(self._wrapped_classifier, training_set) - self._target_name = training_set.target.name + + wrapped_classifier = sk_DecisionTreeClassifier() + fit(wrapped_classifier, training_set) + + result = DecisionTree() + result._wrapped_classifier = wrapped_classifier + result._target_name = training_set.target.name + + return result def predict(self, dataset: Table) -> TaggedTable: """ diff --git a/src/safeds/ml/classification/_gradient_boosting_classification.py b/src/safeds/ml/classification/_gradient_boosting_classification.py index 5d80292a6..24326a94a 100644 --- a/src/safeds/ml/classification/_gradient_boosting_classification.py +++ b/src/safeds/ml/classification/_gradient_boosting_classification.py @@ -1,3 +1,7 @@ +from __future__ import annotations + +from typing import Optional + from safeds.data.tabular.containers import Table, TaggedTable from safeds.ml._util_sklearn import fit, predict from sklearn.ensemble import GradientBoostingClassifier as sk_GradientBoostingClassifier @@ -12,10 +16,10 @@ class GradientBoosting(Classifier): """ def __init__(self) -> None: - self._wrapped_classifier = sk_GradientBoostingClassifier() - self._target_name = "" + self._wrapped_classifier: Optional[sk_GradientBoostingClassifier] = None + self._target_name: Optional[str] = None - def fit(self, training_set: TaggedTable) -> None: + def fit(self, training_set: TaggedTable) -> GradientBoosting: """ Fit this model given a tagged table. @@ -29,8 +33,15 @@ def fit(self, training_set: TaggedTable) -> None: LearningError If the tagged table contains invalid values or if the training failed. """ - fit(self._wrapped_classifier, training_set) - self._target_name = training_set.target.name + + wrapped_classifier = sk_GradientBoostingClassifier() + fit(wrapped_classifier, training_set) + + result = GradientBoosting() + result._wrapped_classifier = wrapped_classifier + result._target_name = training_set.target.name + + return result # noinspection PyProtectedMember def predict(self, dataset: Table) -> TaggedTable: diff --git a/src/safeds/ml/classification/_k_nearest_neighbors.py b/src/safeds/ml/classification/_k_nearest_neighbors.py index 2cb16a612..71f1b2520 100644 --- a/src/safeds/ml/classification/_k_nearest_neighbors.py +++ b/src/safeds/ml/classification/_k_nearest_neighbors.py @@ -1,3 +1,7 @@ +from __future__ import annotations + +from typing import Optional + from safeds.data.tabular.containers import Table, TaggedTable from safeds.ml._util_sklearn import fit, predict from sklearn.neighbors import KNeighborsClassifier as sk_KNeighborsClassifier @@ -16,12 +20,12 @@ class KNearestNeighbors(Classifier): """ def __init__(self, n_neighbors: int) -> None: - self._wrapped_classifier = sk_KNeighborsClassifier( - n_jobs=-1, n_neighbors=n_neighbors - ) - self._target_name = "" + self._n_neighbors = n_neighbors - def fit(self, training_set: TaggedTable) -> None: + self._wrapped_classifier: Optional[sk_KNeighborsClassifier] = None + self._target_name: Optional[str] = None + + def fit(self, training_set: TaggedTable) -> KNearestNeighbors: """ Fit this model given a tagged table. @@ -35,8 +39,14 @@ def fit(self, training_set: TaggedTable) -> None: LearningError If the tagged table contains invalid values or if the training failed. """ - fit(self._wrapped_classifier, training_set) - self._target_name = training_set.target.name + wrapped_classifier = sk_KNeighborsClassifier(self._n_neighbors, n_jobs=-1) + fit(wrapped_classifier, training_set) + + result = KNearestNeighbors(self._n_neighbors) + result._wrapped_classifier = wrapped_classifier + result._target_name = training_set.target.name + + return result def predict(self, dataset: Table) -> TaggedTable: """ diff --git a/src/safeds/ml/classification/_logistic_regression.py b/src/safeds/ml/classification/_logistic_regression.py index c489591d3..7a8b94604 100644 --- a/src/safeds/ml/classification/_logistic_regression.py +++ b/src/safeds/ml/classification/_logistic_regression.py @@ -1,3 +1,7 @@ +from __future__ import annotations + +from typing import Optional + from safeds.data.tabular.containers import Table, TaggedTable from safeds.ml._util_sklearn import fit, predict from sklearn.linear_model import LogisticRegression as sk_LogisticRegression @@ -12,10 +16,10 @@ class LogisticRegression(Classifier): """ def __init__(self) -> None: - self._wrapped_classifier = sk_LogisticRegression(n_jobs=-1) - self._target_name = "" + self._wrapped_classifier: Optional[sk_LogisticRegression] = None + self._target_name: Optional[str] = None - def fit(self, training_set: TaggedTable) -> None: + def fit(self, training_set: TaggedTable) -> LogisticRegression: """ Fit this model given a tagged table. @@ -29,8 +33,14 @@ def fit(self, training_set: TaggedTable) -> None: LearningError If the tagged table contains invalid values or if the training failed. """ - fit(self._wrapped_classifier, training_set) - self._target_name = training_set.target.name + wrapped_classifier = sk_LogisticRegression(n_jobs=-1) + fit(wrapped_classifier, training_set) + + result = LogisticRegression() + result._wrapped_classifier = wrapped_classifier + result._target_name = training_set.target.name + + return result def predict(self, dataset: Table) -> TaggedTable: """ diff --git a/src/safeds/ml/classification/_random_forest.py b/src/safeds/ml/classification/_random_forest.py index 4a9aff14e..777b4f620 100644 --- a/src/safeds/ml/classification/_random_forest.py +++ b/src/safeds/ml/classification/_random_forest.py @@ -1,3 +1,7 @@ +from __future__ import annotations + +from typing import Optional + from safeds.data.tabular.containers import Table, TaggedTable from safeds.ml._util_sklearn import fit, predict from sklearn.ensemble import RandomForestClassifier as sk_RandomForestClassifier @@ -11,10 +15,10 @@ class RandomForest(Classifier): """ def __init__(self) -> None: - self._wrapped_classifier = sk_RandomForestClassifier(n_jobs=-1) - self._target_name = "" + self._wrapped_classifier: Optional[sk_RandomForestClassifier] = None + self._target_name: Optional[str] = None - def fit(self, training_set: TaggedTable) -> None: + def fit(self, training_set: TaggedTable) -> RandomForest: """ Fit this model given a tagged table. @@ -28,8 +32,14 @@ def fit(self, training_set: TaggedTable) -> None: LearningError If the tagged table contains invalid values or if the training failed. """ - fit(self._wrapped_classifier, training_set) - self._target_name = training_set.target.name + wrapped_classifier = sk_RandomForestClassifier(n_jobs=-1) + fit(wrapped_classifier, training_set) + + result = RandomForest() + result._wrapped_classifier = wrapped_classifier + result._target_name = training_set.target.name + + return result def predict(self, dataset: Table) -> TaggedTable: """ diff --git a/tests/safeds/ml/classification/test_ada_boost.py b/tests/safeds/ml/classification/test_ada_boost.py index 0989ffdba..41b04c9f7 100644 --- a/tests/safeds/ml/classification/test_ada_boost.py +++ b/tests/safeds/ml/classification/test_ada_boost.py @@ -40,8 +40,8 @@ class TestPredict: def test_should_succeed_on_valid_data( self, classifier: Classifier, valid_data: TaggedTable ) -> None: - classifier.fit(valid_data) - classifier.predict(valid_data.features) + fitted_classifier = classifier.fit(valid_data) + fitted_classifier.predict(valid_data.features) assert True # This asserts that the predict method succeeds def test_should_raise_when_not_fitted( @@ -53,6 +53,6 @@ def test_should_raise_when_not_fitted( def test_should_raise_on_invalid_data( self, classifier: Classifier, valid_data: TaggedTable, invalid_data: TaggedTable ) -> None: - classifier.fit(valid_data) + fitted_classifier = classifier.fit(valid_data) with pytest.raises(PredictionError): - classifier.predict(invalid_data.features) + fitted_classifier.predict(invalid_data.features) diff --git a/tests/safeds/ml/classification/test_decision_tree.py b/tests/safeds/ml/classification/test_decision_tree.py index 091ff7f8c..68f0fad22 100644 --- a/tests/safeds/ml/classification/test_decision_tree.py +++ b/tests/safeds/ml/classification/test_decision_tree.py @@ -40,8 +40,8 @@ class TestPredict: def test_should_succeed_on_valid_data( self, classifier: Classifier, valid_data: TaggedTable ) -> None: - classifier.fit(valid_data) - classifier.predict(valid_data.features) + fitted_classifier = classifier.fit(valid_data) + fitted_classifier.predict(valid_data.features) assert True # This asserts that the predict method succeeds def test_should_raise_when_not_fitted( @@ -53,6 +53,6 @@ def test_should_raise_when_not_fitted( def test_should_raise_on_invalid_data( self, classifier: Classifier, valid_data: TaggedTable, invalid_data: TaggedTable ) -> None: - classifier.fit(valid_data) + fitted_classifier = classifier.fit(valid_data) with pytest.raises(PredictionError): - classifier.predict(invalid_data.features) + fitted_classifier.predict(invalid_data.features) diff --git a/tests/safeds/ml/classification/test_gradient_boosting.py b/tests/safeds/ml/classification/test_gradient_boosting.py index c820c62b8..d1bf2446d 100644 --- a/tests/safeds/ml/classification/test_gradient_boosting.py +++ b/tests/safeds/ml/classification/test_gradient_boosting.py @@ -44,8 +44,8 @@ class TestPredict: def test_should_succeed_on_valid_data( self, classifier: Classifier, valid_data: TaggedTable ) -> None: - classifier.fit(valid_data) - classifier.predict(valid_data.features) + fitted_classifier = classifier.fit(valid_data) + fitted_classifier.predict(valid_data.features) assert True # This asserts that the predict method succeeds def test_should_raise_when_not_fitted( @@ -57,6 +57,6 @@ def test_should_raise_when_not_fitted( def test_should_raise_on_invalid_data( self, classifier: Classifier, valid_data: TaggedTable, invalid_data: TaggedTable ) -> None: - classifier.fit(valid_data) + fitted_classifier = classifier.fit(valid_data) with pytest.raises(PredictionError): - classifier.predict(invalid_data.features) + fitted_classifier.predict(invalid_data.features) diff --git a/tests/safeds/ml/classification/test_k_nearest_neighbors.py b/tests/safeds/ml/classification/test_k_nearest_neighbors.py index c202248bb..93359a9c6 100644 --- a/tests/safeds/ml/classification/test_k_nearest_neighbors.py +++ b/tests/safeds/ml/classification/test_k_nearest_neighbors.py @@ -42,8 +42,8 @@ class TestPredict: def test_should_succeed_on_valid_data( self, classifier: Classifier, valid_data: TaggedTable ) -> None: - classifier.fit(valid_data) - classifier.predict(valid_data.features) + fitted_classifier = classifier.fit(valid_data) + fitted_classifier.predict(valid_data.features) assert True # This asserts that the predict method succeeds def test_should_raise_when_not_fitted( @@ -55,6 +55,6 @@ def test_should_raise_when_not_fitted( def test_should_raise_on_invalid_data( self, classifier: Classifier, valid_data: TaggedTable, invalid_data: TaggedTable ) -> None: - classifier.fit(valid_data) + fitted_classifier = classifier.fit(valid_data) with pytest.raises(PredictionError): - classifier.predict(invalid_data.features) + fitted_classifier.predict(invalid_data.features) diff --git a/tests/safeds/ml/classification/test_logistic_regression.py b/tests/safeds/ml/classification/test_logistic_regression.py index 936f8beae..fd62ad8ea 100644 --- a/tests/safeds/ml/classification/test_logistic_regression.py +++ b/tests/safeds/ml/classification/test_logistic_regression.py @@ -42,8 +42,8 @@ class TestPredict: def test_should_succeed_on_valid_data( self, classifier: Classifier, valid_data: TaggedTable ) -> None: - classifier.fit(valid_data) - classifier.predict(valid_data.features) + fitted_classifier = classifier.fit(valid_data) + fitted_classifier.predict(valid_data.features) assert True # This asserts that the predict method succeeds def test_should_raise_when_not_fitted( @@ -55,6 +55,6 @@ def test_should_raise_when_not_fitted( def test_should_raise_on_invalid_data( self, classifier: Classifier, valid_data: TaggedTable, invalid_data: TaggedTable ) -> None: - classifier.fit(valid_data) + fitted_classifier = classifier.fit(valid_data) with pytest.raises(PredictionError): - classifier.predict(invalid_data.features) + fitted_classifier.predict(invalid_data.features) diff --git a/tests/safeds/ml/classification/test_random_forest.py b/tests/safeds/ml/classification/test_random_forest.py index df0da59a9..181e2a577 100644 --- a/tests/safeds/ml/classification/test_random_forest.py +++ b/tests/safeds/ml/classification/test_random_forest.py @@ -40,8 +40,8 @@ class TestPredict: def test_should_succeed_on_valid_data( self, classifier: Classifier, valid_data: TaggedTable ) -> None: - classifier.fit(valid_data) - classifier.predict(valid_data.features) + fitted_classifier = classifier.fit(valid_data) + fitted_classifier.predict(valid_data.features) assert True # This asserts that the predict method succeeds def test_should_raise_when_not_fitted( @@ -53,6 +53,6 @@ def test_should_raise_when_not_fitted( def test_should_raise_on_invalid_data( self, classifier: Classifier, valid_data: TaggedTable, invalid_data: TaggedTable ) -> None: - classifier.fit(valid_data) + fitted_classifier = classifier.fit(valid_data) with pytest.raises(PredictionError): - classifier.predict(invalid_data.features) + fitted_classifier.predict(invalid_data.features) From 2075e87ab923efb9aa5cb63137f4b0a792ff1236 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 27 Mar 2023 11:55:12 +0200 Subject: [PATCH 2/6] feat: `fit` method of `Regressor`s returns fitted `Regressor` --- src/safeds/ml/regression/_ada_boost.py | 20 ++++++++++++----- src/safeds/ml/regression/_decision_tree.py | 20 ++++++++++++----- .../ml/regression/_elastic_net_regression.py | 20 ++++++++++++----- .../_gradient_boosting_regression.py | 20 ++++++++++++----- .../ml/regression/_k_nearest_neighbors.py | 22 ++++++++++++++----- src/safeds/ml/regression/_lasso_regression.py | 20 ++++++++++++----- .../ml/regression/_linear_regression.py | 20 ++++++++++++----- src/safeds/ml/regression/_random_forest.py | 20 ++++++++++++----- src/safeds/ml/regression/_regressor.py | 4 +++- src/safeds/ml/regression/_ridge_regression.py | 20 ++++++++++++----- tests/safeds/ml/regression/test_ada_boost.py | 8 +++---- .../ml/regression/test_decision_tree.py | 8 +++---- .../safeds/ml/regression/test_elastic_net.py | 8 +++---- .../ml/regression/test_gradient_boosting.py | 8 +++---- .../ml/regression/test_k_nearest_neighbors.py | 8 +++---- .../ml/regression/test_lasso_regression.py | 8 +++---- .../ml/regression/test_linear_regression.py | 8 +++---- .../ml/regression/test_random_forest.py | 8 +++---- .../ml/regression/test_ridge_regression.py | 8 +++---- tests/safeds/ml/test_util_sklearn.py | 4 ++-- 20 files changed, 177 insertions(+), 85 deletions(-) diff --git a/src/safeds/ml/regression/_ada_boost.py b/src/safeds/ml/regression/_ada_boost.py index 8a6be0e0b..d5998cc21 100644 --- a/src/safeds/ml/regression/_ada_boost.py +++ b/src/safeds/ml/regression/_ada_boost.py @@ -1,3 +1,7 @@ +from __future__ import annotations + +from typing import Optional + from safeds.data.tabular.containers import Table, TaggedTable from safeds.ml._util_sklearn import fit, predict from sklearn.ensemble import AdaBoostRegressor as sk_AdaBoostRegressor @@ -12,10 +16,10 @@ class AdaBoost(Regressor): """ def __init__(self) -> None: - self._wrapped_regressor = sk_AdaBoostRegressor() - self._target_name = "" + self._wrapped_regressor: Optional[sk_AdaBoostRegressor] = None + self._target_name: Optional[str] = None - def fit(self, training_set: TaggedTable) -> None: + def fit(self, training_set: TaggedTable) -> AdaBoost: """ Fit this model given a tagged table. @@ -29,8 +33,14 @@ def fit(self, training_set: TaggedTable) -> None: LearningError If the tagged table contains invalid values or if the training failed. """ - fit(self._wrapped_regressor, training_set) - self._target_name = training_set.target.name + wrapped_regressor = sk_AdaBoostRegressor() + fit(wrapped_regressor, training_set) + + result = AdaBoost() + result._wrapped_regressor = wrapped_regressor + result._target_name = training_set.target.name + + return result def predict(self, dataset: Table) -> TaggedTable: """ diff --git a/src/safeds/ml/regression/_decision_tree.py b/src/safeds/ml/regression/_decision_tree.py index e7eeae520..85f3d6bc4 100644 --- a/src/safeds/ml/regression/_decision_tree.py +++ b/src/safeds/ml/regression/_decision_tree.py @@ -1,3 +1,7 @@ +from __future__ import annotations + +from typing import Optional + from safeds.data.tabular.containers import Table, TaggedTable from safeds.ml._util_sklearn import fit, predict from sklearn.tree import DecisionTreeRegressor as sk_DecisionTreeRegressor @@ -12,10 +16,10 @@ class DecisionTree(Regressor): """ def __init__(self) -> None: - self._wrapped_regressor = sk_DecisionTreeRegressor() - self._target_name = "" + self._wrapped_regressor: Optional[sk_DecisionTreeRegressor] = None + self._target_name: Optional[str] = None - def fit(self, training_set: TaggedTable) -> None: + def fit(self, training_set: TaggedTable) -> DecisionTree: """ Fit this model given a tagged table. @@ -29,8 +33,14 @@ def fit(self, training_set: TaggedTable) -> None: LearningError If the tagged table contains invalid values or if the training failed. """ - fit(self._wrapped_regressor, training_set) - self._target_name = training_set.target.name + wrapped_regressor = sk_DecisionTreeRegressor() + fit(wrapped_regressor, training_set) + + result = DecisionTree() + result._wrapped_regressor = wrapped_regressor + result._target_name = training_set.target.name + + return result def predict(self, dataset: Table) -> TaggedTable: """ diff --git a/src/safeds/ml/regression/_elastic_net_regression.py b/src/safeds/ml/regression/_elastic_net_regression.py index 2da3d2867..cd31225b9 100644 --- a/src/safeds/ml/regression/_elastic_net_regression.py +++ b/src/safeds/ml/regression/_elastic_net_regression.py @@ -1,3 +1,7 @@ +from __future__ import annotations + +from typing import Optional + from safeds.data.tabular.containers import Table, TaggedTable from safeds.ml._util_sklearn import fit, predict from sklearn.linear_model import ElasticNet as sk_ElasticNet @@ -12,10 +16,10 @@ class ElasticNetRegression(Regressor): """ def __init__(self) -> None: - self._wrapped_regressor = sk_ElasticNet() - self._target_name = "" + self._wrapped_regressor: Optional[sk_ElasticNet] = None + self._target_name: Optional[str] = None - def fit(self, training_set: TaggedTable) -> None: + def fit(self, training_set: TaggedTable) -> ElasticNetRegression: """ Fit this model given a tagged table. @@ -29,8 +33,14 @@ def fit(self, training_set: TaggedTable) -> None: LearningError If the tagged table contains invalid values or if the training failed. """ - fit(self._wrapped_regressor, training_set) - self._target_name = training_set.target.name + wrapped_regressor = sk_ElasticNet() + fit(wrapped_regressor, training_set) + + result = ElasticNetRegression() + result._wrapped_regressor = wrapped_regressor + result._target_name = training_set.target.name + + return result def predict(self, dataset: Table) -> TaggedTable: """ diff --git a/src/safeds/ml/regression/_gradient_boosting_regression.py b/src/safeds/ml/regression/_gradient_boosting_regression.py index 68bb20967..5a7c1f2c6 100644 --- a/src/safeds/ml/regression/_gradient_boosting_regression.py +++ b/src/safeds/ml/regression/_gradient_boosting_regression.py @@ -1,3 +1,7 @@ +from __future__ import annotations + +from typing import Optional + from safeds.data.tabular.containers import Table, TaggedTable from safeds.ml._util_sklearn import fit, predict from sklearn.ensemble import GradientBoostingRegressor as sk_GradientBoostingRegressor @@ -12,10 +16,10 @@ class GradientBoosting(Regressor): """ def __init__(self) -> None: - self._wrapped_regressor = sk_GradientBoostingRegressor() - self._target_name = "" + self._wrapped_regressor: Optional[sk_GradientBoostingRegressor] = None + self._target_name: Optional[str] = None - def fit(self, training_set: TaggedTable) -> None: + def fit(self, training_set: TaggedTable) -> GradientBoosting: """ Fit this model given a tagged table. @@ -30,10 +34,14 @@ def fit(self, training_set: TaggedTable) -> None: LearningError If the tagged table contains invalid values or if the training failed. """ - fit(self._wrapped_regressor, training_set) - self._target_name = training_set.target.name + wrapped_regressor = sk_GradientBoostingRegressor() + fit(wrapped_regressor, training_set) + + result = GradientBoosting() + result._wrapped_regressor = wrapped_regressor + result._target_name = training_set.target.name - # noinspection PyProtectedMember + return result def predict(self, dataset: Table) -> TaggedTable: """ diff --git a/src/safeds/ml/regression/_k_nearest_neighbors.py b/src/safeds/ml/regression/_k_nearest_neighbors.py index 315a10509..6066c0198 100644 --- a/src/safeds/ml/regression/_k_nearest_neighbors.py +++ b/src/safeds/ml/regression/_k_nearest_neighbors.py @@ -1,3 +1,7 @@ +from __future__ import annotations + +from typing import Optional + from safeds.data.tabular.containers import Table, TaggedTable from safeds.ml._util_sklearn import fit, predict from sklearn.neighbors import KNeighborsRegressor as sk_KNeighborsRegressor @@ -16,10 +20,12 @@ class KNearestNeighbors(Regressor): """ def __init__(self, n_neighbors: int) -> None: - self._wrapped_regressor = sk_KNeighborsRegressor(n_neighbors) - self._target_name = "" + self._n_neighbors = n_neighbors + + self._wrapped_regressor: Optional[sk_KNeighborsRegressor] = None + self._target_name: Optional[str] = None - def fit(self, training_set: TaggedTable) -> None: + def fit(self, training_set: TaggedTable) -> KNearestNeighbors: """ Fit this model given a tagged table. @@ -33,8 +39,14 @@ def fit(self, training_set: TaggedTable) -> None: LearningError If the tagged table contains invalid values or if the training failed. """ - fit(self._wrapped_regressor, training_set) - self._target_name = training_set.target.name + wrapped_regressor = sk_KNeighborsRegressor(self._n_neighbors, n_jobs=-1) + fit(wrapped_regressor, training_set) + + result = KNearestNeighbors(self._n_neighbors) + result._wrapped_regressor = wrapped_regressor + result._target_name = training_set.target.name + + return result def predict(self, dataset: Table) -> TaggedTable: """ diff --git a/src/safeds/ml/regression/_lasso_regression.py b/src/safeds/ml/regression/_lasso_regression.py index 74b23ee9a..ad9ff6665 100644 --- a/src/safeds/ml/regression/_lasso_regression.py +++ b/src/safeds/ml/regression/_lasso_regression.py @@ -1,3 +1,7 @@ +from __future__ import annotations + +from typing import Optional + from safeds.data.tabular.containers import Table, TaggedTable from safeds.ml._util_sklearn import fit, predict from sklearn.linear_model import Lasso as sk_Lasso @@ -12,10 +16,10 @@ class LassoRegression(Regressor): """ def __init__(self) -> None: - self._wrapped_regressor = sk_Lasso() - self._target_name = "" + self._wrapped_regressor: Optional[sk_Lasso] = None + self._target_name: Optional[str] = None - def fit(self, training_set: TaggedTable) -> None: + def fit(self, training_set: TaggedTable) -> LassoRegression: """ Fit this model given a tagged table. @@ -29,8 +33,14 @@ def fit(self, training_set: TaggedTable) -> None: LearningError If the tagged table contains invalid values or if the training failed. """ - fit(self._wrapped_regressor, training_set) - self._target_name = training_set.target.name + wrapped_regressor = sk_Lasso() + fit(wrapped_regressor, training_set) + + result = LassoRegression() + result._wrapped_regressor = wrapped_regressor + result._target_name = training_set.target.name + + return result def predict(self, dataset: Table) -> TaggedTable: """ diff --git a/src/safeds/ml/regression/_linear_regression.py b/src/safeds/ml/regression/_linear_regression.py index 54f61e984..de8b0e4f6 100644 --- a/src/safeds/ml/regression/_linear_regression.py +++ b/src/safeds/ml/regression/_linear_regression.py @@ -1,3 +1,7 @@ +from __future__ import annotations + +from typing import Optional + from safeds.data.tabular.containers import Table, TaggedTable from safeds.ml._util_sklearn import fit, predict from sklearn.linear_model import LinearRegression as sk_LinearRegression @@ -12,10 +16,10 @@ class LinearRegression(Regressor): """ def __init__(self) -> None: - self._wrapped_regressor = sk_LinearRegression(n_jobs=-1) - self._target_name = "" + self._wrapped_regressor: Optional[sk_LinearRegression] = None + self._target_name: Optional[str] = None - def fit(self, training_set: TaggedTable) -> None: + def fit(self, training_set: TaggedTable) -> LinearRegression: """ Fit this model given a tagged table. @@ -29,8 +33,14 @@ def fit(self, training_set: TaggedTable) -> None: LearningError If the tagged table contains invalid values or if the training failed. """ - fit(self._wrapped_regressor, training_set) - self._target_name = training_set.target.name + wrapped_regressor = sk_LinearRegression(n_jobs=-1) + fit(wrapped_regressor, training_set) + + result = LinearRegression() + result._wrapped_regressor = wrapped_regressor + result._target_name = training_set.target.name + + return result def predict(self, dataset: Table) -> TaggedTable: """ diff --git a/src/safeds/ml/regression/_random_forest.py b/src/safeds/ml/regression/_random_forest.py index 91acaa652..5c5fde720 100644 --- a/src/safeds/ml/regression/_random_forest.py +++ b/src/safeds/ml/regression/_random_forest.py @@ -1,3 +1,7 @@ +from __future__ import annotations + +from typing import Optional + from safeds.data.tabular.containers import Table, TaggedTable from safeds.ml._util_sklearn import fit, predict from sklearn.ensemble import RandomForestRegressor as sk_RandomForestRegressor @@ -11,10 +15,10 @@ class RandomForest(Regressor): """ def __init__(self) -> None: - self._wrapped_regressor = sk_RandomForestRegressor(n_jobs=-1) - self._target_name = "" + self._wrapped_regressor: Optional[sk_RandomForestRegressor] = None + self._target_name: Optional[str] = None - def fit(self, training_set: TaggedTable) -> None: + def fit(self, training_set: TaggedTable) -> RandomForest: """ Fit this model given a tagged table. @@ -28,8 +32,14 @@ def fit(self, training_set: TaggedTable) -> None: LearningError If the tagged table contains invalid values or if the training failed. """ - fit(self._wrapped_regressor, training_set) - self._target_name = training_set.target.name + wrapped_regressor = sk_RandomForestRegressor(n_jobs=-1) + fit(wrapped_regressor, training_set) + + result = RandomForest() + result._wrapped_regressor = wrapped_regressor + result._target_name = training_set.target.name + + return result def predict(self, dataset: Table) -> TaggedTable: """ diff --git a/src/safeds/ml/regression/_regressor.py b/src/safeds/ml/regression/_regressor.py index 232971314..b593e5e2e 100644 --- a/src/safeds/ml/regression/_regressor.py +++ b/src/safeds/ml/regression/_regressor.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from abc import ABC, abstractmethod from safeds.data.tabular.containers import Column, Table, TaggedTable @@ -12,7 +14,7 @@ class Regressor(ABC): """ @abstractmethod - def fit(self, training_set: TaggedTable) -> None: + def fit(self, training_set: TaggedTable) -> Regressor: """ Fit this model given a tagged table. diff --git a/src/safeds/ml/regression/_ridge_regression.py b/src/safeds/ml/regression/_ridge_regression.py index b3d23beca..d39270dc9 100644 --- a/src/safeds/ml/regression/_ridge_regression.py +++ b/src/safeds/ml/regression/_ridge_regression.py @@ -1,3 +1,7 @@ +from __future__ import annotations + +from typing import Optional + from safeds.data.tabular.containers import Table, TaggedTable from safeds.ml._util_sklearn import fit, predict from sklearn.linear_model import Ridge as sk_Ridge @@ -12,10 +16,10 @@ class RidgeRegression(Regressor): """ def __init__(self) -> None: - self._wrapped_regressor = sk_Ridge() - self._target_name = "" + self._wrapped_regressor: Optional[sk_Ridge] = None + self._target_name: Optional[str] = None - def fit(self, training_set: TaggedTable) -> None: + def fit(self, training_set: TaggedTable) -> RidgeRegression: """ Fit this model given a tagged table. @@ -29,8 +33,14 @@ def fit(self, training_set: TaggedTable) -> None: LearningError If the tagged table contains invalid values or if the training failed. """ - fit(self._wrapped_regressor, training_set) - self._target_name = training_set.target.name + wrapped_regressor = sk_Ridge() + fit(wrapped_regressor, training_set) + + result = RidgeRegression() + result._wrapped_regressor = wrapped_regressor + result._target_name = training_set.target.name + + return result def predict(self, dataset: Table) -> TaggedTable: """ diff --git a/tests/safeds/ml/regression/test_ada_boost.py b/tests/safeds/ml/regression/test_ada_boost.py index d290f49c0..d067ece5a 100644 --- a/tests/safeds/ml/regression/test_ada_boost.py +++ b/tests/safeds/ml/regression/test_ada_boost.py @@ -40,8 +40,8 @@ class TestPredict: def test_should_succeed_on_valid_data( self, regressor: Regressor, valid_data: TaggedTable ) -> None: - regressor.fit(valid_data) - regressor.predict(valid_data.features) + fitted_regressor = regressor.fit(valid_data) + fitted_regressor.predict(valid_data.features) assert True # This asserts that the predict method succeeds def test_should_raise_when_not_fitted( @@ -53,6 +53,6 @@ def test_should_raise_when_not_fitted( def test_should_raise_on_invalid_data( self, regressor: Regressor, valid_data: TaggedTable, invalid_data: TaggedTable ) -> None: - regressor.fit(valid_data) + fitted_regressor = regressor.fit(valid_data) with pytest.raises(PredictionError): - regressor.predict(invalid_data.features) + fitted_regressor.predict(invalid_data.features) diff --git a/tests/safeds/ml/regression/test_decision_tree.py b/tests/safeds/ml/regression/test_decision_tree.py index 6db937abe..51dee8f84 100644 --- a/tests/safeds/ml/regression/test_decision_tree.py +++ b/tests/safeds/ml/regression/test_decision_tree.py @@ -40,8 +40,8 @@ class TestPredict: def test_should_succeed_on_valid_data( self, regressor: Regressor, valid_data: TaggedTable ) -> None: - regressor.fit(valid_data) - regressor.predict(valid_data.features) + fitted_regressor = regressor.fit(valid_data) + fitted_regressor.predict(valid_data.features) assert True # This asserts that the predict method succeeds def test_should_raise_when_not_fitted( @@ -53,6 +53,6 @@ def test_should_raise_when_not_fitted( def test_should_raise_on_invalid_data( self, regressor: Regressor, valid_data: TaggedTable, invalid_data: TaggedTable ) -> None: - regressor.fit(valid_data) + fitted_regressor = regressor.fit(valid_data) with pytest.raises(PredictionError): - regressor.predict(invalid_data.features) + fitted_regressor.predict(invalid_data.features) diff --git a/tests/safeds/ml/regression/test_elastic_net.py b/tests/safeds/ml/regression/test_elastic_net.py index 06e443372..1f317fe76 100644 --- a/tests/safeds/ml/regression/test_elastic_net.py +++ b/tests/safeds/ml/regression/test_elastic_net.py @@ -42,8 +42,8 @@ class TestPredict: def test_should_succeed_on_valid_data( self, regressor: Regressor, valid_data: TaggedTable ) -> None: - regressor.fit(valid_data) - regressor.predict(valid_data.features) + fitted_regressor = regressor.fit(valid_data) + fitted_regressor.predict(valid_data.features) assert True # This asserts that the predict method succeeds def test_should_raise_when_not_fitted( @@ -55,6 +55,6 @@ def test_should_raise_when_not_fitted( def test_should_raise_on_invalid_data( self, regressor: Regressor, valid_data: TaggedTable, invalid_data: TaggedTable ) -> None: - regressor.fit(valid_data) + fitted_regressor = regressor.fit(valid_data) with pytest.raises(PredictionError): - regressor.predict(invalid_data.features) + fitted_regressor.predict(invalid_data.features) diff --git a/tests/safeds/ml/regression/test_gradient_boosting.py b/tests/safeds/ml/regression/test_gradient_boosting.py index 023dd76f4..40816d73a 100644 --- a/tests/safeds/ml/regression/test_gradient_boosting.py +++ b/tests/safeds/ml/regression/test_gradient_boosting.py @@ -44,8 +44,8 @@ class TestPredict: def test_should_succeed_on_valid_data( self, regressor: Regressor, valid_data: TaggedTable ) -> None: - regressor.fit(valid_data) - regressor.predict(valid_data.features) + fitted_regressor = regressor.fit(valid_data) + fitted_regressor.predict(valid_data.features) assert True # This asserts that the predict method succeeds def test_should_raise_when_not_fitted( @@ -57,6 +57,6 @@ def test_should_raise_when_not_fitted( def test_should_raise_on_invalid_data( self, regressor: Regressor, valid_data: TaggedTable, invalid_data: TaggedTable ) -> None: - regressor.fit(valid_data) + fitted_regressor = regressor.fit(valid_data) with pytest.raises(PredictionError): - regressor.predict(invalid_data.features) + fitted_regressor.predict(invalid_data.features) diff --git a/tests/safeds/ml/regression/test_k_nearest_neighbors.py b/tests/safeds/ml/regression/test_k_nearest_neighbors.py index 0ad18d54a..0e14ed4d9 100644 --- a/tests/safeds/ml/regression/test_k_nearest_neighbors.py +++ b/tests/safeds/ml/regression/test_k_nearest_neighbors.py @@ -42,8 +42,8 @@ class TestPredict: def test_should_succeed_on_valid_data( self, regressor: Regressor, valid_data: TaggedTable ) -> None: - regressor.fit(valid_data) - regressor.predict(valid_data.features) + fitted_regressor = regressor.fit(valid_data) + fitted_regressor.predict(valid_data.features) assert True # This asserts that the predict method succeeds def test_should_raise_when_not_fitted( @@ -55,6 +55,6 @@ def test_should_raise_when_not_fitted( def test_should_raise_on_invalid_data( self, regressor: Regressor, valid_data: TaggedTable, invalid_data: TaggedTable ) -> None: - regressor.fit(valid_data) + fitted_regressor = regressor.fit(valid_data) with pytest.raises(PredictionError): - regressor.predict(invalid_data.features) + fitted_regressor.predict(invalid_data.features) diff --git a/tests/safeds/ml/regression/test_lasso_regression.py b/tests/safeds/ml/regression/test_lasso_regression.py index 362871a31..4de72b8f2 100644 --- a/tests/safeds/ml/regression/test_lasso_regression.py +++ b/tests/safeds/ml/regression/test_lasso_regression.py @@ -40,8 +40,8 @@ class TestPredict: def test_should_succeed_on_valid_data( self, regressor: Regressor, valid_data: TaggedTable ) -> None: - regressor.fit(valid_data) - regressor.predict(valid_data.features) + fitted_regressor = regressor.fit(valid_data) + fitted_regressor.predict(valid_data.features) assert True # This asserts that the predict method succeeds def test_should_raise_when_not_fitted( @@ -53,6 +53,6 @@ def test_should_raise_when_not_fitted( def test_should_raise_on_invalid_data( self, regressor: Regressor, valid_data: TaggedTable, invalid_data: TaggedTable ) -> None: - regressor.fit(valid_data) + fitted_regressor = regressor.fit(valid_data) with pytest.raises(PredictionError): - regressor.predict(invalid_data.features) + fitted_regressor.predict(invalid_data.features) diff --git a/tests/safeds/ml/regression/test_linear_regression.py b/tests/safeds/ml/regression/test_linear_regression.py index fbabe70ec..2084a1ac4 100644 --- a/tests/safeds/ml/regression/test_linear_regression.py +++ b/tests/safeds/ml/regression/test_linear_regression.py @@ -40,8 +40,8 @@ class TestPredict: def test_should_succeed_on_valid_data( self, regressor: Regressor, valid_data: TaggedTable ) -> None: - regressor.fit(valid_data) - regressor.predict(valid_data.features) + fitted_regressor = regressor.fit(valid_data) + fitted_regressor.predict(valid_data.features) assert True # This asserts that the predict method succeeds def test_should_raise_when_not_fitted( @@ -53,6 +53,6 @@ def test_should_raise_when_not_fitted( def test_should_raise_on_invalid_data( self, regressor: Regressor, valid_data: TaggedTable, invalid_data: TaggedTable ) -> None: - regressor.fit(valid_data) + fitted_regressor = regressor.fit(valid_data) with pytest.raises(PredictionError): - regressor.predict(invalid_data.features) + fitted_regressor.predict(invalid_data.features) diff --git a/tests/safeds/ml/regression/test_random_forest.py b/tests/safeds/ml/regression/test_random_forest.py index 87a30bbce..0d47b7681 100644 --- a/tests/safeds/ml/regression/test_random_forest.py +++ b/tests/safeds/ml/regression/test_random_forest.py @@ -40,8 +40,8 @@ class TestPredict: def test_should_succeed_on_valid_data( self, regressor: Regressor, valid_data: TaggedTable ) -> None: - regressor.fit(valid_data) - regressor.predict(valid_data.features) + fitted_regressor = regressor.fit(valid_data) + fitted_regressor.predict(valid_data.features) assert True # This asserts that the predict method succeeds def test_should_raise_when_not_fitted( @@ -53,6 +53,6 @@ def test_should_raise_when_not_fitted( def test_should_raise_on_invalid_data( self, regressor: Regressor, valid_data: TaggedTable, invalid_data: TaggedTable ) -> None: - regressor.fit(valid_data) + fitted_regressor = regressor.fit(valid_data) with pytest.raises(PredictionError): - regressor.predict(invalid_data.features) + fitted_regressor.predict(invalid_data.features) diff --git a/tests/safeds/ml/regression/test_ridge_regression.py b/tests/safeds/ml/regression/test_ridge_regression.py index 6f201e9e9..0ec6f6978 100644 --- a/tests/safeds/ml/regression/test_ridge_regression.py +++ b/tests/safeds/ml/regression/test_ridge_regression.py @@ -40,8 +40,8 @@ class TestPredict: def test_should_succeed_on_valid_data( self, regressor: Regressor, valid_data: TaggedTable ) -> None: - regressor.fit(valid_data) - regressor.predict(valid_data.features) + fitted_regressor = regressor.fit(valid_data) + fitted_regressor.predict(valid_data.features) assert True # This asserts that the predict method succeeds def test_should_raise_when_not_fitted( @@ -53,6 +53,6 @@ def test_should_raise_when_not_fitted( def test_should_raise_on_invalid_data( self, regressor: Regressor, valid_data: TaggedTable, invalid_data: TaggedTable ) -> None: - regressor.fit(valid_data) + fitted_regressor = regressor.fit(valid_data) with pytest.raises(PredictionError): - regressor.predict(invalid_data.features) + fitted_regressor.predict(invalid_data.features) diff --git a/tests/safeds/ml/test_util_sklearn.py b/tests/safeds/ml/test_util_sklearn.py index 07d32d91f..88bb03b3c 100644 --- a/tests/safeds/ml/test_util_sklearn.py +++ b/tests/safeds/ml/test_util_sklearn.py @@ -12,11 +12,11 @@ def test_predict_should_not_warn_about_feature_names() -> None: training_set = TaggedTable(Table({"a": [1, 2, 3], "b": [2, 4, 6]}), target_name="b") model = LinearRegression() - model.fit(training_set) + fitted_model = model.fit(training_set) test_set = Table({"a": [4, 5, 6]}) # No warning should be emitted with warnings.catch_warnings(): warnings.filterwarnings("error", message="X has feature names") - model.predict(dataset=test_set) + fitted_model.predict(dataset=test_set) From a5b907eb8cfd7e1484a9832d325e296f4e6b8404 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 27 Mar 2023 12:02:30 +0200 Subject: [PATCH 3/6] docs: adjust docstrings to API changes --- src/safeds/ml/classification/_ada_boost.py | 12 +++++++++--- src/safeds/ml/classification/_classifier.py | 7 ++++--- src/safeds/ml/classification/_decision_tree.py | 12 +++++++++--- .../_gradient_boosting_classification.py | 12 +++++++++--- .../ml/classification/_k_nearest_neighbors.py | 12 +++++++++--- .../ml/classification/_logistic_regression.py | 13 ++++++++++--- src/safeds/ml/classification/_random_forest.py | 13 ++++++++++--- src/safeds/ml/regression/_ada_boost.py | 13 ++++++++++--- src/safeds/ml/regression/_decision_tree.py | 13 ++++++++++--- .../ml/regression/_elastic_net_regression.py | 13 ++++++++++--- .../regression/_gradient_boosting_regression.py | 16 +++++++++++----- src/safeds/ml/regression/_k_nearest_neighbors.py | 13 ++++++++++--- src/safeds/ml/regression/_lasso_regression.py | 13 ++++++++++--- src/safeds/ml/regression/_linear_regression.py | 13 ++++++++++--- src/safeds/ml/regression/_random_forest.py | 13 ++++++++++--- src/safeds/ml/regression/_regressor.py | 12 +++++++++--- src/safeds/ml/regression/_ridge_regression.py | 13 ++++++++++--- 17 files changed, 160 insertions(+), 53 deletions(-) diff --git a/src/safeds/ml/classification/_ada_boost.py b/src/safeds/ml/classification/_ada_boost.py index 1cbae2048..4186cd100 100644 --- a/src/safeds/ml/classification/_ada_boost.py +++ b/src/safeds/ml/classification/_ada_boost.py @@ -21,17 +21,23 @@ def __init__(self) -> None: def fit(self, training_set: TaggedTable) -> AdaBoost: """ - Fit this model given a tagged table. + Create a new classifier based on this one and fit it with the given training data. This classifier is not + modified. Parameters ---------- training_set : TaggedTable - The tagged table containing the feature and target vectors. + The training data containing the feature and target vectors. + + Returns + ------- + fitted_classifier : AdaBoost + The fitted classifier. Raises ------ LearningError - If the tagged table contains invalid values or if the training failed. + If the training data contains invalid values or if the training failed. """ wrapped_classifier = sk_AdaBoostClassifier() diff --git a/src/safeds/ml/classification/_classifier.py b/src/safeds/ml/classification/_classifier.py index a2d0af97a..3c30ca13d 100644 --- a/src/safeds/ml/classification/_classifier.py +++ b/src/safeds/ml/classification/_classifier.py @@ -14,12 +14,13 @@ class Classifier(ABC): @abstractmethod def fit(self, training_set: TaggedTable) -> Classifier: """ - Fit this classifier given a tagged table and return the fitted classifier. + Create a new classifier based on this one and fit it with the given training data. This classifier is not + modified. Parameters ---------- training_set : TaggedTable - The tagged table containing the feature and target vectors. + The training data containing the feature and target vectors. Returns ------- @@ -29,7 +30,7 @@ def fit(self, training_set: TaggedTable) -> Classifier: Raises ------ LearningError - If the tagged table contains invalid values or if the training failed. + If the training data contains invalid values or if the training failed. """ @abstractmethod diff --git a/src/safeds/ml/classification/_decision_tree.py b/src/safeds/ml/classification/_decision_tree.py index b769250bc..dfdf730fb 100644 --- a/src/safeds/ml/classification/_decision_tree.py +++ b/src/safeds/ml/classification/_decision_tree.py @@ -21,17 +21,23 @@ def __init__(self) -> None: def fit(self, training_set: TaggedTable) -> DecisionTree: """ - Fit this model given a tagged table. + Create a new classifier based on this one and fit it with the given training data. This classifier is not + modified. Parameters ---------- training_set : TaggedTable - The tagged table containing the feature and target vectors. + The training data containing the feature and target vectors. + + Returns + ------- + fitted_classifier : DecisionTree + The fitted classifier. Raises ------ LearningError - If the tagged table contains invalid values or if the training failed. + If the training data contains invalid values or if the training failed. """ wrapped_classifier = sk_DecisionTreeClassifier() diff --git a/src/safeds/ml/classification/_gradient_boosting_classification.py b/src/safeds/ml/classification/_gradient_boosting_classification.py index 24326a94a..4252783a9 100644 --- a/src/safeds/ml/classification/_gradient_boosting_classification.py +++ b/src/safeds/ml/classification/_gradient_boosting_classification.py @@ -21,17 +21,23 @@ def __init__(self) -> None: def fit(self, training_set: TaggedTable) -> GradientBoosting: """ - Fit this model given a tagged table. + Create a new classifier based on this one and fit it with the given training data. This classifier is not + modified. Parameters ---------- training_set : TaggedTable - The tagged table containing the feature and target vectors. + The training data containing the feature and target vectors. + + Returns + ------- + fitted_classifier : GradientBoosting + The fitted classifier. Raises ------ LearningError - If the tagged table contains invalid values or if the training failed. + If the training data contains invalid values or if the training failed. """ wrapped_classifier = sk_GradientBoostingClassifier() diff --git a/src/safeds/ml/classification/_k_nearest_neighbors.py b/src/safeds/ml/classification/_k_nearest_neighbors.py index 71f1b2520..7a0c7a12f 100644 --- a/src/safeds/ml/classification/_k_nearest_neighbors.py +++ b/src/safeds/ml/classification/_k_nearest_neighbors.py @@ -27,17 +27,23 @@ def __init__(self, n_neighbors: int) -> None: def fit(self, training_set: TaggedTable) -> KNearestNeighbors: """ - Fit this model given a tagged table. + Create a new classifier based on this one and fit it with the given training data. This classifier is not + modified. Parameters ---------- training_set : TaggedTable - The tagged table containing the feature and target vectors. + The training data containing the feature and target vectors. + + Returns + ------- + fitted_classifier : KNearestNeighbors + The fitted classifier. Raises ------ LearningError - If the tagged table contains invalid values or if the training failed. + If the training data contains invalid values or if the training failed. """ wrapped_classifier = sk_KNeighborsClassifier(self._n_neighbors, n_jobs=-1) fit(wrapped_classifier, training_set) diff --git a/src/safeds/ml/classification/_logistic_regression.py b/src/safeds/ml/classification/_logistic_regression.py index 7a8b94604..46fcc102f 100644 --- a/src/safeds/ml/classification/_logistic_regression.py +++ b/src/safeds/ml/classification/_logistic_regression.py @@ -21,18 +21,25 @@ def __init__(self) -> None: def fit(self, training_set: TaggedTable) -> LogisticRegression: """ - Fit this model given a tagged table. + Create a new classifier based on this one and fit it with the given training data. This classifier is not + modified. Parameters ---------- training_set : TaggedTable - The tagged table containing the feature and target vectors. + The training data containing the feature and target vectors. + + Returns + ------- + fitted_classifier : LogisticRegression + The fitted classifier. Raises ------ LearningError - If the tagged table contains invalid values or if the training failed. + If the training data contains invalid values or if the training failed. """ + wrapped_classifier = sk_LogisticRegression(n_jobs=-1) fit(wrapped_classifier, training_set) diff --git a/src/safeds/ml/classification/_random_forest.py b/src/safeds/ml/classification/_random_forest.py index 777b4f620..9f8928f94 100644 --- a/src/safeds/ml/classification/_random_forest.py +++ b/src/safeds/ml/classification/_random_forest.py @@ -20,18 +20,25 @@ def __init__(self) -> None: def fit(self, training_set: TaggedTable) -> RandomForest: """ - Fit this model given a tagged table. + Create a new classifier based on this one and fit it with the given training data. This classifier is not + modified. Parameters ---------- training_set : TaggedTable - The tagged table containing the feature and target vectors. + The training data containing the feature and target vectors. + + Returns + ------- + fitted_classifier : RandomForest + The fitted classifier. Raises ------ LearningError - If the tagged table contains invalid values or if the training failed. + If the training data contains invalid values or if the training failed. """ + wrapped_classifier = sk_RandomForestClassifier(n_jobs=-1) fit(wrapped_classifier, training_set) diff --git a/src/safeds/ml/regression/_ada_boost.py b/src/safeds/ml/regression/_ada_boost.py index d5998cc21..52d1d42f3 100644 --- a/src/safeds/ml/regression/_ada_boost.py +++ b/src/safeds/ml/regression/_ada_boost.py @@ -21,18 +21,25 @@ def __init__(self) -> None: def fit(self, training_set: TaggedTable) -> AdaBoost: """ - Fit this model given a tagged table. + Create a new regressor based on this one and fit it with the given training data. This regressor is not + modified. Parameters ---------- training_set : TaggedTable - The tagged table containing the feature and target vectors. + The training data containing the feature and target vectors. + + Returns + ------- + fitted_regressor : AdaBoost + The fitted regressor. Raises ------ LearningError - If the tagged table contains invalid values or if the training failed. + If the training data contains invalid values or if the training failed. """ + wrapped_regressor = sk_AdaBoostRegressor() fit(wrapped_regressor, training_set) diff --git a/src/safeds/ml/regression/_decision_tree.py b/src/safeds/ml/regression/_decision_tree.py index 85f3d6bc4..5b71d8374 100644 --- a/src/safeds/ml/regression/_decision_tree.py +++ b/src/safeds/ml/regression/_decision_tree.py @@ -21,18 +21,25 @@ def __init__(self) -> None: def fit(self, training_set: TaggedTable) -> DecisionTree: """ - Fit this model given a tagged table. + Create a new regressor based on this one and fit it with the given training data. This regressor is not + modified. Parameters ---------- training_set : TaggedTable - The tagged table containing the feature and target vectors. + The training data containing the feature and target vectors. + + Returns + ------- + fitted_regressor : DecisionTree + The fitted regressor. Raises ------ LearningError - If the tagged table contains invalid values or if the training failed. + If the training data contains invalid values or if the training failed. """ + wrapped_regressor = sk_DecisionTreeRegressor() fit(wrapped_regressor, training_set) diff --git a/src/safeds/ml/regression/_elastic_net_regression.py b/src/safeds/ml/regression/_elastic_net_regression.py index cd31225b9..37f548d6a 100644 --- a/src/safeds/ml/regression/_elastic_net_regression.py +++ b/src/safeds/ml/regression/_elastic_net_regression.py @@ -21,18 +21,25 @@ def __init__(self) -> None: def fit(self, training_set: TaggedTable) -> ElasticNetRegression: """ - Fit this model given a tagged table. + Create a new regressor based on this one and fit it with the given training data. This regressor is not + modified. Parameters ---------- training_set : TaggedTable - The tagged table containing the feature and target vectors. + The training data containing the feature and target vectors. + + Returns + ------- + fitted_regressor : ElasticNetRegression + The fitted regressor. Raises ------ LearningError - If the tagged table contains invalid values or if the training failed. + If the training data contains invalid values or if the training failed. """ + wrapped_regressor = sk_ElasticNet() fit(wrapped_regressor, training_set) diff --git a/src/safeds/ml/regression/_gradient_boosting_regression.py b/src/safeds/ml/regression/_gradient_boosting_regression.py index 5a7c1f2c6..c87085d86 100644 --- a/src/safeds/ml/regression/_gradient_boosting_regression.py +++ b/src/safeds/ml/regression/_gradient_boosting_regression.py @@ -21,19 +21,25 @@ def __init__(self) -> None: def fit(self, training_set: TaggedTable) -> GradientBoosting: """ - Fit this model given a tagged table. + Create a new regressor based on this one and fit it with the given training data. This regressor is not + modified. Parameters - ---------- - tagged_table : SupervisedDataset - The tagged table containing the feature and target vectors. + training_set : TaggedTable + The training data containing the feature and target vectors. + + Returns + ------- + fitted_regressor : GradientBoosting + The fitted regressor. Raises ------ LearningError - If the tagged table contains invalid values or if the training failed. + If the training data contains invalid values or if the training failed. """ + wrapped_regressor = sk_GradientBoostingRegressor() fit(wrapped_regressor, training_set) diff --git a/src/safeds/ml/regression/_k_nearest_neighbors.py b/src/safeds/ml/regression/_k_nearest_neighbors.py index 6066c0198..c9b5fd3b5 100644 --- a/src/safeds/ml/regression/_k_nearest_neighbors.py +++ b/src/safeds/ml/regression/_k_nearest_neighbors.py @@ -27,18 +27,25 @@ def __init__(self, n_neighbors: int) -> None: def fit(self, training_set: TaggedTable) -> KNearestNeighbors: """ - Fit this model given a tagged table. + Create a new regressor based on this one and fit it with the given training data. This regressor is not + modified. Parameters ---------- training_set : TaggedTable - The tagged table containing the feature and target vectors. + The training data containing the feature and target vectors. + + Returns + ------- + fitted_regressor : KNearestNeighbors + The fitted regressor. Raises ------ LearningError - If the tagged table contains invalid values or if the training failed. + If the training data contains invalid values or if the training failed. """ + wrapped_regressor = sk_KNeighborsRegressor(self._n_neighbors, n_jobs=-1) fit(wrapped_regressor, training_set) diff --git a/src/safeds/ml/regression/_lasso_regression.py b/src/safeds/ml/regression/_lasso_regression.py index ad9ff6665..824107481 100644 --- a/src/safeds/ml/regression/_lasso_regression.py +++ b/src/safeds/ml/regression/_lasso_regression.py @@ -21,18 +21,25 @@ def __init__(self) -> None: def fit(self, training_set: TaggedTable) -> LassoRegression: """ - Fit this model given a tagged table. + Create a new regressor based on this one and fit it with the given training data. This regressor is not + modified. Parameters ---------- training_set : TaggedTable - The tagged table containing the feature and target vectors. + The training data containing the feature and target vectors. + + Returns + ------- + fitted_regressor : LassoRegression + The fitted regressor. Raises ------ LearningError - If the tagged table contains invalid values or if the training failed. + If the training data contains invalid values or if the training failed. """ + wrapped_regressor = sk_Lasso() fit(wrapped_regressor, training_set) diff --git a/src/safeds/ml/regression/_linear_regression.py b/src/safeds/ml/regression/_linear_regression.py index de8b0e4f6..d05286188 100644 --- a/src/safeds/ml/regression/_linear_regression.py +++ b/src/safeds/ml/regression/_linear_regression.py @@ -21,18 +21,25 @@ def __init__(self) -> None: def fit(self, training_set: TaggedTable) -> LinearRegression: """ - Fit this model given a tagged table. + Create a new regressor based on this one and fit it with the given training data. This regressor is not + modified. Parameters ---------- training_set : TaggedTable - The tagged table containing the feature and target vectors. + The training data containing the feature and target vectors. + + Returns + ------- + fitted_regressor : LinearRegression + The fitted regressor. Raises ------ LearningError - If the tagged table contains invalid values or if the training failed. + If the training data contains invalid values or if the training failed. """ + wrapped_regressor = sk_LinearRegression(n_jobs=-1) fit(wrapped_regressor, training_set) diff --git a/src/safeds/ml/regression/_random_forest.py b/src/safeds/ml/regression/_random_forest.py index 5c5fde720..9a49b1298 100644 --- a/src/safeds/ml/regression/_random_forest.py +++ b/src/safeds/ml/regression/_random_forest.py @@ -20,18 +20,25 @@ def __init__(self) -> None: def fit(self, training_set: TaggedTable) -> RandomForest: """ - Fit this model given a tagged table. + Create a new regressor based on this one and fit it with the given training data. This regressor is not + modified. Parameters ---------- training_set : TaggedTable - The tagged table containing the feature and target vectors. + The training data containing the feature and target vectors. + + Returns + ------- + fitted_regressor : RandomForest + The fitted regressor. Raises ------ LearningError - If the tagged table contains invalid values or if the training failed. + If the training data contains invalid values or if the training failed. """ + wrapped_regressor = sk_RandomForestRegressor(n_jobs=-1) fit(wrapped_regressor, training_set) diff --git a/src/safeds/ml/regression/_regressor.py b/src/safeds/ml/regression/_regressor.py index b593e5e2e..e84371399 100644 --- a/src/safeds/ml/regression/_regressor.py +++ b/src/safeds/ml/regression/_regressor.py @@ -16,17 +16,23 @@ class Regressor(ABC): @abstractmethod def fit(self, training_set: TaggedTable) -> Regressor: """ - Fit this model given a tagged table. + Create a new regressor based on this one and fit it with the given training data. This regressor is not + modified. Parameters ---------- training_set : TaggedTable - The tagged table containing the feature and target vectors. + The training data containing the feature and target vectors. + + Returns + ------- + fitted_regressor : Regressor + The fitted regressor. Raises ------ LearningError - If the tagged table contains invalid values or if the training failed. + If the training data contains invalid values or if the training failed. """ @abstractmethod diff --git a/src/safeds/ml/regression/_ridge_regression.py b/src/safeds/ml/regression/_ridge_regression.py index d39270dc9..bfcbe5404 100644 --- a/src/safeds/ml/regression/_ridge_regression.py +++ b/src/safeds/ml/regression/_ridge_regression.py @@ -21,18 +21,25 @@ def __init__(self) -> None: def fit(self, training_set: TaggedTable) -> RidgeRegression: """ - Fit this model given a tagged table. + Create a new regressor based on this one and fit it with the given training data. This regressor is not + modified. Parameters ---------- training_set : TaggedTable - The tagged table containing the feature and target vectors. + The training data containing the feature and target vectors. + + Returns + ------- + fitted_regressor : RidgeRegression + The fitted regressor. Raises ------ LearningError - If the tagged table contains invalid values or if the training failed. + If the training data contains invalid values or if the training failed. """ + wrapped_regressor = sk_Ridge() fit(wrapped_regressor, training_set) From 71107c3e7a29f82307573df294c960c78ef3a9c3 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 27 Mar 2023 12:05:19 +0200 Subject: [PATCH 4/6] docs: adjust tutorial to API changes --- docs/tutorials/machine_learning.ipynb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/tutorials/machine_learning.ipynb b/docs/tutorials/machine_learning.ipynb index 11c5641f3..809b85df5 100644 --- a/docs/tutorials/machine_learning.ipynb +++ b/docs/tutorials/machine_learning.ipynb @@ -60,7 +60,7 @@ "from safeds.ml.regression import LinearRegression\n", "\n", "model = LinearRegression()\n", - "model.fit(tagged_table)" + "fitted_model = model.fit(tagged_table)" ], "metadata": { "collapsed": false @@ -71,7 +71,7 @@ "source": [ "## Predicting new values\n", "\n", - "The `fit` method trains the model in place. This means that the model object is modified and can be used to make predictions. Predictions are made by calling the `predict` method on the model object. The `predict` method takes a `Table` as input and returns a `Table` with the predictions:" + "The `fit` method returns the fitted model, the original model is **not** changed. Predictions are made by calling the `predict` method on the fitted model. The `predict` method takes a `Table` as input and returns a `Table` with the predictions:" ], "metadata": { "collapsed": false @@ -87,7 +87,7 @@ " \"b\": [2, 0, 5, 2, 7],\n", " \"c\": [1, 4, 3, 2, 1]})\n", "\n", - "model.predict(dataset=test_set)\n" + "fitted_model.predict(dataset=test_set)\n" ], "metadata": { "collapsed": false From d507af830d9d41ee631a094e71185f7161ea444e Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 27 Mar 2023 12:19:18 +0200 Subject: [PATCH 5/6] fix: type hint --- src/safeds/ml/_util_sklearn.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/safeds/ml/_util_sklearn.py b/src/safeds/ml/_util_sklearn.py index fdf194af7..a403682f7 100644 --- a/src/safeds/ml/_util_sklearn.py +++ b/src/safeds/ml/_util_sklearn.py @@ -1,4 +1,4 @@ -from typing import Any +from typing import Any, Optional from safeds.data.tabular.containers import Table, TaggedTable from safeds.exceptions import LearningError, PredictionError @@ -34,7 +34,7 @@ def fit(model: Any, tagged_table: TaggedTable) -> None: # noinspection PyProtectedMember -def predict(model: Any, dataset: Table, target_name: str) -> TaggedTable: +def predict(model: Any, dataset: Table, target_name: Optional[str]) -> TaggedTable: """ Predict a target vector using a dataset containing feature vectors. The model has to be trained first. @@ -58,7 +58,7 @@ def predict(model: Any, dataset: Table, target_name: str) -> TaggedTable: If predicting with the given dataset failed. """ - if model is None: + if model is None or target_name is None: raise PredictionError("The model was not trained") dataset_df = dataset._data From 1ada7ffaa0dec765f3f393e14682d51260c995bb Mon Sep 17 00:00:00 2001 From: lars-reimann Date: Mon, 27 Mar 2023 10:21:20 +0000 Subject: [PATCH 6/6] style: apply automated linter fixes --- src/safeds/ml/classification/_ada_boost.py | 4 ++-- src/safeds/ml/classification/_decision_tree.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/safeds/ml/classification/_ada_boost.py b/src/safeds/ml/classification/_ada_boost.py index 4186cd100..4c6080d65 100644 --- a/src/safeds/ml/classification/_ada_boost.py +++ b/src/safeds/ml/classification/_ada_boost.py @@ -2,10 +2,10 @@ from typing import Optional -from sklearn.ensemble import AdaBoostClassifier as sk_AdaBoostClassifier - from safeds.data.tabular.containers import Table, TaggedTable from safeds.ml._util_sklearn import fit, predict +from sklearn.ensemble import AdaBoostClassifier as sk_AdaBoostClassifier + from ._classifier import Classifier diff --git a/src/safeds/ml/classification/_decision_tree.py b/src/safeds/ml/classification/_decision_tree.py index dfdf730fb..aa83ee77b 100644 --- a/src/safeds/ml/classification/_decision_tree.py +++ b/src/safeds/ml/classification/_decision_tree.py @@ -2,10 +2,10 @@ from typing import Optional -from sklearn.tree import DecisionTreeClassifier as sk_DecisionTreeClassifier - from safeds.data.tabular.containers import Table, TaggedTable from safeds.ml._util_sklearn import fit, predict +from sklearn.tree import DecisionTreeClassifier as sk_DecisionTreeClassifier + from ._classifier import Classifier