1- from itertools import chain
21import math
32
43import numpy as np
87from AnyQt .QtGui import QColor , QPen , QBrush
98from AnyQt .QtWidgets import QGraphicsScene , QGraphicsLineItem , QSizePolicy
109
11- from Orange .data import Table , filter
10+ from Orange .data import Table , filter , Variable
1211from Orange .data .sql .table import SqlTable , LARGE_TABLE , DEFAULT_SAMPLE_TIME
1312from Orange .preprocess import Discretize
1413from Orange .preprocess .discretize import EqualFreq
1514from Orange .statistics .contingency import get_contingency
16- from Orange .widgets import gui , widget
15+ from Orange .widgets import gui , widget , settings
1716from Orange .widgets .settings import DomainContextHandler , ContextSetting
1817from Orange .widgets .utils import to_html as to_html
1918from Orange .widgets .utils .annotated_data import (create_annotated_table ,
2019 ANNOTATED_DATA_SIGNAL_NAME )
21- from Orange .widgets .utils .itemmodels import VariableListModel
20+ from Orange .widgets .utils .itemmodels import DomainModel
2221from Orange .widgets .visualize .utils import (
2322 CanvasText , CanvasRectangle , ViewWithPress , VizRankDialogAttrPair )
2423from Orange .widgets .widget import OWWidget , Default , AttributeList
@@ -51,7 +50,8 @@ def initialize(self):
5150 self .attrs = self .master .attrs
5251
5352 def compute_score (self , state ):
54- p = ChiSqStats (self .master .discrete_data , * state ).p
53+ p = ChiSqStats (self .master .discrete_data ,
54+ * (self .attrs [i ].name for i in state )).p
5555 return 2 if np .isnan (p ) else p
5656
5757 def bar_length (self , score ):
@@ -74,9 +74,10 @@ class OWSieveDiagram(OWWidget):
7474
7575 want_control_area = False
7676
77+ settings_version = 1
7778 settingsHandler = DomainContextHandler ()
78- attrX = ContextSetting ("" , exclude_metas = False )
79- attrY = ContextSetting ("" , exclude_metas = False )
79+ attr_x = ContextSetting (None , exclude_metas = False )
80+ attr_y = ContextSetting (None , exclude_metas = False )
8081 selection = ContextSetting (set ())
8182
8283 def __init__ (self ):
@@ -90,16 +91,15 @@ def __init__(self):
9091 self .selection = set ()
9192
9293 self .attr_box = gui .hBox (self .mainArea )
93- model = VariableListModel ()
94- model .wrap (self .attrs )
94+ self .domain_model = DomainModel (valid_types = DomainModel .PRIMITIVE )
9595 combo_args = dict (
9696 widget = self .attr_box , master = self , contentsLength = 12 ,
9797 callback = self .update_attr , sendSelectedValue = True , valueType = str ,
98- model = model )
98+ model = self . domain_model )
9999 fixed_size = (QSizePolicy .Fixed , QSizePolicy .Fixed )
100- self . attrXCombo = gui .comboBox (value = "attrX " , ** combo_args )
100+ gui .comboBox (value = "attr_x " , ** combo_args )
101101 gui .widgetLabel (self .attr_box , "\u2715 " , sizePolicy = fixed_size )
102- self . attrYCombo = gui .comboBox (value = "attrY " , ** combo_args )
102+ gui .comboBox (value = "attr_y " , ** combo_args )
103103 self .vizrank , self .vizrank_button = SieveRank .add_vizrank (
104104 self .attr_box , self , "Score Combinations" , self .set_attr )
105105 self .vizrank_button .setSizePolicy (* fixed_size )
@@ -126,10 +126,17 @@ def showEvent(self, event):
126126 super ().showEvent (event )
127127 self .update_graph ()
128128
129+ @classmethod
130+ def migrate_context (cls , context , version ):
131+ if not version :
132+ settings .rename_setting (context , "attrX" , "attr_x" )
133+ settings .rename_setting (context , "attrY" , "attr_y" )
134+ settings .migrate_str_to_variable (context )
135+
129136 def set_data (self , data ):
130137 """
131138 Discretize continuous attributes, and put all attributes and discrete
132- metas into self.attrs, which is used as a model for combos .
139+ metas into self.attrs.
133140
134141 Select the first two attributes unless context overrides this.
135142 Method `resolve_shown_attributes` is called to use the attributes from
@@ -150,24 +157,22 @@ def set_data(self, data):
150157 self .selection = set ()
151158 if self .data is None :
152159 self .attrs [:] = []
160+ self .domain_model .set_domain (None )
153161 else :
162+ self .domain_model .set_domain (data .domain )
154163 if any (attr .is_continuous for attr in data .domain ):
155164 discretizer = Discretize (
156165 method = EqualFreq (n = 4 ),
157166 discretize_classes = True , discretize_metas = True )
158167 self .discrete_data = discretizer (data )
159168 else :
160169 self .discrete_data = self .data
161- self .attrs [:] = [
162- var for var in chain (
163- self .discrete_data .domain ,
164- (var for var in self .data .domain .metas if var .is_discrete ))
165- ]
170+ self .attrs = [x for x in self .domain_model if isinstance (x , Variable )]
166171 if self .attrs :
167- self .attrX = self .attrs [0 ]. name
168- self .attrY = self .attrs [len (self .attrs ) > 1 ]. name
172+ self .attr_x = self .attrs [0 ]
173+ self .attr_y = self .attrs [len (self .attrs ) > 1 ]
169174 else :
170- self .attrX = self .attrY = None
175+ self .attr_x = self .attr_y = None
171176 self .areas = []
172177 self .selection = set ()
173178 self .openContext (self .data )
@@ -181,7 +186,7 @@ def set_data(self, data):
181186 len (self .data .domain .attributes ) > 1 )
182187
183188 def set_attr (self , attr_x , attr_y ):
184- self .attrX , self .attrY = attr_x . name , attr_y . name
189+ self .attr_x , self .attr_y = attr_x , attr_y
185190 self .update_attr ()
186191
187192 def update_attr (self ):
@@ -213,15 +218,15 @@ def resolve_shown_attributes(self):
213218 self .attr_box .setEnabled (True )
214219 if not self .input_features : # None or empty
215220 return
216- features = [f for f in self .input_features if f in self .attrs ]
221+ features = [f for f in self .input_features if f in self .domain_model ]
217222 if not features :
218223 self .warning (
219224 "Features from the input signal are not present in the data" )
220225 return
221- old_attrs = self .attrX , self .attrY
222- self .attrX , self .attrY = [f . name for f in (features * 2 )[:2 ]]
226+ old_attrs = self .attr_x , self .attr_y
227+ self .attr_x , self .attr_y = [f for f in (features * 2 )[:2 ]]
223228 self .attr_box .setEnabled (False )
224- if (self .attrX , self .attrY ) != old_attrs :
229+ if (self .attr_x , self .attr_y ) != old_attrs :
225230 self .selection = set ()
226231 self .update_graph ()
227232
@@ -264,8 +269,8 @@ def update_selection(self):
264269 val_x , val_y = area .value_pair
265270 filts .append (
266271 filter .Values ([
267- filter .FilterDiscrete (self .attrX , [val_x ]),
268- filter .FilterDiscrete (self .attrY , [val_y ])
272+ filter .FilterDiscrete (self .attr_x . name , [val_x ]),
273+ filter .FilterDiscrete (self .attr_y . name , [val_y ])
269274 ]))
270275 else :
271276 width = 1
@@ -356,22 +361,22 @@ def make_tooltip():
356361 """Create the tooltip. The function uses local variables from
357362 the enclosing scope."""
358363 # pylint: disable=undefined-loop-variable
359- def _oper (attr_name , txt ):
360- if self .data .domain [attr_name ] is ddomain [attr_name ]:
364+ def _oper (attr , txt ):
365+ if self .data .domain [attr . name ] is ddomain [attr . name ]:
361366 return "="
362367 return " " if txt [0 ] in "<≥" else " in "
363368
364369 return (
365- "<b>{attrX }{xeq}{xval_name}</b>: {obs_x}/{n} ({p_x:.0f} %)" .
366- format (attrX = to_html (attr_x ),
370+ "<b>{attr_x }{xeq}{xval_name}</b>: {obs_x}/{n} ({p_x:.0f} %)" .
371+ format (attr_x = to_html (attr_x . name ),
367372 xeq = _oper (attr_x , xval_name ),
368373 xval_name = to_html (xval_name ),
369374 obs_x = fmt (chi .probs_x [x ] * n ),
370375 n = int (n ),
371376 p_x = 100 * chi .probs_x [x ]) +
372377 "<br/>" +
373- "<b>{attrY }{yeq}{yval_name}</b>: {obs_y}/{n} ({p_y:.0f} %)" .
374- format (attrY = to_html (attr_y ),
378+ "<b>{attr_y }{yeq}{yval_name}</b>: {obs_y}/{n} ({p_y:.0f} %)" .
379+ format (attr_y = to_html (attr_y . name ),
375380 yeq = _oper (attr_y , yval_name ),
376381 yval_name = to_html (yval_name ),
377382 obs_y = fmt (chi .probs_y [y ] * n ),
@@ -389,19 +394,19 @@ def _oper(attr_name, txt):
389394 for item in self .canvas .items ():
390395 self .canvas .removeItem (item )
391396 if self .data is None or len (self .data ) == 0 or \
392- self .attrX is None or self .attrY is None :
397+ self .attr_x is None or self .attr_y is None :
393398 return
394399
395400 ddomain = self .discrete_data .domain
396- attr_x , attr_y = self .attrX , self .attrY
397- disc_x , disc_y = ddomain [attr_x ], ddomain [attr_y ]
401+ attr_x , attr_y = self .attr_x , self .attr_y
402+ disc_x , disc_y = ddomain [attr_x . name ], ddomain [attr_y . name ]
398403 view = self .canvasView
399404
400- chi = ChiSqStats (self .discrete_data , attr_x , attr_y )
405+ chi = ChiSqStats (self .discrete_data , disc_x , disc_y )
401406 n = chi .n
402407 max_ylabel_w = max ((width (val ) for val in disc_y .values ), default = 0 )
403408 max_ylabel_w = min (max_ylabel_w , 200 )
404- x_off = width (attr_x ) + max_ylabel_w
409+ x_off = width (attr_x . name ) + max_ylabel_w
405410 y_off = 15
406411 square_size = min (view .width () - x_off - 35 , view .height () - y_off - 50 )
407412 square_size = max (square_size , 10 )
@@ -443,9 +448,9 @@ def _oper(attr_name, txt):
443448 curr_x += width
444449
445450 bottom = y_off + square_size + max_xlabel_h
446- text (attr_y , 0 , y_off + square_size / 2 ,
451+ text (attr_y . name , 0 , y_off + square_size / 2 ,
447452 Qt .AlignLeft | Qt .AlignVCenter , bold = True , vertical = True )
448- text (attr_x , x_off + square_size / 2 , bottom ,
453+ text (attr_x . name , x_off + square_size / 2 , bottom ,
449454 Qt .AlignHCenter | Qt .AlignTop , bold = True )
450455 xl = text ("χ²={:.2f}, p={:.3f}" .format (chi .chisq , chi .p ),
451456 0 , bottom )
@@ -454,7 +459,7 @@ def _oper(attr_name, txt):
454459
455460 def get_widget_name_extension (self ):
456461 if self .data is not None :
457- return "{} vs {}" .format (self .attrX , self .attrY )
462+ return "{} vs {}" .format (self .attr_x . name , self .attr_y . name )
458463
459464 def send_report (self ):
460465 self .report_plot ()
0 commit comments