Skip to content

Commit 68c34fe

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

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
@@ -454,6 +454,9 @@ def clear(self):
454454
def __len__(self):
455455
return len(self._list)
456456

457+
def __contains__(self, value):
458+
return value in self._list
459+
457460
def __iter__(self):
458461
return iter(self._list)
459462

Orange/widgets/visualize/owsieve.py

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

64
from PyQt4.QtCore import Qt, QSize
75
from PyQt4.QtGui import (
86
QGraphicsScene, QColor, QPen, QBrush, QSizePolicy, QGraphicsLineItem)
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
1412
from Orange.statistics.contingency import get_contingency
1513
from Orange.widgets import gui
1614
from Orange.widgets.settings import DomainContextHandler, ContextSetting
1715
from Orange.widgets.utils import to_html as to_html
18-
from Orange.widgets.utils.itemmodels import VariableListModel
16+
from Orange.widgets.utils.itemmodels import DomainModel
1917
from Orange.widgets.visualize.utils import (
2018
CanvasText, CanvasRectangle, ViewWithPress, VizRankDialogAttrPair)
2119
from Orange.widgets.widget import OWWidget, Default, AttributeList
@@ -48,7 +46,8 @@ def initialize(self):
4846
self.attrs = self.master.attrs
4947

5048
def compute_score(self, state):
51-
return ChiSqStats(self.master.discrete_data, *state).p
49+
return ChiSqStats(self.master.discrete_data,
50+
*(self.attrs[i].name for i in state)).p
5251

5352

5453
class OWSieveDiagram(OWWidget):
@@ -67,8 +66,8 @@ class OWSieveDiagram(OWWidget):
6766
want_control_area = False
6867

6968
settingsHandler = DomainContextHandler()
70-
attrX = ContextSetting("", exclude_metas=False)
71-
attrY = ContextSetting("", exclude_metas=False)
69+
attr_x = ContextSetting(None, exclude_metas=False)
70+
attr_y = ContextSetting(None, exclude_metas=False)
7271
selection = ContextSetting(set())
7372

7473
def __init__(self):
@@ -82,16 +81,15 @@ def __init__(self):
8281
self.selection = set()
8382

