Skip to content

Commit

Permalink
Merge branch 'release-3.3.10'
Browse files Browse the repository at this point in the history
  • Loading branch information
astaric committed Jan 18, 2017
2 parents 6661596 + 3a5123a commit 5ff92a9
Show file tree
Hide file tree
Showing 30 changed files with 253 additions and 16 deletions.
36 changes: 35 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,39 @@ Change Log
* ...


[3.3.10] - 2017-01-18
--------------------
##### Enhancements
* Input/output signal replacement declarations ([#1810](../../pull/1810))

##### Bugfixes
* MDS Widget: Handle NaN values for plot point styling ([#1931](../../pull/1931))
* OWPCA: Fix crash for dataset with no rows or no attributes ([#1915](../../pull/1915))
* OWMosaic: Discretize metas as well ([#1912](../../pull/1912))
* owfeaturecontructor: Fix an IndexError accessing exception's args ([#1905](../../pull/1905))
* owrank: Remove `super()` call from `migrate_settings` ([#1902](../../pull/1902))
* OWBoxPlot: Fix ordering of boxes ([#1900](../../pull/1900))
* canvas/readwrite: Fix byte literal serialization ([#1898](../../pull/1898))
* owpca: Handle the case of 0 total variance in the PCA solution ([#1897](../../pull/1897))
* Copy data attributes for annotated data set ([#1895](../../pull/1895))
* colorpalette: Fix AttributeError ([#1889](../../pull/1889))
* OWDistributions: Reset combos when data is removed ([#1887](../../pull/1887))
* Concatenate bugfix ([#1886](../../pull/1886))
* OWPredictions: Fix crash when opening report ([#1884](../../pull/1884))
* owsilhouetteplot: Fix TypeError when cluster column is an object array ([#1876](../../pull/1876))
* OWSave: Safer Check if Writer Support Sparse ([#1864](../../pull/1864))
* OWImageViewer: Fix selection with missing values ([#1861](../../pull/1861))
* owselectcolumns: Fix auto commit on any change ([#1859](../../pull/1859))
* Table.transpose: Keep metas array two dimensional when no attributes in domain ([#1855](../../pull/1855))
* Select Rows filter enum ([#1854](../../pull/1854))
* Scatter plot: don't crash on report without data ([#1840](../../pull/1840))
* Crash on ctrl-c/cmd-c in widgets without graphs ([#1827](../../pull/1827))
* Fix crash in listview if labels are changed before calling __setitem__ ([#1825](../../pull/1825))
* Scatterplot: Allow labelling by string attributes ([#1812](../../pull/1812))
* Fix copy to clipboard in "Data Table" widget ([#1808](../../pull/1808))
* TreeGraph: Compatibility with old schemas ([#1804](../../pull/1804))


[3.3.9] - 2016-12-02
--------------------
##### Enhancements
Expand Down Expand Up @@ -314,7 +347,8 @@ Change Log
* Initial version based on Python 1.5.2 and Qt 2.3


[next]: https://github.com/biolab/orange3/compare/3.3.9...HEAD
[next]: https://github.com/biolab/orange3/compare/3.3.10...HEAD
[3.3.10]: https://github.com/biolab/orange3/compare/3.3.9...3.3.10
[3.3.9]: https://github.com/biolab/orange3/compare/3.3.8...3.3.9
[3.3.8]: https://github.com/biolab/orange3/compare/3.3.7...3.3.8
[3.3.7]: https://github.com/biolab/orange3/compare/3.3.6...3.3.7
Expand Down
4 changes: 4 additions & 0 deletions Orange/data/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@ def one_hot(values, dtype=float):
result
2d array with ones in respective indicator columns.
"""
if not len(values):
return np.zeros((0, 0), dtype=dtype)
return np.eye(int(np.max(values) + 1), dtype=dtype)[np.asanyarray(values, dtype=int)]


def scale(values, min=0, max=1):
"""Return values scaled to [min, max]"""
if not len(values):
return np.array([])
minval = np.float_(bn.nanmin(values))
ptp = bn.nanmax(values) - minval
if ptp == 0:
Expand Down
2 changes: 2 additions & 0 deletions Orange/tests/test_data_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ def test_scale(self):
np.testing.assert_equal(scale([0, 1, 2], -1, 1), [-1, 0, 1])
np.testing.assert_equal(scale([3, 3, 3]), [1, 1, 1])
np.testing.assert_equal(scale([.1, .5, np.nan]), [0, 1, np.nan])
np.testing.assert_equal(scale(np.array([])), np.array([]))

def test_one_hot(self):
np.testing.assert_equal(
one_hot([0, 1, 2, 1], int), [[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
[0, 1, 0]])
np.testing.assert_equal(one_hot([], int), np.zeros((0, 0), dtype=int))


class DummyPlus(SharedComputeValue):
Expand Down
4 changes: 2 additions & 2 deletions Orange/widgets/data/owcontinuize.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,12 @@ def constructContinuizer(self):

def commit(self):
continuizer = self.constructContinuizer()
if self.data is not None:
if self.data is not None and len(self.data):
domain = continuizer(self.data)
data = Table.from_table(domain, self.data)
self.send("Data", data)
else:
self.send("Data", None)
self.send("Data", self.data) # None or empty data

def send_report(self):
self.report_items(
Expand Down
5 changes: 4 additions & 1 deletion Orange/widgets/data/owdatasampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class Error(OWWidget.Error):
too_many_folds = Msg("Number of folds exceeds data size")
sample_larger_than_data = Msg("Sample must be smaller than data")
not_enough_to_stratify = Msg("Data is too small to stratify")
no_data = Msg("Data set is empty")

def __init__(self):
super().__init__()
Expand Down Expand Up @@ -228,7 +229,9 @@ def updateindices(self):
if self.data.domain.has_discrete_class else 0

size = None
if self.sampling_type == self.FixedSize:
if not data_length:
self.Error.no_data()
elif self.sampling_type == self.FixedSize:
size = self.sampleSizeNumber
repl = self.replacement
elif self.sampling_type == self.FixedProportion:
Expand Down
8 changes: 6 additions & 2 deletions Orange/widgets/data/owdiscretize.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,9 @@ def _update_points(self):
"""
Update the induced cut points.
"""
if self.data is None or not len(self.data):
return

def induce_cuts(method, data, var):
dvar = _dispatch[type(method)](method, data, var)
if dvar is None:
Expand All @@ -327,7 +330,6 @@ def induce_cuts(method, data, var):
points, dvar = induce_cuts(state.method, self.data, var)
new_state = state._replace(points=points, disc_var=dvar)
self._set_var_state(i, new_state)
self.commit()

def _method_index(self, method):
return METHODS.index((type(method), ))
Expand Down Expand Up @@ -389,6 +391,7 @@ def _default_disc_changed(self):
if isinstance(self.var_state[i].method, Default):
self._set_var_state(i, state)
self._update_points()
self.commit()

def _disc_method_changed(self):
self._update_spin_positions()
Expand All @@ -398,6 +401,7 @@ def _disc_method_changed(self):
for idx in indices:
self._set_var_state(idx, state)
self._update_points()
self.commit()

def _var_selection_changed(self, *args):
indices = self.selected_indices()
Expand Down Expand Up @@ -461,7 +465,7 @@ def disc_var(source):

def commit(self):
output = None
if self.data is not None:
if self.data is not None and len(self.data):
domain = self.discretized_domain()
output = self.data.from_table(domain, self.data)
self.send("Data", output)
Expand Down
5 changes: 5 additions & 0 deletions Orange/widgets/data/owimpute.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,11 @@ def commit(self):
data = self.data

if self.data is not None:
if not len(self.data):
self.send("Data", self.data)
self.modified = False
return

drop_mask = np.zeros(len(self.data), bool)

attributes = []
Expand Down
4 changes: 2 additions & 2 deletions Orange/widgets/data/owpaintdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -990,15 +990,15 @@ def set_data(self, data):
"""Set the input_data and call reset_to_input"""
def _check_and_set_data(data):
self.clear_messages()
if data is not None:
if data is not None and len(data):
if not data.domain.attributes:
self.Warning.no_input_variables()
data = None
elif len(data.domain.attributes) > 2:
self.Information.use_first_two()
self.input_data = data
self.btResetToInput.setDisabled(data is None)
return data is not None
return data is not None and len(data)

if not _check_and_set_data(data):
return
Expand Down
35 changes: 35 additions & 0 deletions Orange/widgets/data/tests/test_owcontinuize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Test methods with long descriptive names can omit docstrings
# pylint: disable=missing-docstring
import numpy as np

from Orange.data import Table
from Orange.widgets.data.owcontinuize import OWContinuize
from Orange.widgets.tests.base import WidgetTest


class TestOWContinuize(WidgetTest):
def setUp(self):
self.widget = self.create_widget(OWContinuize)

def test_empty_data(self):
"""No crash on empty data"""
data = Table("iris")
widget = self.widget
widget.multinomial_treatment = 1

self.send_signal("Data", data)
widget.unconditional_commit()
imp_data = self.get_output("Data")
np.testing.assert_equal(imp_data.X, data.X)
np.testing.assert_equal(imp_data.Y, data.Y)

widget.continuous_treatment = 1
self.send_signal("Data", Table(data.domain))
widget.unconditional_commit()
imp_data = self.get_output("Data")
self.assertEqual(len(imp_data), 0)

self.send_signal("Data", None)
widget.unconditional_commit()
imp_data = self.get_output("Data")
self.assertIsNone(imp_data)
2 changes: 2 additions & 0 deletions Orange/widgets/data/tests/test_owdatasampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ def test_error_message(self):
self.assertTrue(self.widget.Error.too_many_folds.is_shown())
self.send_signal("Data", None)
self.assertFalse(self.widget.Error.too_many_folds.is_shown())
self.send_signal("Data", Table(self.iris.domain))
self.assertTrue(self.widget.Error.no_data.is_shown())
18 changes: 18 additions & 0 deletions Orange/widgets/data/tests/test_owdiscretize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Test methods with long descriptive names can omit docstrings
# pylint: disable=missing-docstring
from Orange.data import Table
from Orange.widgets.data.owdiscretize import OWDiscretize
from Orange.widgets.tests.base import WidgetTest


class TestOWDiscretize(WidgetTest):
def setUp(self):
self.widget = self.create_widget(OWDiscretize)

def test_empty_data(self):
"""No crash on empty data"""
data = Table("iris")
widget = self.widget
widget.default_method = 3
self.send_signal("Data", Table(data.domain))
widget.unconditional_commit()
30 changes: 30 additions & 0 deletions Orange/widgets/data/tests/test_owimpute.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Test methods with long descriptive names can omit docstrings
# pylint: disable=missing-docstring
import numpy as np

from Orange.data import Table
from Orange.widgets.data.owimpute import OWImpute
from Orange.widgets.tests.base import WidgetTest


class TestOWImpute(WidgetTest):
def setUp(self):
self.widget = self.create_widget(OWImpute)

def test_empty_data(self):
"""No crash on empty data"""
data = Table("iris")
widget = self.widget
widget.default_method_index = widget.MODEL_BASED_IMPUTER
widget.default_method = widget.METHODS[widget.default_method_index]

self.send_signal("Data", data)
widget.unconditional_commit()
imp_data = self.get_output("Data")
np.testing.assert_equal(imp_data.X, data.X)
np.testing.assert_equal(imp_data.Y, data.Y)

self.send_signal("Data", Table(data.domain))
widget.unconditional_commit()
imp_data = self.get_output("Data")
self.assertEqual(len(imp_data), 0)
16 changes: 16 additions & 0 deletions Orange/widgets/data/tests/test_owpaintdata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Test methods with long descriptive names can omit docstrings
# pylint: disable=missing-docstring
from Orange.data import Table
from Orange.widgets.data.owpaintdata import OWPaintData
from Orange.widgets.tests.base import WidgetTest


class TestOWPaintData(WidgetTest):
def setUp(self):
self.widget = self.create_widget(OWPaintData)

def test_empty_data(self):
"""No crash on empty data"""
data = Table("iris")
self.send_signal("Data", data)
self.send_signal("Data", Table(data.domain))
6 changes: 6 additions & 0 deletions Orange/widgets/evaluate/owpredictions.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,12 @@ def set_predictor(self, predictor=None, id=None):

def handleNewSignals(self):
self.clear_messages()
if self.data is not None and not len(self.data):
# The logic of this widget is complicated, so let us just pretend
# that we have not data. (Even issuing an error message here doesn't
# work since the next signal would clear it.). If the widget
# shows nothing, the user will check the data table anyway.
self.data = None
if self.data is not None:
for inputid, pred in list(self.predictors.items()):
if pred.results is None or numpy.isnan(pred.results[0]).all():
Expand Down
13 changes: 12 additions & 1 deletion Orange/widgets/evaluate/owtestlearners.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ class OWTestLearners(OWWidget):
class_selection = settings.ContextSetting(TARGET_AVERAGE)

class Error(OWWidget.Error):
train_data_empty = Msg("Train data set is empty.")
test_data_empty = Msg("Test data set is empty.")
class_required = Msg("Train data input requires a target variable.")
too_many_classes = Msg("Too many target variables.")
class_required_test = Msg("Test data input requires a target variable.")
Expand Down Expand Up @@ -274,6 +276,10 @@ def set_train_data(self, data):
Set the input training dataset.
"""
self.Information.data_sampled.clear()
self.Error.train_data_empty.clear()
if data is not None and not len(data):
self.Error.train_data_empty()
data = None
if data and not data.domain.class_vars:
self.Error.class_required()
data = None
Expand Down Expand Up @@ -314,6 +320,10 @@ def set_test_data(self, data):
Set the input separate testing dataset.
"""
self.Information.test_data_sampled.clear()
self.Error.test_data_empty.clear()
if data is not None and not len(data):
self.Error.test_data_empty()
data = None
if data and not data.domain.class_var:
self.Error.class_required()
data = None
Expand Down Expand Up @@ -388,7 +398,8 @@ def _update_results(self):

if self.resampling == OWTestLearners.TestOnTest:
if self.test_data is None:
self.Warning.test_data_missing()
if not self.Error.test_data_empty.is_shown():
self.Warning.test_data_missing()
return
elif self.test_data.domain.class_var != class_var:
self.Error.class_inconsistent()
Expand Down
11 changes: 10 additions & 1 deletion Orange/widgets/unsupervised/owcorrespondence.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ class OWCorrespondenceAnalysis(widget.OWWidget):

graph_name = "plot.plotItem"

class Error(widget.OWWidget.Error):
empty_data = widget.Msg("Empty data set")

def __init__(self):
super().__init__()

Expand Down Expand Up @@ -95,8 +98,14 @@ def __init__(self):
def set_data(self, data):
self.closeContext()
self.clear()
self.data = data

if data is not None and not len(data):
self.Error.empty_data()
data = None
else:
self.Error.empty_data.clear()

self.data = data
if data is not None:
self.varlist[:] = [var for var in data.domain.variables
if var.is_discrete]
Expand Down
4 changes: 2 additions & 2 deletions Orange/widgets/unsupervised/owdistances.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class OWDistances(OWWidget):
class Error(OWWidget.Error):
no_continuous_features = Msg("No continuous features")
dense_metric_sparse_data = Msg("Selected metric does not support sparse data")
empty_data = Msg("Empty data (shape = {})")
empty_data = Msg("Empty data set")
too_few_observations = Msg("Too few observations for the number of dimensions")

class Warning(OWWidget.Warning):
Expand Down Expand Up @@ -127,7 +127,7 @@ def compute_distances(self, metric, data):
data = distance._preprocess(data)

if not data.X.size:
self.Error.empty_data(data.X.shape)
self.Error.empty_data()
return

if isinstance(metric, distance.MahalanobisDistance):
Expand Down
Loading

0 comments on commit 5ff92a9

Please sign in to comment.