Repository: biolab/orange3

Issue:
TypeError:  OneClassSVMLearner doesn't support multiple class variables
https://sentry.io/biolab/orange3/issues/202498152/

```
TypeError:  OneClassSVMLearner doesn't support multiple class variables
  Module "Orange.widgets.data.owoutliers", line 141, in commit

    y_pred = self.detect_outliers()
  Module "Orange.widgets.data.owoutliers", line 169, in detect_outliers

    model = learner(self.data)
  Module "Orange.base", line 253, in __call__

    m = super().__call__(data)
  Module "Orange.base", line 52, in __call__

    self.__class__.__name__)
```


Discussion:
The PR that closed this was a temporary workaround.
Reopening the issues until a proper fix.
Decision: @lanzagar will check the "temporary fix" again.

We may close this issue and open another instead: check the error messages what widgets show when the data has multiple classes.

Resolving patch:
diff --git a/Orange/classification/elliptic_envelope.py b/Orange/classification/elliptic_envelope.py
--- a/Orange/classification/elliptic_envelope.py
+++ b/Orange/classification/elliptic_envelope.py
@@ -1,7 +1,7 @@
 import sklearn.covariance as skl_covariance
 
 from Orange.base import SklLearner, SklModel
-from Orange.data import Table
+from Orange.data import Table, Domain
 from Orange.preprocess import Continuize, RemoveNaNColumns, SklImpute
 
 __all__ = ["EllipticEnvelopeLearner"]
@@ -35,3 +35,7 @@ def __init__(self, store_precision=True, assume_centered=False,
                  random_state=None, preprocessors=None):
         super().__init__(preprocessors=preprocessors)
         self.params = vars()
+
+    def __call__(self, data):
+        classless_data = data.transform(Domain(data.domain.attributes))
+        return super().__call__(classless_data)
diff --git a/Orange/classification/svm.py b/Orange/classification/svm.py
--- a/Orange/classification/svm.py
+++ b/Orange/classification/svm.py
@@ -1,7 +1,8 @@
 import sklearn.svm as skl_svm
 
-from Orange.classification import SklLearner, SklModel
 from Orange.base import SklLearner as SklLearnerBase
+from Orange.classification import SklLearner, SklModel
+from Orange.data import Domain
 from Orange.preprocess import AdaptiveNormalize
 
 __all__ = ["SVMLearner", "LinearSVMLearner", "NuSVMLearner",
@@ -77,6 +78,10 @@ def __init__(self, kernel='rbf', degree=3, gamma="auto", coef0=0.0,
         super().__init__(preprocessors=preprocessors)
         self.params = vars()
 
+    def __call__(self, data):
+        classless_data = data.transform(Domain(data.domain.attributes))
+        return super().__call__(classless_data)
+
     def fit(self, X, Y=None, W=None):
         clf = self.__wraps__(**self.params)
         if W is not None:
diff --git a/Orange/widgets/data/owoutliers.py b/Orange/widgets/data/owoutliers.py
--- a/Orange/widgets/data/owoutliers.py
+++ b/Orange/widgets/data/owoutliers.py
@@ -183,9 +183,8 @@ def detect_outliers(self):
                 support_fraction=self.support_fraction
                 if self.empirical_covariance else None,
                 contamination=self.cont / 100.)
-        data = self.data.transform(Domain(self.data.domain.attributes))
-        model = learner(data)
-        y_pred = model(data)
+        model = learner(self.data)
+        y_pred = model(self.data)
         amended_data = self.amended_data(model)
         return np.array(y_pred), amended_data
 


Test patch:
diff --git a/Orange/tests/test_elliptic_envelope.py b/Orange/tests/test_elliptic_envelope.py
--- a/Orange/tests/test_elliptic_envelope.py
+++ b/Orange/tests/test_elliptic_envelope.py
@@ -41,3 +41,22 @@ def test_mahalanobis(self):
         y_mahal, y_pred = zip(*sorted(zip(y_mahal, y_pred), reverse=True))
         self.assertTrue(all(i == -1 for i in y_pred[:int(self.cont * n)]))
         self.assertTrue(all(i == 1 for i in y_pred[int(self.cont * n):]))
+
+    def test_EllipticEnvelope_ignores_y(self):
+        domain = Domain((ContinuousVariable("x1"), ContinuousVariable("x2")),
+                        class_vars=(ContinuousVariable("y1"), ContinuousVariable("y2")))
+        X = np.random.random((40, 2))
+        Y = np.random.random((40, 2))
+        table = Table(domain, X, Y)
+        classless_table = table.transform(Domain(table.domain.attributes))
+        learner = EllipticEnvelopeLearner()
+        classless_model = learner(classless_table)
+        model = learner(table)
+        pred1 = classless_model(classless_table)
+        pred2 = classless_model(table)
+        pred3 = model(classless_table)
+        pred4 = model(table)
+
+        np.testing.assert_array_equal(pred1, pred2)
+        np.testing.assert_array_equal(pred2, pred3)
+        np.testing.assert_array_equal(pred3, pred4)
diff --git a/Orange/tests/test_svm.py b/Orange/tests/test_svm.py
--- a/Orange/tests/test_svm.py
+++ b/Orange/tests/test_svm.py
@@ -88,3 +88,22 @@ def test_OneClassSVM(self):
         self.assertLess(np.absolute(n_pred_out_all - n_true_out), 2)
         self.assertLess(np.absolute(n_pred_in_true_in - n_true_in), 4)
         self.assertLess(np.absolute(n_pred_out_true_out - n_true_out), 3)
+
+    def test_OneClassSVM_ignores_y(self):
+        domain = Domain((ContinuousVariable("x1"), ContinuousVariable("x2")),
+                        class_vars=(ContinuousVariable("y1"), ContinuousVariable("y2")))
+        X = np.random.random((40, 2))
+        Y = np.random.random((40, 2))
+        table = Table(domain, X, Y)
+        classless_table = table.transform(Domain(table.domain.attributes))
+        learner = OneClassSVMLearner()
+        classless_model = learner(classless_table)
+        model = learner(table)
+        pred1 = classless_model(classless_table)
+        pred2 = classless_model(table)
+        pred3 = model(classless_table)
+        pred4 = model(table)
+
+        np.testing.assert_array_equal(pred1, pred2)
+        np.testing.assert_array_equal(pred2, pred3)
+        np.testing.assert_array_equal(pred3, pred4)