1- from itertools import chain
2-
31import numpy as np
42from scipy .stats .distributions import chi2
53
64from AnyQt .QtCore import Qt , QSize
75from AnyQt .QtGui import QColor , QPen , QBrush
86from AnyQt .QtWidgets import QGraphicsScene , QGraphicsLineItem , QSizePolicy
97
10- from Orange .data import Table , filter
8+ from Orange .data import Table , filter , Variable
119from Orange .data .sql .table import SqlTable , LARGE_TABLE , DEFAULT_SAMPLE_TIME
1210from Orange .preprocess import Discretize
1311from Orange .preprocess .discretize import EqualFreq
1715from Orange .widgets .utils import to_html as to_html
1816from 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
2119from Orange .widgets .visualize .utils import (
2220 CanvasText , CanvasRectangle , ViewWithPress , VizRankDialogAttrPair )
2321from 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
5655class 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