diff --git a/orangecontrib/prototypes/widgets/owquickselect.py b/orangecontrib/prototypes/widgets/owquickselect.py index f50ee8ba..2f154b4e 100644 --- a/orangecontrib/prototypes/widgets/owquickselect.py +++ b/orangecontrib/prototypes/widgets/owquickselect.py @@ -4,7 +4,8 @@ from Orange.data import Table, DiscreteVariable from Orange.widgets import widget, gui, settings from Orange.widgets.utils.annotated_data import ANNOTATED_DATA_SIGNAL_NAME, \ - create_annotated_table + lazy_annotated_table +from Orange.widgets.utils.signals import LazyValue from Orange.widgets.utils.itemmodels import DomainModel from Orange.widgets.utils.state_summary import format_summary_details from Orange.widgets.utils.widgetpreview import WidgetPreview @@ -69,8 +70,6 @@ def set_data(self, data): self.closeContext() self.data = data self.Error.no_categorical.clear() - # TODO: Check that contexts are retrieved properly, also when removing - # and re-adding a connection if data: self.var_model.set_domain(data.domain) @@ -114,10 +113,15 @@ def commit(self): column = self.data.get_column(self.variable) valind = self.variable.values.index(self.value) mask = column == valind - annotated = create_annotated_table(self.data, np.flatnonzero(mask)) - matching = self.data[mask] - unmatched = self.data[~mask] - self.n_matched = len(matching) + annotated = lazy_annotated_table(self.data, np.flatnonzero(mask)) + self.n_matched = int(np.sum(mask)) + # Don't use `mask` in lambdas because it wastes memory + matching = LazyValue[Table]( + lambda: self.data[self.data.get_column(self.variable) == valind], + domain=self.data.domain, length=self.n_matched) + unmatched = LazyValue[Table]( + lambda: self.data[self.data.get_column(self.variable) != valind], + domain=self.data.domain, length=len(self.data) - self.n_matched) self.Outputs.matching.send(matching) self.Outputs.unmatched.send(unmatched) diff --git a/orangecontrib/prototypes/widgets/tests/test_owquickselect.py b/orangecontrib/prototypes/widgets/tests/test_owquickselect.py new file mode 100644 index 00000000..fc6f78c9 --- /dev/null +++ b/orangecontrib/prototypes/widgets/tests/test_owquickselect.py @@ -0,0 +1,176 @@ +import unittest + +import numpy as np + +from Orange.data import DiscreteVariable, ContinuousVariable, StringVariable, \ + Domain, Table +from Orange.widgets.tests.base import WidgetTest +from Orange.widgets.tests.utils import simulate +from orangecontrib.prototypes.widgets.owquickselect import OWQuickSelect + +class TestOWQuickSelect(WidgetTest): + def setUp(self): + self.widget: OWQuickSelect = self.create_widget(OWQuickSelect) + a = DiscreteVariable("A", values=("a", "b", "c")) + b = DiscreteVariable("B", values=("x", "y")) + c = DiscreteVariable("C", values=("1", "2", "3")) + d, e, f = (ContinuousVariable(c) for c in "DEF") + g = StringVariable("G") + self.domain = Domain([d, a, b, e], c, [f, g]) + + self.data = Table.from_numpy( + self.domain, + X=[[1, 0, 0, 2], + [0, 1, 1, 3], + [1, 2, 0, 4], + [2, np.nan, 1, 5], + [0, 0, 1, 6]], + Y=[0, 1, 0, 2, 2], + metas=[[2, "x"], [np.nan, "y"], [np.nan, "x"], [1, "y"], [2, "x"]] + + ) + + def test_model_and_settings(self): + w = self.widget + var_combo = w.controls.variable + value_combo = w.controls.value + + self.send_signal(self.data) + self.assertEqual(len(w.var_model), 3) + self.assertIs(w.variable, self.domain["A"]) + self.assertEqual(len(w.value_model), 3) + self.assertEqual(w.value_model[0], "a") + + simulate.combobox_activate_index(var_combo, 1) + self.assertEqual(len(w.value_model), 2) + self.assertEqual(w.value, "x") + + simulate.combobox_activate_index(value_combo, 1) + self.assertEqual(w.value, "y") + + self.send_signal(None) + self.assertEqual(len(w.var_model), 0) + self.assertIsNone(w.variable) + self.assertEqual(len(w.value_model), 0) + self.assertEqual(w.value, "") + + self.send_signal(w.Inputs.data, self.data) + self.assertEqual(len(w.var_model), 3) + self.assertIs(w.variable, self.domain["B"]) + self.assertEqual(len(w.value_model), 2) + self.assertEqual(w.value, "y") + + def test_no_categorical(self): + w = self.widget + no_cat = self.data.transform( + Domain( + [self.domain["D"], self.domain["E"]], + None, + [self.domain["F"], self.domain["G"]])) + + self.send_signal(no_cat) + self.assertEqual(len(w.var_model), 0) + self.assertIsNone(w.variable) + self.assertTrue(w.Error.no_categorical.is_shown()) + self.assertIsNone(self.get_output(w.Outputs.annotated)) + w.send_report() + + self.send_signal(None) + self.assertEqual(len(w.var_model), 0) + self.assertIsNone(w.variable) + self.assertFalse(w.Error.no_categorical.is_shown()) + self.assertIsNone(self.get_output(w.Outputs.annotated)) + w.send_report() + + self.send_signal(no_cat) + self.assertTrue(w.Error.no_categorical.is_shown()) + self.assertIsNone(self.get_output(w.Outputs.annotated)) + w.send_report() + + self.send_signal(self.data) + self.assertFalse(w.Error.no_categorical.is_shown()) + self.assertIsNotNone(self.get_output(w.Outputs.annotated)) + w.send_report() + + self.send_signal(no_cat) + self.assertIsNone(self.get_output(w.Outputs.annotated)) + + def test_missing_context_value(self): + w = self.widget + var_combo = w.controls.variable + value_combo = w.controls.value + + self.send_signal(self.data) + simulate.combobox_activate_index(var_combo, 1) + simulate.combobox_activate_index(value_combo, 1) + self.assertEqual(w.value, "y") + + new_b = DiscreteVariable("B", values=("x", )) + new_data = self.data.transform( + Domain(self.domain.attributes[:2] + (new_b, ), + self.domain.class_var, + self.domain.metas)) + + self.send_signal(None) + self.assertIsNone(w.variable) + self.assertEqual(w.value, "") + + self.send_signal(new_data) + self.assertIs(w.variable, new_b) + self.assertEqual(w.value, "x") + + def test_output(self): + w = self.widget + var_combo = w.controls.variable + value_combo = w.controls.value + + self.send_signal(self.data) + simulate.combobox_activate_index(var_combo, 1) + + np.testing.assert_equal( + self.get_output(w.Outputs.annotated).metas[:, -1], + [1, 0, 1, 0, 0]) + np.testing.assert_equal( + self.get_output(w.Outputs.matching).X, + self.data.X[[0, 2]]) + np.testing.assert_equal( + self.get_output(w.Outputs.unmatched).X, + self.data.X[[1, 3, 4]]) + + simulate.combobox_activate_index(value_combo, 1) + np.testing.assert_equal( + self.get_output(w.Outputs.annotated).metas[:, -1], + [0, 1, 0, 1, 1]) + np.testing.assert_equal( + self.get_output(w.Outputs.matching).X, + self.data.X[[1, 3, 4]]) + np.testing.assert_equal( + self.get_output(w.Outputs.unmatched).X, + self.data.X[[0, 2]]) + + # Test with nans + simulate.combobox_activate_index(var_combo, 0) + self.assertIs(w.variable, self.domain["A"]) + self.assertEqual(w.value, "a") + np.testing.assert_equal( + self.get_output(w.Outputs.annotated).metas[:, -1], + [1, 0, 0, 0, 1]) + + simulate.combobox_activate_index(value_combo, 1) + np.testing.assert_equal( + self.get_output(w.Outputs.annotated).metas[:, -1], + [0, 1, 0, 0, 0]) + + simulate.combobox_activate_index(value_combo, 2) + np.testing.assert_equal( + self.get_output(w.Outputs.annotated).metas[:, -1], + [0, 0, 1, 0, 0]) + + self.send_signal(None) + self.send_signal(self.data) + np.testing.assert_equal( + self.get_output(w.Outputs.annotated).metas[:, -1], + [0, 0, 1, 0, 0]) + +if __name__ == "__main__": + unittest.main() diff --git a/setup.py b/setup.py index 37b0df36..0ed939af 100644 --- a/setup.py +++ b/setup.py @@ -63,12 +63,9 @@ def include_documentation(local_dir, install_dir): "orangecontrib.prototypes.widgets": ["icons/*.svg", "tests/*.tab"] }, install_requires=[ - 'Orange3>=3.34', - 'numpy>=1.19.5', - 'scipy>=1.9.0', - 'scikit-learn>=1.0.1', - 'pyqtgraph', - 'AnyQt>=0.1.0', + 'Orange3>=3.36.2', + 'scipy>=1.9.2', + 'AnyQt>=0.2.0', 'pandas>=1.3.0', 'openai>=1', 'tiktoken', diff --git a/tox.ini b/tox.ini index 80d7c668..f42f8c54 100644 --- a/tox.ini +++ b/tox.ini @@ -21,12 +21,12 @@ setenv = deps = {env:PYQT_PYPI_NAME:PyQt5}=={env:PYQT_PYPI_VERSION:5.15.*} {env:WEBENGINE_PYPI_NAME:PyQtWebEngine}=={env:WEBENGINE_PYPI_VERSION:5.15.*} - oldest: orange3==3.34.0 - oldest: orange-canvas-core==0.1.28 - oldest: orange-widget-base==4.19.0 + oldest: orange3==3.36.2 + oldest: orange-canvas-core==0.1.30 + oldest: orange-widget-base==4.22.0 oldest: scipy==1.9.2 oldest: pandas==1.4.0 - oldest: anyqt==0.1.0 + oldest: anyqt==0.2.0 oldest: openai==1.0.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