2121from Orange .widgets import widget , gui
2222from Orange .widgets .settings import (Setting , DomainContextHandler ,
2323 ContextSetting )
24- from Orange .widgets .utils .itemmodels import DomainModel , VariableListModel
24+ from Orange .widgets .utils .itemmodels import VariableListModel
2525from Orange .widgets .utils .annotated_data import (create_annotated_table ,
2626 ANNOTATED_DATA_SIGNAL_NAME )
2727from Orange .widgets .utils .widgetpreview import WidgetPreview
@@ -154,6 +154,7 @@ class Outputs:
154154
155155 attribute = ContextSetting (None )
156156 order_by_importance = Setting (False )
157+ order_grouping_by_importance = Setting (False )
157158 group_var = ContextSetting (None )
158159 show_annotations = Setting (True )
159160 compare = Setting (CompareMeans )
@@ -213,19 +214,20 @@ def __init__(self):
213214 # set the minimal height (see the penultimate paragraph of
214215 # http://doc.qt.io/qt-4.8/qabstractscrollarea.html#addScrollBarWidget)
215216 view .setSizePolicy (QSizePolicy .Expanding , QSizePolicy .Ignored )
216- gui .separator (view .box , 6 , 6 )
217- self .cb_order = gui .checkBox (
217+ gui .checkBox (
218218 view .box , self , "order_by_importance" ,
219- "Order by relevance" ,
219+ "Order by relevance to subgroups " ,
220220 tooltip = "Order by 𝜒² or ANOVA over the subgroups" ,
221- callback = self .apply_sorting )
222- self .group_vars = DomainModel (
223- placeholder = "None" , separators = False ,
224- valid_types = Orange .data .DiscreteVariable )
225- self .group_view = view = gui .listView (
221+ callback = self .apply_attr_sorting )
222+ self .group_vars = VariableListModel (placeholder = "None" )
223+ view = gui .listView (
226224 self .controlArea , self , "group_var" , box = "Subgroups" ,
227225 model = self .group_vars , callback = self .grouping_changed )
228- view .setEnabled (False )
226+ gui .checkBox (
227+ view .box , self , "order_grouping_by_importance" ,
228+ "Order by relevance to variable" ,
229+ tooltip = "Order by 𝜒² or ANOVA over the variable values" ,
230+ callback = self .apply_group_sorting )
229231 view .setMinimumSize (QSize (30 , 30 ))
230232 # See the comment above
231233 view .setSizePolicy (QSizePolicy .Expanding , QSizePolicy .Ignored )
@@ -258,7 +260,6 @@ def __init__(self):
258260 self .sort_cb = gui .checkBox (
259261 box , self , 'sort_freqs' , "Sort by subgroup frequencies" ,
260262 callback = self .display_changed )
261- gui .rubber (box )
262263
263264 gui .vBox (self .mainArea , addSpace = True )
264265 self .box_scene = QGraphicsScene ()
@@ -290,12 +291,20 @@ def eventFilter(self, obj, event):
290291
291292 return super ().eventFilter (obj , event )
292293
293- def reset_attrs (self , domain ):
294+ def reset_attrs (self ):
295+ domain = self .dataset .domain
294296 self .attrs [:] = [
295297 var for var in chain (
296298 domain .class_vars , domain .metas , domain .attributes )
297299 if var .is_primitive ()]
298300
301+ def reset_groups (self ):
302+ domain = self .dataset .domain
303+ self .group_vars [:] = [None ] + [
304+ var for var in chain (
305+ domain .class_vars , domain .metas , domain .attributes )
306+ if var .is_discrete ]
307+
299308 # noinspection PyTypeChecker
300309 @Inputs .data
301310 def set_data (self , dataset ):
@@ -309,19 +318,19 @@ def set_data(self, dataset):
309318 self .group_var = None
310319 self .attribute = None
311320 if dataset :
312- domain = dataset .domain
313- self .group_vars .set_domain (domain )
314- self .group_view .setEnabled (len (self .group_vars ) > 1 )
315- self .reset_attrs (domain )
316- self .select_default_variables (domain )
321+ self .reset_attrs ()
322+ self .reset_groups ()
323+ self .select_default_variables ()
317324 self .openContext (self .dataset )
318325 self .grouping_changed ()
326+ self .attr_changed ()
319327 else :
320328 self .reset_all_data ()
321329 self .commit ()
322330
323- def select_default_variables (self , domain ):
331+ def select_default_variables (self ):
324332 # visualize first non-class variable, group by class (if present)
333+ domain = self .dataset .domain
325334 if len (self .attrs ) > len (domain .class_vars ):
326335 self .attribute = self .attrs [len (domain .class_vars )]
327336 elif self .attrs :
@@ -332,7 +341,7 @@ def select_default_variables(self, domain):
332341 else :
333342 self .group_var = None # Reset to trigger selection via callback
334343
335- def apply_sorting (self ):
344+ def apply_attr_sorting (self ):
336345 def compute_score (attr ):
337346 if attr is group_var :
338347 return 3
@@ -362,8 +371,48 @@ def compute_score(attr):
362371 include_class = True , include_metas = True ) else None
363372 self .attrs .sort (key = compute_score )
364373 else :
365- self .reset_attrs (domain )
366- self .attribute = attribute
374+ self .reset_attrs ()
375+ self .attribute = attribute # reset selection
376+ self ._ensure_selection_visible (self .controls .attribute )
377+
378+ def apply_group_sorting (self ):
379+ def compute_stat (group ):
380+ if group is attr :
381+ return 3
382+ if group is None :
383+ return - 1
384+ if attr .is_continuous :
385+ group_col = data .get_column_view (group )[0 ].astype (int )
386+ groups = (attr_col [group_col == i ]
387+ for i in range (len (group .values )))
388+ groups = (col [~ np .isnan (col )] for col in groups )
389+ groups = [group for group in groups if len (group )]
390+ p = f_oneway (* groups )[1 ] if len (groups ) > 1 else 2
391+ else :
392+ p = self ._chi_square (group , attr )[1 ]
393+ if math .isnan (p ):
394+ return 2
395+ return p
396+
397+ data = self .dataset
398+ if data is None :
399+ return
400+ attr = self .attribute
401+ group_var = self .group_var
402+ if self .order_grouping_by_importance :
403+ if attr .is_continuous :
404+ attr_col = data .get_column_view (attr )[0 ].astype (float )
405+ self .group_vars .sort (key = compute_stat )
406+ else :
407+ self .reset_groups ()
408+ self .group_var = group_var # reset selection
409+ self ._ensure_selection_visible (self .controls .group_var )
410+
411+ @staticmethod
412+ def _ensure_selection_visible (view ):
413+ selection = view .selectedIndexes ()
414+ if len (selection ) == 1 :
415+ view .scrollTo (selection [0 ])
367416
368417 def _chi_square (self , group_var , attr ):
369418 # Chi-square with the given distribution into groups
@@ -380,16 +429,14 @@ def _chi_square(self, group_var, attr):
380429 def reset_all_data (self ):
381430 self .clear_scene ()
382431 self .stat_test = ""
383- self .attrs .clear ()
384- self .group_vars .set_domain (None )
385- self .group_view .setEnabled (False )
432+ self .attrs [:] = []
433+ self .group_vars [:] = [None ]
386434 self .is_continuous = False
387435 self .update_display_box ()
388436
389437 def grouping_changed (self ):
390- self .cb_order .setEnabled (self .group_var is not None )
391- self .apply_sorting ()
392- self .attr_changed ()
438+ self .apply_attr_sorting ()
439+ self .update_graph ()
393440
394441 def select_box_items (self ):
395442 temp_cond = self .conditions .copy ()
@@ -399,6 +446,10 @@ def select_box_items(self):
399446 [c .conditions for c in temp_cond ])
400447
401448 def attr_changed (self ):
449+ self .apply_group_sorting ()
450+ self .update_graph ()
451+
452+ def update_graph (self ):
402453 self .compute_box_data ()
403454 self .update_display_box ()
404455 self .layout_changed ()
0 commit comments