Skip to content

Commit 4302132

Browse files
authored
Merge pull request #2336 from jerneju/memory-silhouette
[FIX] Silhouette Plot: handling memory error
2 parents e826256 + 29b4ccf commit 4302132

File tree

2 files changed

+51
-17
lines changed

2 files changed

+51
-17
lines changed

Orange/widgets/visualize/owsilhouetteplot.py

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
QGraphicsProxyWidget, QGraphicsItemGroup, QGraphicsSimpleTextItem,
1717
QGraphicsRectItem, QFrame, QSizePolicy
1818
)
19-
from AnyQt.QtGui import QColor, QPen, QBrush, QPainter, QFont, QFontMetrics
19+
from AnyQt.QtGui import QColor, QPen, QBrush, QPainter, QFontMetrics
2020
from AnyQt.QtCore import Qt, QEvent, QRectF, QSizeF, QSize, QPointF
2121
from AnyQt.QtCore import pyqtSignal as Signal
2222

@@ -83,6 +83,7 @@ class Outputs:
8383
class Error(widget.OWWidget.Error):
8484
need_two_clusters = Msg("Need at least two non-empty clusters")
8585
singleton_clusters_all = Msg("All clusters are singletons")
86+
memory_error = Msg("Not enough memory")
8687

8788
class Warning(widget.OWWidget.Warning):
8889
missing_cluster_assignment = Msg(
@@ -253,30 +254,44 @@ def _invalidate_scores(self):
253254

254255
def _update(self):
255256
# Update/recompute the distances/scores as required
257+
self._clear_messages()
258+
256259
if self.data is None or not len(self.data):
257-
self._mask = None
258-
self._silhouette = None
259-
self._labels = None
260-
self._matrix = None
261-
self._clear_scene()
260+
self._reset_all()
262261
return
263262

264263
if self._matrix is None and self._effective_data is not None:
265264
_, metric = self.Distances[self.distance_idx]
266-
self._matrix = numpy.asarray(metric(self._effective_data))
265+
try:
266+
self._matrix = numpy.asarray(metric(self._effective_data))
267+
except MemoryError:
268+
self.Error.memory_error()
269+
return
270+
271+
self._update_labels()
272+
273+
def _reset_all(self):
274+
self._mask = None
275+
self._silhouette = None
276+
self._labels = None
277+
self._matrix = None
278+
self._clear_scene()
267279

280+
def _clear_messages(self):
281+
self.Error.memory_error.clear()
282+
self.Error.singleton_clusters_all.clear()
283+
self.Error.need_two_clusters.clear()
284+
self.Warning.missing_cluster_assignment.clear()
285+
286+
def _update_labels(self):
268287
labelvar = self.cluster_var_model[self.cluster_var_idx]
269288
labels, _ = self.data.get_column_view(labelvar)
270289
labels = numpy.asarray(labels, dtype=float)
271290
mask = numpy.isnan(labels)
272291
labels = labels.astype(int)
273292
labels = labels[~mask]
274293

275-
labels_unq, counts = numpy.unique(labels, return_counts=True)
276-
277-
self.Error.singleton_clusters_all.clear()
278-
self.Error.need_two_clusters.clear()
279-
self.Warning.missing_cluster_assignment.clear()
294+
labels_unq, _ = numpy.unique(labels, return_counts=True)
280295

281296
if len(labels_unq) < 2:
282297
self.Error.need_two_clusters()
@@ -529,8 +544,8 @@ def setRowNames(self, names):
529544
else:
530545
tooltips = grp.rownames
531546

532-
for bar, tooltip in zip(baritems, tooltips):
533-
bar.setToolTip(tooltip)
547+
for baritem, tooltip in zip(baritems, tooltips):
548+
baritem.setToolTip(tooltip)
534549

535550
self.layout().activate()
536551

@@ -969,7 +984,7 @@ def __update(self):
969984

970985
pen = self.pen()
971986
brush = self.brush()
972-
for i in range(self.count()):
987+
for _ in range(self.count()):
973988
item = QGraphicsRectItem(self)
974989
item.setPen(pen)
975990
item.setBrush(brush)
@@ -1017,7 +1032,7 @@ def __init__(self, parent=None, items=None, **kwargs):
10171032
self.__spacing = 0
10181033

10191034
sp = QSizePolicy(QSizePolicy.Preferred,
1020-
QSizePolicy.Preferred)
1035+
QSizePolicy.Preferred)
10211036
sp.setWidthForHeight(True)
10221037
self.setSizePolicy(sp)
10231038

@@ -1113,8 +1128,10 @@ def remove(items, scene):
11131128
self.__textitems = []
11141129

11151130

1116-
def main(argv=sys.argv):
1131+
def main(argv=None):
11171132
from AnyQt.QtWidgets import QApplication
1133+
if argv is None:
1134+
argv = sys.argv
11181135
app = QApplication(list(argv))
11191136
argv = app.arguments()
11201137
if len(argv) > 1:

Orange/widgets/visualize/tests/test_owsilhouetteplot.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Test methods with long descriptive names can omit docstrings
22
# pylint: disable=missing-docstring
33
import random
4+
import unittest
45

56
import numpy as np
67

@@ -88,3 +89,19 @@ def test_meta_object_dtype(self):
8889
)
8990
data = data.from_table(domain, data)
9091
self.send_signal(self.widget.Inputs.data, data)
92+
93+
def test_memory_error(self):
94+
"""
95+
Handling memory error.
96+
GH-2336
97+
"""
98+
data = Orange.data.Table("iris")[::3]
99+
self.assertFalse(self.widget.Error.memory_error.is_shown())
100+
with unittest.mock.patch(
101+
"numpy.asarray",
102+
side_effect=MemoryError):
103+
self.widget._matrix = None
104+
self.widget.data = data
105+
self.widget._effective_data = data
106+
self.widget._update()
107+
self.assertTrue(self.widget.Error.memory_error.is_shown())

0 commit comments

Comments
 (0)