8483
self.attr_box = gui.hBox(self.mainArea)
85-
model = VariableListModel()
86-
model.wrap(self.attrs)
84+
self.domain_model = DomainModel()
8785
combo_args = dict(
8886
widget=self.attr_box, master=self, contentsLength=12,
8987
callback=self.update_attr, sendSelectedValue=True, valueType=str,
90-
model=model)
88+
model=self.domain_model)
9189
fixed_size = (QSizePolicy.Fixed, QSizePolicy.Fixed)
92-
self.attrXCombo = gui.comboBox(value="attrX", **combo_args)
90+
self.attr_x_combo = gui.comboBox(value="attr_x", **combo_args)
9391
gui.widgetLabel(self.attr_box, "\u2715", sizePolicy=fixed_size)
94-
self.attrYCombo = gui.comboBox(value="attrY", **combo_args)
92+
self.attr_y_combo = gui.comboBox(value="attr_y", **combo_args)
9593
self.vizrank, self.vizrank_button = SieveRank.add_vizrank(
9694
self.attr_box, self, "Score Combinations", self.set_attr)
9795
self.vizrank_button.setSizePolicy(*fixed_size)
@@ -121,7 +119,7 @@ def showEvent(self, event):
121119
def set_data(self, data):
122120
"""
123121
Discretize continuous attributes, and put all attributes and discrete
124-
metas into self.attrs, which is used as a model for combos.
122+
metas into self.attrs.
125123
126124
Select the first two attributes unless context overrides this.
127125
Method `resolve_shown_attributes` is called to use the attributes from
@@ -142,24 +140,22 @@ def set_data(self, data):
142140
self.selection = set()
143141
if self.data is None:
144142
self.attrs[:] = []
143+
self.domain_model.set_domain(None)
145144
else:
145+
self.domain_model.set_domain(data.domain)
146146
if any(attr.is_continuous for attr in data.domain):
147147
discretizer = Discretize(
148148
method=EqualFreq(n=4),
149149
discretize_classes=True, discretize_metas=True)
150150
self.discrete_data = discretizer(data)
151151
else:
152152
self.discrete_data = self.data
153-
self.attrs[:] = [
154-
var for var in chain(
155-
self.discrete_data.domain,
156-
(var for var in self.data.domain.metas if var.is_discrete))
157-
]
153+
self.attrs = [x for x in self.domain_model if isinstance(x, Variable)]
158154
if self.attrs:
159-
self.attrX = self.attrs[0].name
160-
self.attrY = self.attrs[len(self.attrs) > 1].name
155+
self.attr_x = self.attrs[0]
156+
self.attr_y = self.attrs[len(self.attrs) > 1]
161157
else:
162-
self.attrX = self.attrY = None
158+
self.attr_x = self.attr_y = None
163159
self.areas = []
164160
self.selection = set()
165161
self.openContext(self.data)
@@ -173,7 +169,7 @@ def set_data(self, data):
173169
len(self.data.domain.attributes) > 1)
174170

175171
def set_attr(self, attr_x, attr_y):
176-
self.attrX, self.attrY = attr_x.name, attr_y.name
172+
self.attr_x, self.attr_y = attr_x, attr_y
177173
self.update_attr()
178174

179175
def update_attr(self):
@@ -205,15 +201,15 @@ def resolve_shown_attributes(self):
205201
self.attr_box.setEnabled(True)
206202
if not self.input_features: # None or empty
207203
return
208-
features = [f for f in self.input_features if f in self.attrs]
204+
features = [f for f in self.input_features if f in self.domain_model]
209205
if not features:
210206
self.warning(
211207
"Features from the input signal are not present in the data")
212208
return
213-
old_attrs = self.attrX, self.attrY
214-
self.attrX, self.attrY = [f.name for f in (features * 2)[:2]]
209+
old_attrs = self.attr_x, self.attr_y
210+
self.attr_x, self.attr_y = [f for f in (features * 2)[:2]]
215211
self.attr_box.setEnabled(False)
216-
if (self.attrX, self.attrY) != old_attrs:
212+
if (self.attr_x, self.attr_y) != old_attrs:
217213
self.selection = set()
218214
self.update_graph()
219215

@@ -254,8 +250,8 @@ def update_selection(self):
254250
val_x, val_y = area.value_pair
255251
filts.append(
256252
filter.Values([
257-
filter.FilterDiscrete(self.attrX, [val_x]),
258-
filter.FilterDiscrete(self.attrY, [val_y])
253+
filter.FilterDiscrete(self.attr_x.name, [val_x]),
254+
filter.FilterDiscrete(self.attr_y.name, [val_y])
259255
]))
260256
else:
261257
width = 1
@@ -344,22 +340,22 @@ def make_tooltip():
344340
"""Create the tooltip. The function uses local variables from
345341
the enclosing scope."""
346342
# pylint: disable=undefined-loop-variable
347-
def _oper(attr_name, txt):
348-
if self.data.domain[attr_name] is ddomain[attr_name]:
343+
def _oper(attr, txt):
344+
if self.data.domain[attr.name] is ddomain[attr.name]:
349345
return "="
350346
return " " if txt[0] in "<≥" else " in "
351347

352348
return (
353-
"<b>{attrX}{xeq}{xval_name}</b>: {obs_x}/{n} ({p_x:.0f} %)".
354-
format(attrX=to_html(attr_x),
349+
"<b>{attr_x}{xeq}{xval_name}</b>: {obs_x}/{n} ({p_x:.0f} %)".
350+
format(attr_x=to_html(attr_x.name),
355351
xeq=_oper(attr_x, xval_name),
356352
xval_name=to_html(xval_name),
357353
obs_x=fmt(chi.probs_x[x] * n),
358354
n=int(n),
359355
p_x=100 * chi.probs_x[x]) +
360356
"<br/>" +
361-
"<b>{attrY}{yeq}{yval_name}</b>: {obs_y}/{n} ({p_y:.0f} %)".
362-
format(attrY=to_html(attr_y),
357+
"<b>{attr_y}{yeq}{yval_name}</b>: {obs_y}/{n} ({p_y:.0f} %)".
358+
format(attr_y=to_html(attr_y.name),
363359
yeq=_oper(attr_y, yval_name),
364360
yval_name=to_html(yval_name),
365361
obs_y=fmt(chi.probs_y[y] * n),
@@ -377,19 +373,19 @@ def _oper(attr_name, txt):
377373
for item in self.canvas.items():
378374
self.canvas.removeItem(item)
379375
if self.data is None or len(self.data) == 0 or \
380-
self.attrX is None or self.attrY is None:
376+
self.attr_x is None or self.attr_y is None:
381377
return
382378

383379
ddomain = self.discrete_data.domain
384-
attr_x, attr_y = self.attrX, self.attrY
385-
disc_x, disc_y = ddomain[attr_x], ddomain[attr_y]
380+
attr_x, attr_y = self.attr_x, self.attr_y
381+
disc_x, disc_y = ddomain[attr_x.name], ddomain[attr_y.name]
386382
view = self.canvasView
387383

388-
chi = ChiSqStats(self.discrete_data, attr_x, attr_y)
384+
chi = ChiSqStats(self.discrete_data, disc_x, disc_y)
389385
n = chi.n
390386
max_ylabel_w = max((width(val) for val in disc_y.values), default=0)
391387
max_ylabel_w = min(max_ylabel_w, 200)
392-
x_off = width(attr_x) + max_ylabel_w
388+
x_off = width(attr_x.name) + max_ylabel_w
393389
y_off = 15
394390
square_size = min(view.width() - x_off - 35, view.height() - y_off - 50)
395391
square_size = max(square_size, 10)
@@ -431,9 +427,9 @@ def _oper(attr_name, txt):
431427
curr_x += width
432428

433429
bottom = y_off + square_size + max_xlabel_h
434-
text(attr_y, 0, y_off + square_size / 2,
430+
text(attr_y.name, 0, y_off + square_size / 2,
435431
Qt.AlignLeft | Qt.AlignVCenter, bold=True, vertical=True)
436-
text(attr_x, x_off + square_size / 2, bottom,
432+
text(attr_x.name, x_off + square_size / 2, bottom,
437433
Qt.AlignHCenter | Qt.AlignTop, bold=True)
438434
xl = text("χ²={:.2f}, p={:.3f}".format(chi.chisq, chi.p),
439435
0, bottom)
@@ -442,7 +438,7 @@ def _oper(attr_name, txt):
442438

443439
def get_widget_name_extension(self):
444440
if self.data is not None:
445-
return "{} vs {}".format(self.attrX, self.attrY)
441+
return "{} vs {}".format(self.attr_x.name, self.attr_y.name)
446442

447443
def send_report(self):
448444
self.report_plot()

0 commit comments

Comments
 (0)