1212 QGraphicsItem , QGraphicsRectItem , QGraphicsItemGroup , QSizePolicy , \
1313 QGraphicsPathItem
1414
15- from Orange .widgets .visualize .utils import CanvasRectangle , CanvasText
16- from Orange .widgets .utils .annotated_data import ANNOTATED_DATA_SIGNAL_NAME , \
17- create_annotated_table
15+ from Orange .data import Table , Domain
16+ from Orange .projection .som import SOM
1817
19- from Orange .data import Table , Domain , DiscreteVariable
2018from Orange .widgets import gui
21- from Orange .widgets .visualize .utils .plotutils import wrap_legend_items
2219from Orange .widgets .widget import OWWidget , Msg , Input , Output
2320from Orange .widgets .settings import \
2421 DomainContextHandler , ContextSetting , Setting
2522from Orange .widgets .utils .itemmodels import DomainModel
2623from Orange .widgets .utils .widgetpreview import WidgetPreview
27- from Orange .projection .som import SOM
24+ from Orange .widgets .utils .annotated_data import \
25+ create_annotated_table , ANNOTATED_DATA_SIGNAL_NAME
26+ from Orange .widgets .utils .colorpalette import ContinuousPaletteGenerator
27+ from Orange .widgets .visualize .utils import CanvasRectangle , CanvasText
28+ from Orange .widgets .visualize .utils .plotutils import wrap_legend_items
2829
2930
3031sqrt3_2 = np .sqrt (3 ) / 2
3132
33+
3234class SomView (QGraphicsView ):
3335 SelectionClear , SelectionAdd , SelectionRemove , SelectionToggle = 1 , 2 , 4 , 8
3436 SelectionSet = SelectionClear | SelectionAdd
@@ -207,6 +209,7 @@ def __init__(self):
207209 self .sizes = None
208210 self .cells = self .member_data = None
209211 self .selection = set ()
212+ self .colors = self .bins = None
210213
211214 box = gui .vBox (self .controlArea , box = True )
212215 hbox = gui .hBox (box )
@@ -241,7 +244,7 @@ def __init__(self):
241244 box , self , "attr_color" , maximumContentsLength = 15 ,
242245 callback = self .on_attr_color_change ,
243246 model = DomainModel (placeholder = "(Same color)" ,
244- valid_types = DiscreteVariable ))
247+ valid_types = DomainModel . PRIMITIVE ))
245248 gui .checkBox (
246249 box , self , "pie_charts" , label = "Show pie charts" ,
247250 callback = self .on_pie_chart_change )
@@ -314,12 +317,9 @@ def set_data(self, data):
314317
315318 if self .data is not None :
316319 self .controls .attr_color .model ().set_domain (data .domain )
317- class_var = data .domain .class_var
318- if class_var is not None and class_var .is_discrete :
319- self .attr_color = class_var
320- else :
321- self .attr_color = None
320+ self .attr_color = data .domain .class_var
322321 self .openContext (data )
322+ self .set_color_bins ()
323323 self .create_legend ()
324324 self .recompute_dimensions ()
325325 self .replot ()
@@ -341,6 +341,7 @@ def clear(self):
341341 self .data = self .cont_x = None
342342 self .sizes = None
343343 self .cells = self .member_data = None
344+ self .colors = self .bins = None
344345 self .assignments = None
345346 if self .elements is not None :
346347 self .scene .removeItem (self .elements )
@@ -381,6 +382,7 @@ def on_geometry_change(self):
381382
382383 def on_attr_color_change (self ):
383384 self .controls .pie_charts .setEnabled (self .attr_color is not None )
385+ self .set_color_bins ()
384386 self .create_legend ()
385387 self .rescale ()
386388 self ._redraw ()
@@ -474,14 +476,10 @@ def _redraw(self):
474476 self .scene .addItem (self .elements )
475477 if self .attr_color is None :
476478 self ._redraw_same_color ()
479+ elif self .pie_charts :
480+ self ._redraw_pie_charts ()
477481 else :
478- color_column = \
479- self .data .get_column_view (self .attr_color )[0 ].astype (float )
480- colors = [QColor (* color ) for color in self .attr_color .colors ]
481- if self .pie_charts :
482- self ._redraw_pie_charts (color_column , colors )
483- else :
484- self ._redraw_colored_circles (color_column , colors )
482+ self ._redraw_colored_circles ()
485483
486484 @property
487485 def _grid_factors (self ):
@@ -503,40 +501,53 @@ def _redraw_same_color(self):
503501 ellipse .setBrush (brush )
504502 self .elements .addToGroup (ellipse )
505503
506- def _redraw_pie_charts (self , color_column , colors ):
504+ def _get_color_column (self ):
505+ color_column = \
506+ self .data .get_column_view (self .attr_color )[0 ].astype (float ,
507+ copy = False )
508+ if self .attr_color .is_discrete :
509+ with np .errstate (invalid = "ignore" ):
510+ int_col = color_column .astype (int )
511+ int_col [np .isnan (color_column )] = len (self .colors )
512+ else :
513+ int_col = np .zeros (len (color_column ), dtype = int )
514+ int_col [np .isnan (color_column )] = len (self .colors )
515+ for i , thresh in enumerate (self .bins , start = 1 ):
516+ int_col [color_column >= thresh ] = i
517+ return int_col
518+
519+ def _redraw_pie_charts (self ):
507520 fx , fy = self ._grid_factors
508- color_column = color_column .copy ()
509- color_column [np .isnan (color_column )] = len (colors )
510- color_column = color_column .astype (int )
511- colors .append (Qt .gray )
521+ color_column = self ._get_color_column ()
522+ colors = self .colors + [Qt .gray ]
512523 for y in range (self .size_y ):
513524 for x in range (self .size_x - self .hexagonal * (y % 2 )):
514525 r = self .sizes [x , y ]
515526 if not r :
516527 continue
517528 members = self .get_member_indices (x , y )
518- color_dist = np .bincount (
519- color_column [ members ], minlength = len (self . attr_color . values ))
529+ color_dist = np .bincount (color_column [ members ],
530+ minlength = len (colors ))
520531 color_dist = color_dist .astype (float ) / len (members )
521532 pie = PieChart (color_dist , r / 2 , colors )
522533 self .elements .addToGroup (pie )
523534 pie .setPos (x + (y % 2 ) * fx , y * fy )
524535
525- def _redraw_colored_circles (self , color_column , colors ):
536+ def _redraw_colored_circles (self ):
526537 fx , fy = self ._grid_factors
538+ color_column = self ._get_color_column ()
527539 for y in range (self .size_y ):
528540 for x in range (self .size_x - self .hexagonal * (y % 2 )):
529541 r = self .sizes [x , y ]
530542 if not r :
531543 continue
532544 members = self .get_member_indices (x , y )
533545 color_dist = color_column [members ]
534- color_dist = color_dist [np . isfinite ( color_dist )]
546+ color_dist = color_dist [color_dist < len ( self . colors )]
535547 if len (color_dist ) != len (members ):
536548 self .Warning .missing_colors (self .attr_color .name )
537- color_dist = color_dist .astype (int )
538- bc = np .bincount (color_dist , minlength = len (self .attr_color .values ))
539- color = colors [np .argmax (bc )]
549+ bc = np .bincount (color_dist , minlength = len (self .colors ))
550+ color = self .colors [np .argmax (bc )]
540551 pen = QPen (QBrush (color ), 4 )
541552 brush = QBrush (color .lighter (200 - 100 * np .max (bc ) / len (members )))
542553 pen .setCosmetic (True )
@@ -703,18 +714,40 @@ def update_output(self):
703714 self .Outputs .annotated_data .send (None )
704715 self .info .set_output_summary (self .info .NoOutput )
705716
717+ def set_color_bins (self ):
718+ if self .attr_color is None :
719+ self .bins = self .colors = None
720+ elif self .attr_color .is_discrete :
721+ self .bins = None
722+ self .colors = [QColor (* color ) for color in self .attr_color .colors ]
723+ else :
724+ col = self .data .get_column_view (self .attr_color )[0 ].astype (float )
725+ # TODO: Use intelligent binning from #3896, when it's merged
726+ self .bins = np .linspace (np .min (col ), np .max (col ), 6 )[1 :- 1 ]
727+ palette = ContinuousPaletteGenerator (* self .attr_color .colors )
728+ nbins = len (self .bins ) + 1
729+ self .colors = [palette [i / (nbins - 1 )] for i in range (nbins )]
730+
706731 def create_legend (self ):
707732 if self .legend is not None :
708733 self .scene .removeItem (self .legend )
709734 self .legend = None
710735 if self .attr_color is None :
711736 return None
712737
713- names = self .attr_color .values
714- colors = [QColor (* color ) for color in self .attr_color .colors ]
738+ if self .attr_color .is_discrete :
739+ names = self .attr_color .values
740+ else :
741+ sval = self .attr_color .repr_val
742+ names = \
743+ [f"< { sval (self .bins [0 ])} " ] \
744+ + [f"{ sval (x )} - { sval (y )} "
745+ for x , y in zip (self .bins , self .bins [1 :])] \
746+ + [f"> { sval (self .bins [- 1 ])} " ]
747+
715748 items = []
716749 size = 8
717- for name , color in zip (names , colors ):
750+ for name , color in zip (names , self . colors ):
718751 item = QGraphicsItemGroup ()
719752 item .addToGroup (
720753 CanvasRectangle (None , - size / 2 , - size / 2 , size , size ,
0 commit comments