Skip to content

Commit

Permalink
Allow PCA widget to be interrupted during preprocessing
Browse files Browse the repository at this point in the history
Code stolen from Learner and the Outliers widget
  • Loading branch information
markotoplak committed Aug 11, 2023
1 parent 5f63e6f commit e94858f
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 8 deletions.
34 changes: 28 additions & 6 deletions Orange/projection/base.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import warnings

import copy
import inspect
import threading
Expand All @@ -9,6 +11,7 @@
from Orange.data.util import SharedComputeValue, get_unique_names
from Orange.misc.wrapper_meta import WrapperMeta
from Orange.preprocess import RemoveNaNRows
from Orange.util import dummy_callback, wrap_callback, OrangeDeprecationWarning
import Orange.preprocess

__all__ = ["LinearCombinationSql", "Projector", "Projection", "SklProjector",
Expand Down Expand Up @@ -44,17 +47,36 @@ def fit(self, X, Y=None):
raise NotImplementedError(
"Classes derived from Projector must overload method fit")

def __call__(self, data):
data = self.preprocess(data)
def __call__(self, data, progress_callback=None):
if progress_callback is None:
progress_callback = dummy_callback
progress_callback(0, "Preprocessing...")
try:
cb = wrap_callback(progress_callback, end=0.1)
data = self.preprocess(data, progress_callback=cb)
except TypeError:
data = self.preprocess(data)
warnings.warn("A keyword argument 'progress_callback' has been "

Check warning on line 59 in Orange/projection/base.py

View check run for this annotation

Codecov / codecov/patch

Orange/projection/base.py#L57-L59

Added lines #L57 - L59 were not covered by tests
"added to the preprocess() signature. Implementing "
"the method without the argument is deprecated and "
"will result in an error in the future.",
OrangeDeprecationWarning, stacklevel=2)
self.domain = data.domain
progress_callback(0.1, "Fitting...")
clf = self.fit(data.X, data.Y)
clf.pre_domain = data.domain
clf.name = self.name
progress_callback(1)
return clf

def preprocess(self, data):
for pp in self.preprocessors:
def preprocess(self, data, progress_callback=None):
if progress_callback is None:
progress_callback = dummy_callback
n_pps = len(self.preprocessors)
for i, pp in enumerate(self.preprocessors):
progress_callback(i / n_pps)
data = pp(data)
progress_callback(1)
return data

# Projectors implemented using `fit` access the `domain` through the
Expand Down Expand Up @@ -208,8 +230,8 @@ def _get_sklparams(self, values):
raise TypeError("Wrapper does not define '__wraps__'")
return params

def preprocess(self, data):
data = super().preprocess(data)
def preprocess(self, data, progress_callback=None):
data = super().preprocess(data, progress_callback)
if any(v.is_discrete and len(v.values) > 2
for v in data.domain.attributes):
raise ValueError("Wrapped scikit-learn methods do not support "
Expand Down
12 changes: 10 additions & 2 deletions Orange/widgets/unsupervised/owpca.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,16 @@ def fit(self):
self.start(self._call_projector, data, projector)

@staticmethod
def _call_projector(data: Table, projector, _):
return projector(data)
def _call_projector(data: Table, projector, state):

def callback(i: float, status=""):
state.set_progress_value(i * 100)
if status:
state.set_status(status)
if state.is_interruption_requested():
raise Exception # pylint: disable=broad-exception-raised

return projector(data, progress_callback=callback)

def on_done(self, result):
pca = result
Expand Down

0 comments on commit e94858f

Please sign in to comment.