1+ # pylint: disable=too-many-lines
12from collections import defaultdict
23from functools import reduce
34from itertools import product , chain
1011from AnyQt .QtGui import QColor , QPainter , QPen , QStandardItem
1112from AnyQt .QtWidgets import QGraphicsScene , QGraphicsLineItem
1213
13- from Orange .data import Table , filter , Variable
14+ from Orange .data import Table , filter , Variable , Domain
1415from Orange .data .sql .table import SqlTable , LARGE_TABLE , DEFAULT_SAMPLE_TIME
1516from Orange .preprocess import Discretize
1617from Orange .preprocess .discretize import EqualFreq
2324from Orange .widgets .utils import to_html , get_variable_values_sorted
2425from Orange .widgets .utils .annotated_data import (create_annotated_table ,
2526 ANNOTATED_DATA_SIGNAL_NAME )
27+ from Orange .widgets .utils .itemmodels import DomainModel
2628from Orange .widgets .visualize .utils import (
2729 CanvasText , CanvasRectangle , ViewWithPress , VizRankDialog )
2830from Orange .widgets .widget import OWWidget , Default , Msg
@@ -118,7 +120,7 @@ def check_preconditions(self):
118120 def compute_attr_order (self ):
119121 """
120122 Order attributes by Relief if there is a target variable. In case of
121- ties or without target, other by name.
123+ ties or without target, order by name.
122124
123125 Add the class variable at the beginning when not coloring by class
124126 distribution.
@@ -250,7 +252,7 @@ def on_selection_changed(self, selected, deselected):
250252 def row_for_state (self , score , state ):
251253 """The row consists of attributes sorted by name; class is at the
252254 beginning, if present, so it's on the x-axis and not lost somewhere."""
253- class_var = self .master .data .domain .class_var
255+ class_var = self .master .color_data .domain .class_var
254256 attrs = tuple (
255257 sorted ((self .attr_ordering [x ] for x in state ),
256258 key = lambda attr : (1 - (attr is class_var ), attr .name )))
@@ -271,8 +273,6 @@ class OWMosaicDisplay(OWWidget):
271273 (ANNOTATED_DATA_SIGNAL_NAME , Table )]
272274
273275 PEARSON , CLASS_DISTRIBUTION = 0 , 1
274- interior_coloring_opts = ["Pearson residuals" ,
275- "Class distribution" ]
276276
277277 settingsHandler = DomainContextHandler ()
278278 use_boxes = Setting (True )
@@ -281,6 +281,7 @@ class OWMosaicDisplay(OWWidget):
281281 variable2 = ContextSetting ("" , exclude_metas = False )
282282 variable3 = ContextSetting ("" , exclude_metas = False )
283283 variable4 = ContextSetting ("" , exclude_metas = False )
284+ variable_color = ContextSetting ("" , exclude_metas = False )
284285 selection = ContextSetting (set ())
285286
286287 BAR_WIDTH = 5
@@ -310,6 +311,8 @@ def __init__(self):
310311 self .unprocessed_subset_data = None
311312 self .subset_data = None
312313
314+ self .color_data = None
315+
313316 self .areas = []
314317
315318 self .canvas = QGraphicsScene ()
@@ -331,13 +334,18 @@ def __init__(self):
331334 self .vizrank , self .vizrank_button = MosaicVizRank .add_vizrank (
332335 box , self , "Find Informative Mosaics" , self .set_attr )
333336
334- self .rb_colors = gui .radioButtonsInBox (
335- self .controlArea , self , "interior_coloring" ,
336- self .interior_coloring_opts , box = "Interior Coloring" ,
337- callback = self .coloring_changed )
337+ box2 = gui .vBox (self .controlArea , box = "Interior Coloring" )
338+ dmod = DomainModel
339+ self .color_model = DomainModel (order = dmod .MIXED ,
340+ valid_types = dmod .PRIMITIVE ,
341+ placeholder = "(Pearson residuals)" )
342+ self .cb_attr_color = gui .comboBox (
343+ box2 , self , value = "variable_color" ,
344+ orientation = Qt .Horizontal , contentsLength = 12 , labelWidth = 50 ,
345+ callback = self .set_color_data ,
346+ sendSelectedValue = True , model = self .color_model , valueType = str )
338347 self .bar_button = gui .checkBox (
339- gui .indentedBox (self .rb_colors ),
340- self , 'use_boxes' , label = 'Compare with total' ,
348+ box2 , self , 'use_boxes' , label = 'Compare with total' ,
341349 callback = self ._compare_with_total )
342350 gui .rubber (self .controlArea )
343351
@@ -380,7 +388,7 @@ def init_combos(self, data):
380388
381389 icons = gui .attributeIconDict
382390 for attr in chain (data .domain , data .domain .metas ):
383- if attr .is_discrete or attr . is_continuous :
391+ if attr .is_primitive :
384392 for combo in self .attr_combos :
385393 combo .addItem (icons [attr ], attr .name )
386394
@@ -390,6 +398,12 @@ def init_combos(self, data):
390398 2 * (self .attr_combos [1 ].count () > 2 ))
391399 self .variable3 = self .attr_combos [2 ].itemText (0 )
392400 self .variable4 = self .attr_combos [3 ].itemText (0 )
401+ if self .data .domain .class_var :
402+ self .variable_color = self .data .domain .class_var .name
403+ idx = self .cb_attr_color .findText (self .variable_color )
404+ else :
405+ idx = 0
406+ self .cb_attr_color .setCurrentIndex (idx )
393407
394408 def get_attr_list (self ):
395409 return [
@@ -416,8 +430,6 @@ def set_data(self, data):
416430
417431 self .closeContext ()
418432 self .data = data
419- self .init_combos (self .data )
420- self .discrete_data = self ._get_discrete_data (self .data )
421433
422434 self .vizrank .stop_and_reset ()
423435 self .vizrank_button .setEnabled (
@@ -427,10 +439,8 @@ def set_data(self, data):
427439 if self .data is None :
428440 return
429441
430- has_class = self .data .domain .class_var is not None
431- self .rb_colors .setDisabled (not has_class )
432- self .interior_coloring = \
433- self .CLASS_DISTRIBUTION if has_class else self .PEARSON
442+ self .color_model .set_domain (self .data .domain )
443+ self .init_combos (self .data )
434444
435445 self .openContext (self .data )
436446
@@ -439,6 +449,8 @@ def set_data(self, data):
439449 self .set_subset_data (self .unprocessed_subset_data )
440450 self .unprocessed_subset_data = None
441451
452+ self .set_color_data ()
453+
442454 def set_subset_data (self , data ):
443455 self .Warning .incompatible_subset .clear ()
444456 if self .data is None :
@@ -468,6 +480,26 @@ def reset_graph(self):
468480 self .clear_selection ()
469481 self .update_graph ()
470482
483+ def set_color_data (self ):
484+ if self .data is None or len (self .data ) < 2 or len (self .data .domain .attributes ) < 1 :
485+ return
486+ if self .cb_attr_color .currentIndex () <= 0 :
487+ color_var = None
488+ self .interior_coloring = self .PEARSON
489+ self .bar_button .setEnabled (False )
490+ else :
491+ color_var = self .data .domain [self .cb_attr_color .currentText ()]
492+ self .interior_coloring = self .CLASS_DISTRIBUTION
493+ self .bar_button .setEnabled (True )
494+ attributes = [v for v in self .data .domain if v != color_var ]
495+ metas = [v for v in self .data .domain .metas if v != color_var ]
496+ domain = Domain (attributes , color_var , metas )
497+ self .color_data = color_data = self .data .from_table (domain , self .data )
498+ self .discrete_data = self ._get_discrete_data (color_data )
499+ self .vizrank .stop_and_reset ()
500+ self .vizrank_button .setEnabled (True )
501+ self .coloring_changed ()
502+
471503 def update_selection_rects (self ):
472504 for i , (_ , _ , area ) in enumerate (self .areas ):
473505 if i in self .selection :
0 commit comments