Skip to content

Commit a5e7dcc

Browse files
committed
Sieve: Switch to DomainModel, which also fixes bug with vizrank if the domain includes meta attributes
1 parent c35c195 commit a5e7dcc

File tree

2 files changed

+41
-42
lines changed

2 files changed

+41
-42
lines changed

Orange/widgets/utils/itemmodels.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,9 @@ def clear(self):
456456
def __len__(self):
457457
return len(self._list)
458458

459+
def __contains__(self, value):
460+
return value in self._list
461+
459462
def __iter__(self):
460463
return iter(self._list)
461464

Orange/widgets/visualize/owsieve.py

Lines changed: 38 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
from itertools import chain
2-
31
import numpy as np
42
from scipy.stats.distributions import chi2
53

64
from AnyQt.QtCore import Qt, QSize
75
from AnyQt.QtGui import QColor, QPen, QBrush
86
from AnyQt.QtWidgets import QGraphicsScene, QGraphicsLineItem, QSizePolicy
97

10-
from Orange.data import Table, filter
8+
from Orange.data import Table, filter, Variable
119
from Orange.data.sql.table import SqlTable, LARGE_TABLE, DEFAULT_SAMPLE_TIME
1210
from Orange.preprocess import Discretize
1311
from Orange.preprocess.discretize import EqualFreq
@@ -17,7 +15,7 @@
1715
from Orange.widgets.utils import to_html as to_html
1816
from Orange.widgets.utils.annotated_data import (create_annotated_table,
1917
ANNOTATED_DATA_SIGNAL_NAME)
20-
from Orange.widgets.utils.itemmodels import VariableListModel
18+
from Orange.widgets.utils.itemmodels import DomainModel
2119
from Orange.widgets.visualize.utils import (
2220
CanvasText, CanvasRectangle, ViewWithPress, VizRankDialogAttrPair)
2321
from Orange.widgets.widget import OWWidget, Default, AttributeList
@@ -50,7 +48,8 @@ def initialize(self):
5048
self.attrs = self.master.attrs
5149

5250
def compute_score(self, state):
53-
return ChiSqStats(self.master.discrete_data, *state).p
51+
return ChiSqStats(self.master.discrete_data,
52+
*(self.attrs[i].name for i in state)).p
5453

5554

5655
class OWSieveDiagram(OWWidget):
@@ -70,8 +69,8 @@ class OWSieveDiagram(OWWidget):
7069
want_control_area = False
7170

7271
settingsHandler = DomainContextHandler()
73-
attrX = ContextSetting("", exclude_metas=False)
74-
attrY = ContextSetting("", exclude_metas=False)
72+
attr_x = ContextSetting(None, exclude_metas=False)
73+
attr_y = ContextSetting(None, exclude_metas=False)
7574
selection = ContextSetting(set())
7675

7776
def __init__(self):
@@ -85,16 +84,15 @@ def __init__(self):
8584
self.selection = set()
8685

