From 5dace185d11dfb1bc73c2e75a180f4bd552a603d Mon Sep 17 00:00:00 2001 From: PrimozGodec Date: Wed, 26 Jul 2023 11:28:19 +0200 Subject: [PATCH 1/5] Explainer - fix deprecations --- orangecontrib/explain/explainer.py | 2 +- orangecontrib/explain/tests/test_explainer.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/orangecontrib/explain/explainer.py b/orangecontrib/explain/explainer.py index dcaa155..568ff51 100644 --- a/orangecontrib/explain/explainer.py +++ b/orangecontrib/explain/explainer.py @@ -596,7 +596,7 @@ def get_instance_ordering( NotImplementedError if unknown order_by. """ if isinstance(order_by, Variable): - x_data = data.get_column_view(order_by)[0] + x_data = data.get_column(order_by) clust_ord = np.argsort(hclust_ordering(shap_values)) return np.lexsort([clust_ord, x_data]) elif order_by == ORIGINAL_ORDER: diff --git a/orangecontrib/explain/tests/test_explainer.py b/orangecontrib/explain/tests/test_explainer.py index 6dbe84b..77c8700 100644 --- a/orangecontrib/explain/tests/test_explainer.py +++ b/orangecontrib/explain/tests/test_explainer.py @@ -276,7 +276,7 @@ def test_all_regressors(self): if learner == CurveFitLearner: attr = self.housing.domain.attributes learner = CurveFitLearner( - lambda x, a: np.sum(x[:, i] for i in range(len(attr))), + lambda x, a: sum(x[:, i] for i in range(len(attr))), [], [a.name for a in self.housing.domain.attributes] ) else: From e6dcec23ab2fd37498c2886600f40831d05574cc Mon Sep 17 00:00:00 2001 From: PrimozGodec Date: Wed, 26 Jul 2023 11:36:56 +0200 Subject: [PATCH 2/5] owexplainfeaturebase - use deferred commit --- orangecontrib/explain/widgets/owexplainfeaturebase.py | 5 +++-- orangecontrib/explain/widgets/owexplainmodel.py | 2 +- orangecontrib/explain/widgets/owpermutationimportance.py | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/orangecontrib/explain/widgets/owexplainfeaturebase.py b/orangecontrib/explain/widgets/owexplainfeaturebase.py index 85abc83..eb68624 100644 --- a/orangecontrib/explain/widgets/owexplainfeaturebase.py +++ b/orangecontrib/explain/widgets/owexplainfeaturebase.py @@ -549,16 +549,17 @@ def _update_plot(self): def _clear_selection(self): if self.selection: self.selection = () - self.commit() + self.commit.deferred() def update_selection(self, *_): raise NotImplementedError def select_pending(self, pending_selection: Tuple): self.__pending_selection = pending_selection - self.unconditional_commit() + self.commit.now() # Outputs + @gui.deferred def commit(self): selected_data = self.get_selected_data() if not selected_data: diff --git a/orangecontrib/explain/widgets/owexplainmodel.py b/orangecontrib/explain/widgets/owexplainmodel.py index 7b7316f..a3a6e3e 100644 --- a/orangecontrib/explain/widgets/owexplainmodel.py +++ b/orangecontrib/explain/widgets/owexplainmodel.py @@ -421,7 +421,7 @@ def update_selection(self, min_val: float, max_val: float, attr_name: str): if not self.selection and not any(mask): return self.selection = (attr_name, list(np.flatnonzero(mask))) - self.commit() + self.commit.deferred() def select_pending(self, pending_selection: Tuple): if not pending_selection or not pending_selection[1] \ diff --git a/orangecontrib/explain/widgets/owpermutationimportance.py b/orangecontrib/explain/widgets/owpermutationimportance.py index 2484530..c77b4c5 100644 --- a/orangecontrib/explain/widgets/owpermutationimportance.py +++ b/orangecontrib/explain/widgets/owpermutationimportance.py @@ -315,7 +315,7 @@ def update_selection(self, attr_names: Set[str]): return assert self.results is not None self.selection = tuple(attr_names) - self.commit() + self.commit.deferred() def select_pending(self, pending_selection: Tuple): if not pending_selection or self.results is None: From 3d6fd373c5aeb6deead78e8eda668c0e6f0a4cfd Mon Sep 17 00:00:00 2001 From: PrimozGodec Date: Wed, 26 Jul 2023 11:44:18 +0200 Subject: [PATCH 3/5] owexplainpredictions - use deferred commit --- orangecontrib/explain/widgets/owexplainpredictions.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/orangecontrib/explain/widgets/owexplainpredictions.py b/orangecontrib/explain/widgets/owexplainpredictions.py index b77c57b..a50730f 100644 --- a/orangecontrib/explain/widgets/owexplainpredictions.py +++ b/orangecontrib/explain/widgets/owexplainpredictions.py @@ -587,7 +587,7 @@ def _add_plot(self): def __on_selection_changed(self, selection: List[Tuple[float, float]]): self.selection_ranges = selection - self.commit() + self.commit.deferred() def _add_controls(self): box = gui.vBox(self.controlArea, "Target class") @@ -624,12 +624,12 @@ def _add_controls(self): def __on_target_changed(self): self.selection_ranges = [] self.setup_plot() - self.commit() + self.commit.deferred() def __on_order_changed(self): self.selection_ranges = [] self.setup_plot() - self.commit() + self.commit.deferred() def __on_annot_changed(self): if not self.__results or not self.data: @@ -717,7 +717,7 @@ def _setup_controls(self): def handleNewSignals(self): self.clear() self.start(run, self.data, self.background_data, self.model) - self.commit() + self.commit.deferred() def clear(self): self.__results = None @@ -815,6 +815,7 @@ def apply_selection(self): self.__on_selection_changed(selection_ranges) self.__pending_selection = [] + @gui.deferred def commit(self): selected = None selected_indices = [] From 61705eeb95559e40694403c31d5106c9244bacb9 Mon Sep 17 00:00:00 2001 From: PrimozGodec Date: Wed, 26 Jul 2023 12:12:51 +0200 Subject: [PATCH 4/5] ICE - replace ListViewSearch with ListViewFilter --- orangecontrib/explain/widgets/owice.py | 25 ++++++++----------- .../explain/widgets/tests/test_owice.py | 17 ++++++++----- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/orangecontrib/explain/widgets/owice.py b/orangecontrib/explain/widgets/owice.py index 26f75ac..3764515 100644 --- a/orangecontrib/explain/widgets/owice.py +++ b/orangecontrib/explain/widgets/owice.py @@ -5,7 +5,7 @@ import numpy as np from AnyQt.QtCore import Qt, QSortFilterProxyModel, QSize, QModelIndex, \ - QItemSelection, QPointF, Signal, QLineF + QPointF, Signal, QLineF from AnyQt.QtGui import QColor from AnyQt.QtWidgets import QComboBox, QSizePolicy, QGraphicsSceneHelpEvent, \ QToolTip, QGraphicsLineItem, QApplication @@ -13,7 +13,7 @@ import pyqtgraph as pg from orangecanvas.gui.utils import disconnected -from orangewidget.utils.listview import ListViewSearch +from orangewidget.utils.listview import ListViewFilter from Orange.base import Model from Orange.data import Table, ContinuousVariable, Variable, \ @@ -261,7 +261,7 @@ def __on_mouse_moved(self, point: QPointF): ) # points - x_data = self.__data.get_column_view(self.__feature)[0][indices] + x_data = self.__data.get_column(self.__feature)[indices] n_dec = self.__feature.number_of_decimals y_data = [] for i, x in zip(indices, x_data): @@ -537,7 +537,7 @@ def __init__(self): self.domain: Optional[Domain] = None self.graph: ICEPlot = None self._target_combo: QComboBox = None - self._features_view: ListViewSearch = None + self._features_view: ListViewFilter = None self._features_model: VariableListModel = None self._color_model: DomainModel = None @@ -571,10 +571,10 @@ def _add_controls(self): box = gui.vBox(self.controlArea, "Feature") self._features_model = VariableListModel() sorted_model = SortProxyModel(sortRole=Qt.UserRole) - sorted_model.setSourceModel(self._features_model) sorted_model.sort(0) - self._features_view = ListViewSearch() - self._features_view.setModel(sorted_model) + self._features_view = ListViewFilter( + model=self._features_model, proxy=sorted_model + ) self._features_view.setMinimumSize(QSize(30, 100)) self._features_view.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) @@ -600,12 +600,9 @@ def __on_target_changed(self): self._apply_feature_sorting() self.__on_parameter_changed() - def __on_feature_changed(self, selection: QItemSelection): - if not selection: - return - - self.feature = selection.indexes()[0].data(gui.TableVariable) - self._apply_feature_sorting() + def __on_feature_changed(self): + selection = self._features_view.selectionModel().selectedIndexes() + self.feature = selection[0].data(gui.TableVariable) if selection else None self._run() def __on_order_changed(self): @@ -788,7 +785,7 @@ def setup_plot(self): color_labels = None if self.color_var and self.color_var.is_discrete: colors = self.color_var.colors - color_col = self.data[mask].get_column_view(self.color_var)[0] + color_col = self.data[mask].get_column(self.color_var) color_labels = self.color_var.values self.graph.set_data(self.data[mask], self.feature, diff --git a/orangecontrib/explain/widgets/tests/test_owice.py b/orangecontrib/explain/widgets/tests/test_owice.py index 5100bb5..c1538f6 100644 --- a/orangecontrib/explain/widgets/tests/test_owice.py +++ b/orangecontrib/explain/widgets/tests/test_owice.py @@ -1,12 +1,13 @@ # pylint: disable=missing-docstring import unittest -from unittest.mock import Mock +from unittest.mock import Mock, patch from AnyQt.QtCore import Qt, QPointF from Orange.classification import RandomForestLearner, CalibratedLearner, \ ThresholdLearner, SimpleRandomForestLearner as SimpleRandomForestClassifier from Orange.data import Table +from Orange.data.table import DomainTransformationError from Orange.regression import RandomForestRegressionLearner, \ SimpleRandomForestLearner from Orange.tests.test_classification import all_learners as all_cls_learners @@ -39,15 +40,19 @@ def test_input_cls(self): self.send_signal(self.widget.Inputs.model, self.rf_reg) self.wait_until_finished() - self.assertTrue(self.widget.Error.unknown_err.is_shown()) + # no error since no attributes in view and those no future is selected + self.assertFalse(self.widget.Error.unknown_err.is_shown()) self.send_signal(self.widget.Inputs.model, None) self.assertFalse(self.widget.Error.unknown_err.is_shown()) - self.send_signal(self.widget.Inputs.data, self.iris) - self.send_signal(self.widget.Inputs.model, self.rf_cls) - self.wait_until_finished() - self.assertTrue(self.widget.Error.domain_transform_err.is_shown()) + with patch( + "orangecontrib.explain.widgets.owice.individual_condition_expectation", + side_effect=DomainTransformationError + ): + self.send_signal(self.widget.Inputs.model, self.rf_cls) + self.wait_until_finished() + self.assertTrue(self.widget.Error.domain_transform_err.is_shown()) def test_output(self): self.send_signal(self.widget.Inputs.data, self.heart) From 4f6e91eee382842cb8dd38159a491f58a955b753 Mon Sep 17 00:00:00 2001 From: PrimozGodec Date: Wed, 26 Jul 2023 12:33:26 +0200 Subject: [PATCH 5/5] Update oldest orange-widget-base for ListViewFilter --- setup.py | 6 +++--- tox.ini | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index 020240b..558d64d 100644 --- a/setup.py +++ b/setup.py @@ -40,9 +40,9 @@ INSTALL_REQUIRES = [ "AnyQt", "numpy", - "Orange3 >= 3.33.0", - "orange-widget-base", - "orange-canvas-core", + "Orange3 >=3.34.0", + "orange-canvas-core >=0.1.28", + "orange-widget-base >=4.19.0", "pyqtgraph", "scipy", "shap >=0.42.1", diff --git a/tox.ini b/tox.ini index 343efee..d7e474d 100644 --- a/tox.ini +++ b/tox.ini @@ -23,9 +23,9 @@ deps = {env:WEBENGINE_PYPI_NAME:PyQtWebEngine}=={env:WEBENGINE_PYPI_VERSION:5.15.*} xgboost oldest: scikit-learn==1.0.1 - oldest: orange3==3.33.0 - oldest: orange-canvas-core==0.1.27 - oldest: orange-widget-base==4.18.0 + oldest: orange3==3.34.0 + oldest: orange-canvas-core==0.1.28 + oldest: orange-widget-base==4.19.0 latest: https://github.com/biolab/orange3/archive/refs/heads/master.zip#egg=orange3 latest: https://github.com/biolab/orange-canvas-core/archive/refs/heads/master.zip#egg=orange-canvas-core latest: https://github.com/biolab/orange-widget-base/archive/refs/heads/master.zip#egg=orange-widget-base