Repository: biolab/orange3

Issue:
Fitter preprocessors
<!--
This is an issue template. Please fill in the relevant details in the
sections below.
-->

##### Orange version
<!-- From menu _Help→About→Version_ or code `Orange.version.full_version` -->
3.4.0

##### Expected behavior
Learners use preprocessors.


##### Actual behavior
Learners extending the Fitter base class do not use preprocessors.


##### Steps to reproduce the behavior
Use a learner on e.g. hearth_disease data set


##### Additional info (worksheets, data, screenshots, ...)





Discussion:
This is a very high priority bug in the latest version that needs a fix.
@pavlin-policar do you have time to check this out as you are most familiar with the code?
Sure thing, I'll get right on it.

Resolving patch:
diff --git a/Orange/modelling/base.py b/Orange/modelling/base.py
--- a/Orange/modelling/base.py
+++ b/Orange/modelling/base.py
@@ -55,6 +55,12 @@ def _fit_model(self, data):
             X, Y, W = data.X, data.Y, data.W if data.has_weights() else None
             return learner.fit(X, Y, W)
 
+    def preprocess(self, data):
+        if data.domain.has_discrete_class:
+            return self.get_learner(self.CLASSIFICATION).preprocess(data)
+        else:
+            return self.get_learner(self.REGRESSION).preprocess(data)
+
     def get_learner(self, problem_type):
         """Get the learner for a given problem type.
 


Test patch:
diff --git a/Orange/tests/test_fitter.py b/Orange/tests/test_fitter.py
--- a/Orange/tests/test_fitter.py
+++ b/Orange/tests/test_fitter.py
@@ -2,10 +2,10 @@
 from unittest.mock import Mock, patch
 
 from Orange.classification.base_classification import LearnerClassification
-from Orange.data import Table
+from Orange.data import Table, ContinuousVariable
 from Orange.evaluation import CrossValidation
 from Orange.modelling import Fitter
-from Orange.preprocess import Randomize
+from Orange.preprocess import Randomize, Discretize
 from Orange.regression.base_regression import LearnerRegression
 
 
@@ -130,3 +130,25 @@ def test_correctly_sets_preprocessors_on_learner(self):
     def test_n_jobs_fitting(self):
         with patch('Orange.evaluation.testing.CrossValidation._MIN_NJOBS_X_SIZE', 1):
             CrossValidation(self.heart_disease, [DummyFitter()], k=5, n_jobs=5)
+
+    def test_properly_delegates_preprocessing(self):
+        class DummyClassificationLearner(LearnerClassification):
+            preprocessors = [Discretize()]
+
+            def __init__(self, classification_param=1, **_):
+                super().__init__()
+                self.param = classification_param
+
+        class DummyFitter(Fitter):
+            __fits__ = {'classification': DummyClassificationLearner,
+                        'regression': DummyRegressionLearner}
+
+        data = self.heart_disease
+        fitter = DummyFitter()
+        # Sanity check
+        self.assertTrue(any(
+            isinstance(v, ContinuousVariable) for v in data.domain.variables))
+        # Preprocess the data and check that the discretization was applied
+        pp_data = fitter.preprocess(self.heart_disease)
+        self.assertTrue(not any(
+            isinstance(v, ContinuousVariable) for v in pp_data.domain.variables))