8786
self.attr_box = gui.hBox(self.mainArea)
88-
model = VariableListModel()
89-
model.wrap(self.attrs)
87+
self.domain_model = DomainModel(valid_types=DomainModel.PRIMITIVE)
9088
combo_args = dict(
9189
widget=self.attr_box, master=self, contentsLength=12,
9290
callback=self.update_attr, sendSelectedValue=True, valueType=str,
93-
model=model)
91+
model=self.domain_model)
9492
fixed_size = (QSizePolicy.Fixed, QSizePolicy.Fixed)
95-
self.attrXCombo = gui.comboBox(value="attrX", **combo_args)
93+
self.attr_x_combo = gui.comboBox(value="attr_x", **combo_args)
9694
gui.widgetLabel(self.attr_box, "\u2715", sizePolicy=fixed_size)
97-
self.attrYCombo = gui.comboBox(value="attrY", **combo_args)
95+
self.attr_y_combo = gui.comboBox(value="attr_y", **combo_args)
9896
self.vizrank, self.vizrank_button = SieveRank.add_vizrank(
9997
self.attr_box, self, "Score Combinations", self.set_attr)
10098
self.vizrank_button.setSizePolicy(*fixed_size)
@@ -124,7 +122,7 @@ def showEvent(self, event):
124122
def set_data(self, data):
125123
"""
126124
Discretize continuous attributes, and put all attributes and discrete
127-
metas into self.attrs, which is used as a model for combos.
125+
metas into self.attrs.
128126
129127
Select the first two attributes unless context overrides this.
130128
Method `resolve_shown_attributes` is called to use the attributes from
@@ -145,24 +143,22 @@ def set_data(self, data):
145143
self.selection = set()
146144
if self.data is None:
147145
self.attrs[:] = []
146+
self.domain_model.set_domain(None)
148147
else:
148+
self.domain_model.set_domain(data.domain)
149149
if any(attr.is_continuous for attr in data.domain):
150150
discretizer = Discretize(
151151
method=EqualFreq(n=4),
152152
discretize_classes=True, discretize_metas=True)
153153
self.discrete_data = discretizer(data)
154154
else:
155155
self.discrete_data = self.data
156-
self.attrs[:] = [
157-
var for var in chain(
158-
self.discrete_data.domain,
159-
(var for var in self.data.domain.metas if var.is_discrete))
160-
]
156+
self.attrs = [x for x in self.domain_model if isinstance(x, Variable)]
161157
if self.attrs:
162-
self.attrX = self.attrs[0].name
163-
self.attrY = self.attrs[len(self.attrs) > 1].name
158+
self.attr_x = self.attrs[0]
159+
self.attr_y = self.attrs[len(self.attrs) > 1]
164160
else:
165-
self.attrX = self.attrY = None
161+
self.attr_x = self.attr_y = None
166162
self.areas = []
167163
self.selection = set()
168164
self.openContext(self.data)
@@ -176,7 +172,7 @@ def set_data(self, data):
176172
len(self.data.domain.attributes) > 1)
177173

178174
def set_attr(self, attr_x, attr_y):
179-
self.attrX, self.attrY = attr_x.name, attr_y.name
175+
self.attr_x, self.attr_y = attr_x, attr_y
180176
self.update_attr()
181177

182178
def update_attr(self):
@@ -208,15 +204,15 @@ def resolve_shown_attributes(self):
208204
self.attr_box.setEnabled(True)
209205
if not self.input_features: # None or empty
210206
return
211-
features = [f for f in self.input_features if f in self.attrs]
207+
features = [f for f in self.input_features if f in self.domain_model]
212208
if not features:
213209
self.warning(
214210
"Features from the input signal are not present in the data")
215211
return
216-
old_attrs = self.attrX, self.attrY
217-
self.attrX, self.attrY = [f.name for f in (features * 2)[:2]]
212+
old_attrs = self.attr_x, self.attr_y
213+
self.attr_x, self.attr_y = [f for f in (features * 2)[:2]]
218214
self.attr_box.setEnabled(False)
219-
if (self.attrX, self.attrY) != old_attrs:
215+
if (self.attr_x, self.attr_y) != old_attrs:
220216
self.selection = set()
221217
self.update_graph()
222218

@@ -259,8 +255,8 @@ def update_selection(self):
259255
val_x, val_y = area.value_pair
260256
filts.append(
261257
filter.Values([
262-
filter.FilterDiscrete(self.attrX, [val_x]),
263-
filter.FilterDiscrete(self.attrY, [val_y])
258+
filter.FilterDiscrete(self.attr_x.name, [val_x]),
259+
filter.FilterDiscrete(self.attr_y.name, [val_y])
264260
]))
265261
else:
266262
width = 1
@@ -351,22 +347,22 @@ def make_tooltip():
351347
"""Create the tooltip. The function uses local variables from
352348
the enclosing scope."""
353349
# pylint: disable=undefined-loop-variable
354-
def _oper(attr_name, txt):
355-
if self.data.domain[attr_name] is ddomain[attr_name]:
350+
def _oper(attr, txt):
351+
if self.data.domain[attr.name] is ddomain[attr.name]:
356352
return "="
357353
return " " if txt[0] in "<≥" else " in "
358354

359355
return (
360-
"<b>{attrX}{xeq}{xval_name}</b>: {obs_x}/{n} ({p_x:.0f} %)".
361-
format(attrX=to_html(attr_x),
356+
"<b>{attr_x}{xeq}{xval_name}</b>: {obs_x}/{n} ({p_x:.0f} %)".
357+
format(attr_x=to_html(attr_x.name),
362358
xeq=_oper(attr_x, xval_name),
363359
xval_name=to_html(xval_name),
364360
obs_x=fmt(chi.probs_x[x] * n),
365361
n=int(n),
366362
p_x=100 * chi.probs_x[x]) +
367363
"<br/>" +
368-
"<b>{attrY}{yeq}{yval_name}</b>: {obs_y}/{n} ({p_y:.0f} %)".
369-
format(attrY=to_html(attr_y),
364+
"<b>{attr_y}{yeq}{yval_name}</b>: {obs_y}/{n} ({p_y:.0f} %)".
365+
format(attr_y=to_html(attr_y.name),
370366
yeq=_oper(attr_y, yval_name),
371367
yval_name=to_html(yval_name),
372368
obs_y=fmt(chi.probs_y[y] * n),
@@ -384,19 +380,19 @@ def _oper(attr_name, txt):
384380
for item in self.canvas.items():
385381
self.canvas.removeItem(item)
386382
if self.data is None or len(self.data) == 0 or \
387-
self.attrX is None or self.attrY is None:
383+
self.attr_x is None or self.attr_y is None:
388384
return
389385

390386
ddomain = self.discrete_data.domain
391-
attr_x, attr_y = self.attrX, self.attrY
392-
disc_x, disc_y = ddomain[attr_x], ddomain[attr_y]
387+
attr_x, attr_y = self.attr_x, self.attr_y
388+
disc_x, disc_y = ddomain[attr_x.name], ddomain[attr_y.name]
393389
view = self.canvasView
394390

395-
chi = ChiSqStats(self.discrete_data, attr_x, attr_y)
391+
chi = ChiSqStats(self.discrete_data, disc_x, disc_y)
396392
n = chi.n
397393
max_ylabel_w = max((width(val) for val in disc_y.values), default=0)
398394
max_ylabel_w = min(max_ylabel_w, 200)
399-
x_off = width(attr_x) + max_ylabel_w
395+
x_off = width(attr_x.name) + max_ylabel_w
400396
y_off = 15
401397
square_size = min(view.width() - x_off - 35, view.height() - y_off - 50)
402398
square_size = max(square_size, 10)
@@ -438,9 +434,9 @@ def _oper(attr_name, txt):
438434
curr_x += width
439435

440436
bottom = y_off + square_size + max_xlabel_h
441-
text(attr_y, 0, y_off + square_size / 2,
437+
text(attr_y.name, 0, y_off + square_size / 2,
442438
Qt.AlignLeft | Qt.AlignVCenter, bold=True, vertical=True)
443-
text(attr_x, x_off + square_size / 2, bottom,
439+
text(attr_x.name, x_off + square_size / 2, bottom,
444440
Qt.AlignHCenter | Qt.AlignTop, bold=True)
445441
xl = text("χ²={:.2f}, p={:.3f}".format(chi.chisq, chi.p),
446442
0, bottom)
@@ -449,7 +445,7 @@ def _oper(attr_name, txt):
449445

450446
def get_widget_name_extension(self):
451447
if self.data is not None:
452-
return "{} vs {}".format(self.attrX, self.attrY)
448+
return "{} vs {}".format(self.attr_x.name, self.attr_y.name)
453449

454450
def send_report(self):
455451
self.report_plot()

0 commit comments

Comments
 (0)