diff --git a/Orange/widgets/data/owconcatenate.py b/Orange/widgets/data/owconcatenate.py index 1a019c04fc2..0a839ad419b 100644 --- a/Orange/widgets/data/owconcatenate.py +++ b/Orange/widgets/data/owconcatenate.py @@ -28,9 +28,9 @@ class OWConcatenate(widget.OWWidget): icon = "icons/Concatenate.svg" inputs = [("Primary Data", Orange.data.Table, - "set_primary_data", widget.Default), + "set_primary_data"), ("Additional Data", Orange.data.Table, - "set_more_data", widget.Multiple)] + "set_more_data", widget.Multiple | widget.Default)] outputs = [("Data", Orange.data.Table)] #: Domain merging operations @@ -64,9 +64,9 @@ def __init__(self): self.primary_data = None self.more_data = OrderedDict() - mergebox = gui.vBox(self.controlArea, "Domain Merging") + self.mergebox = gui.vBox(self.controlArea, "Domain Merging") box = gui.radioButtons( - mergebox, self, "merge_type", + self.mergebox, self, "merge_type", callback=self._merge_type_changed) gui.widgetLabel( @@ -91,7 +91,8 @@ def __init__(self): cb = gui.checkBox( box, self, "append_source_column", - self.tr("Append data source IDs")) + self.tr("Append data source IDs"), + callback=self._source_changed) ibox = gui.indentedBox(box, sep=gui.checkButtonOffsetHint(cb)) @@ -104,12 +105,13 @@ def __init__(self): form.addRow( self.tr("Feature name:"), - gui.lineEdit(ibox, self, "source_attr_name", valueType=str)) + gui.lineEdit(ibox, self, "source_attr_name", valueType=str, + callback=self._source_changed)) form.addRow( self.tr("Place:"), - gui.comboBox(ibox, self, "source_column_role", items=self.id_roles) - ) + gui.comboBox(ibox, self, "source_column_role", items=self.id_roles, + callback=self._source_changed)) ibox.layout().addLayout(form) mleft, mtop, mright, _ = ibox.layout().getContentsMargins() @@ -135,6 +137,7 @@ def set_more_data(self, data=None, id=None): del self.more_data[id] def handleNewSignals(self): + self.mergebox.setDisabled(self.primary_data is not None) self.apply() def apply(self): @@ -180,6 +183,9 @@ def _merge_type_changed(self, ): if self.primary_data is None and self.more_data: self.apply() + def _source_changed(self): + self.apply() + def send_report(self): items = OrderedDict() if self.primary_data is not None: @@ -261,7 +267,7 @@ def ascolumn(array): metas = [ascolumn(col) for _, col in metas] X = numpy.hstack((data.X,) + tuple(attr_cols)) - Y = numpy.hstack((data.Y,) + tuple(class_cols)) + Y = numpy.hstack((data._Y,) + tuple(class_cols)) metas = numpy.hstack((data.metas,) + tuple(metas)) new_data = Orange.data.Table.from_numpy(new_domain, X, Y, metas) diff --git a/Orange/widgets/data/tests/test_owconcatenate.py b/Orange/widgets/data/tests/test_owconcatenate.py new file mode 100644 index 00000000000..0dfff09b968 --- /dev/null +++ b/Orange/widgets/data/tests/test_owconcatenate.py @@ -0,0 +1,98 @@ +# Test methods with long descriptive names can omit docstrings +# pylint: disable=missing-docstring + +import numpy as np + +from Orange.data import Table, Domain +from Orange.widgets.data.owconcatenate import OWConcatenate +from Orange.widgets.tests.base import WidgetTest + + +class TestOWConcatenate(WidgetTest): + + def setUp(self): + self.widget = self.create_widget(OWConcatenate) + self.iris = Table("iris") + self.titanic = Table("titanic") + + def test_single_input(self): + self.assertIsNone(self.get_output("Data")) + self.send_signal("Primary Data", self.iris) + output = self.get_output("Data") + self.assertEqual(list(output), list(self.iris)) + self.send_signal("Primary Data", None) + self.assertIsNone(self.get_output("Data")) + self.send_signal("Additional Data", self.iris) + output = self.get_output("Data") + self.assertEqual(list(output), list(self.iris)) + self.send_signal("Additional Data", None) + self.assertIsNone(self.get_output("Data")) + + def test_two_inputs_union(self): + self.send_signal("Additional Data", self.iris, 0) + self.send_signal("Additional Data", self.titanic, 1) + output = self.get_output("Data") + # needs to contain all instances + self.assertEqual(len(output), len(self.iris) + len(self.titanic)) + # needs to contain all variables + outvars = output.domain.variables + self.assertLess(set(self.iris.domain.variables), set(outvars)) + self.assertLess(set(self.titanic.domain.variables), set(outvars)) + # the first part of the data set is iris, the second part is titanic + np.testing.assert_equal(self.iris.X, output.X[:len(self.iris), :-3]) + self.assertTrue(np.isnan(output.X[:len(self.iris), -3:]).all()) + np.testing.assert_equal(self.titanic.X, output.X[len(self.iris):, -3:]) + self.assertTrue(np.isnan(output.X[len(self.iris):, :-3]).all()) + + def test_two_inputs_intersection(self): + self.send_signal("Additional Data", self.iris, 0) + self.send_signal("Additional Data", self.titanic, 1) + self.widget.controls.merge_type.buttons[1].click() + output = self.get_output("Data") + # needs to contain all instances + self.assertEqual(len(output), len(self.iris) + len(self.titanic)) + # no common variables + outvars = output.domain.variables + self.assertEqual(0, len(outvars)) + + def test_source(self): + self.send_signal("Additional Data", self.iris, 0) + self.send_signal("Additional Data", self.titanic, 1) + outputb = self.get_output("Data") + outvarsb = outputb.domain.variables + def get_source(): + output = self.get_output("Data") + outvars = output.domain.variables + output.domain.metas + return (set(outvars) - set(outvarsb)).pop() + # test adding source + self.widget.controls.append_source_column.toggle() + source = get_source() + self.assertEquals(source.name, "Source ID") + # test name changing + self.widget.controls.source_attr_name.setText("Source") + self.widget.controls.source_attr_name.callback() + source = get_source() + self.assertEquals(source.name, "Source") + # test source_column role + places = ["class_vars", "attributes", "metas"] + for i, place in enumerate(places): + self.widget.source_column_role = i + self.widget.apply() + source = get_source() + output = self.get_output("Data") + self.assertTrue(source in getattr(output.domain, place)) + data = Table(Domain([source]), output) + np.testing.assert_equal(data[:len(self.iris)].X, 0) + np.testing.assert_equal(data[len(self.iris):].X, 1) + + def test_singleclass_source_class(self): + self.send_signal("Primary Data", self.iris) + # add source into a class variable + self.widget.controls.append_source_column.toggle() + + def test_disable_merging_on_primary(self): + self.assertTrue(self.widget.mergebox.isEnabled()) + self.send_signal("Primary Data", self.iris) + self.assertFalse(self.widget.mergebox.isEnabled()) + self.send_signal("Primary Data", None) + self.assertTrue(self.widget.mergebox.isEnabled())