Skip to content

Commit

Permalink
feat: Support vector machine for regression and for classification (#236
Browse files Browse the repository at this point in the history
)

Closes #154.

### Summary of Changes
Added a new svm class with a wrapped svc of scikit-learn to both the ml
classification- and regression packages and tested the new class when
added to the test_classifier.py and test_regressor.py.
More than half of the time was spent at the level of testing due to
error configurations with poetry and pyproject.toml, git rebase and
merges.

---------

Co-authored-by: Junior Atemebang Ashu <jay@juniors-mbp.localdomain>
Co-authored-by: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com>
Co-authored-by: Lars Reimann <mail@larsreimann.com>
  • Loading branch information
4 people committed Apr 21, 2023
1 parent 0a9ce72 commit 7f6c3bd
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/safeds/ml/classical/classification/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from ._k_nearest_neighbors import KNearestNeighbors
from ._logistic_regression import LogisticRegression
from ._random_forest import RandomForest
from ._support_vector_machine import SupportVectorMachine

__all__ = [
"AdaBoost",
Expand All @@ -16,4 +17,5 @@
"KNearestNeighbors",
"LogisticRegression",
"RandomForest",
"SupportVectorMachine",
]
90 changes: 90 additions & 0 deletions src/safeds/ml/classical/classification/_support_vector_machine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from sklearn.svm import SVC as sk_SVC # noqa: N811

from safeds.ml.classical._util_sklearn import fit, predict

from ._classifier import Classifier

if TYPE_CHECKING:
from safeds.data.tabular.containers import Table, TaggedTable


class SupportVectorMachine(Classifier):
"""Support vector machine."""

def __init__(self) -> None:
self._wrapped_classifier: sk_SVC | None = None
self._feature_names: list[str] | None = None
self._target_name: str | None = None

def fit(self, training_set: TaggedTable) -> SupportVectorMachine:
"""
Create a copy of this classifier and fit it with the given training data.
This classifier is not modified.
Parameters
----------
training_set : TaggedTable
The training data containing the feature and target vectors.
Returns
-------
fitted_classifier : SupportVectorMachine
The fitted classifier.
Raises
------
LearningError
If the training data contains invalid values or if the training failed.
"""
wrapped_classifier = sk_SVC()
fit(wrapped_classifier, training_set)

result = SupportVectorMachine()
result._wrapped_classifier = wrapped_classifier
result._feature_names = training_set.features.column_names
result._target_name = training_set.target.name

return result

def predict(self, dataset: Table) -> TaggedTable:
"""
Predict a target vector using a dataset containing feature vectors. The model has to be trained first.
Parameters
----------
dataset : Table
The dataset containing the feature vectors.
Returns
-------
table : TaggedTable
A dataset containing the given feature vectors and the predicted target vector.
Raises
------
ModelNotFittedError
If the model has not been fitted yet.
DatasetContainsTargetError
If the dataset contains the target column already.
DatasetMissesFeaturesError
If the dataset misses feature columns.
PredictionError
If predicting with the given dataset failed.
"""
return predict(self._wrapped_classifier, dataset, self._feature_names, self._target_name)

def is_fitted(self) -> bool:
"""
Check if the classifier is fitted.
Returns
-------
is_fitted : bool
Whether the classifier is fitted.
"""
return self._wrapped_classifier is not None
2 changes: 2 additions & 0 deletions src/safeds/ml/classical/regression/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from ._random_forest import RandomForest
from ._regressor import Regressor
from ._ridge_regression import RidgeRegression
from ._support_vector_machine import SupportVectorMachine

__all__ = [
"AdaBoost",
Expand All @@ -22,4 +23,5 @@
"RandomForest",
"Regressor",
"RidgeRegression",
"SupportVectorMachine",
]
90 changes: 90 additions & 0 deletions src/safeds/ml/classical/regression/_support_vector_machine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from sklearn.svm import SVR as sk_SVR # noqa: N811

from safeds.ml.classical._util_sklearn import fit, predict

from ._regressor import Regressor

if TYPE_CHECKING:
from safeds.data.tabular.containers import Table, TaggedTable


class SupportVectorMachine(Regressor):
"""Support vector machine."""

def __init__(self) -> None:
self._wrapped_regressor: sk_SVR | None = None
self._feature_names: list[str] | None = None
self._target_name: str | None = None

def fit(self, training_set: TaggedTable) -> SupportVectorMachine:
"""
Create a copy of this regressor and fit it with the given training data.
This regressor is not modified.
Parameters
----------
training_set : TaggedTable
The training data containing the feature and target vectors.
Returns
-------
fitted_regressor : SupportVectorMachine
The fitted regressor.
Raises
------
LearningError
If the training data contains invalid values or if the training failed.
"""
wrapped_regressor = sk_SVR()
fit(wrapped_regressor, training_set)

result = SupportVectorMachine()
result._wrapped_regressor = wrapped_regressor
result._feature_names = training_set.features.column_names
result._target_name = training_set.target.name

return result

def predict(self, dataset: Table) -> TaggedTable:
"""
Predict a target vector using a dataset containing feature vectors. The model has to be trained first.
Parameters
----------
dataset : Table
The dataset containing the feature vectors.
Returns
-------
table : TaggedTable
A dataset containing the given feature vectors and the predicted target vector.
Raises
------
ModelNotFittedError
If the model has not been fitted yet.
DatasetContainsTargetError
If the dataset contains the target column already.
DatasetMissesFeaturesError
If the dataset misses feature columns.
PredictionError
If predicting with the given dataset failed.
"""
return predict(self._wrapped_regressor, dataset, self._feature_names, self._target_name)

def is_fitted(self) -> bool:
"""
Check if the regressor is fitted.
Returns
-------
is_fitted : bool
Whether the regressor is fitted.
"""
return self._wrapped_regressor is not None
11 changes: 10 additions & 1 deletion tests/safeds/ml/classical/classification/test_classifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
KNearestNeighbors,
LogisticRegression,
RandomForest,
SupportVectorMachine,
)
from safeds.ml.exceptions import (
DatasetContainsTargetError,
Expand All @@ -37,7 +38,15 @@ def classifiers() -> list[Classifier]:
classifiers : list[Classifier]
The list of classifiers to test.
"""
return [AdaBoost(), DecisionTree(), GradientBoosting(), KNearestNeighbors(2), LogisticRegression(), RandomForest()]
return [
AdaBoost(),
DecisionTree(),
GradientBoosting(),
KNearestNeighbors(2),
LogisticRegression(),
RandomForest(),
SupportVectorMachine(),
]


@pytest.fixture()
Expand Down
2 changes: 2 additions & 0 deletions tests/safeds/ml/classical/regression/test_regressor.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
RandomForest,
Regressor,
RidgeRegression,
SupportVectorMachine,
)

# noinspection PyProtectedMember
Expand Down Expand Up @@ -55,6 +56,7 @@ def regressors() -> list[Regressor]:
LinearRegression(),
RandomForest(),
RidgeRegression(),
SupportVectorMachine(),
]


Expand Down

0 comments on commit 7f6c3bd

Please sign in to comment.